Skip to content

Commit 172e369

Browse files
authored
added maximum size for slices (#10)
1 parent 2166488 commit 172e369

File tree

6 files changed

+406
-3
lines changed

6 files changed

+406
-3
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,6 @@ Available for: *int*, *int8*, *int16*, *int32*, *int64*, *uint*, *uint8*, *uint1
116116

117117
### slices
118118

119-
Tags will be applied for every element in the slice, not the slice itself. For example: a field of type `[]string` with the tag `max=5` will have every string truncated to 5 characters at most.
119+
1. **maxsize=`<n>`** - Maximum slice length. It will truncate the slice to `<n>` elements if the limit is exceeded
120+
121+
Other tags will be applied for every element in the slice, not the slice itself. For example: a field of type `[]string` with the tag `max=5` will have every string truncated to 5 characters at most.

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/go-sanitize/sanitize
2+
3+
go 1.12

sanitize.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ func (s Sanitizer) sanitizeRec(v reflect.Value) error {
161161
field := v.Field(i)
162162
fkind := field.Kind()
163163

164+
// If the field is a slice, sanitize it first
165+
isPtrToSlice := fkind == reflect.Ptr && field.Elem().Kind() == reflect.Slice
166+
isSlice := fkind == reflect.Slice
167+
if isSlice || isPtrToSlice {
168+
if err := sanitizeSliceField(s, v, i); err != nil {
169+
return err
170+
}
171+
}
172+
164173
// Do we have a special sanitization function for this type? If so, use it
165174
ftype := field.Type().String()
166175
if sanFn, ok := fieldSanFns[ftype]; ok {
@@ -182,8 +191,7 @@ func (s Sanitizer) sanitizeRec(v reflect.Value) error {
182191
}
183192

184193
// If the field is a slice of structs, recurse through them
185-
isPtrToSlice := fkind == reflect.Ptr && field.Elem().Kind() == reflect.Slice
186-
if fkind == reflect.Slice || isPtrToSlice {
194+
if isSlice || isPtrToSlice {
187195
if isPtrToSlice {
188196
field = field.Elem()
189197
}

sanitize_test.go

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,3 +621,214 @@ func Test_Sanitize(t *testing.T) {
621621
})
622622
}
623623
}
624+
625+
func Test_SliceSanitize(t *testing.T) {
626+
627+
type TestStruct struct {
628+
SlcFloat32Field []float32 `san:"maxsize=2"`
629+
SlcStrField []string `san:"maxsize=2"`
630+
SlcIntField []int `san:"maxsize=2"`
631+
SlcBoolField []bool `san:"maxsize=2"`
632+
SlcFloat32PtrField []*float32 `san:"maxsize=2"`
633+
SlcStrPtrField []*string `san:"maxsize=2"`
634+
SlcIntPtrField []*int `san:"maxsize=2"`
635+
SlcBoolPtrField []*bool `san:"maxsize=2"`
636+
SlcPtrFloat32Field *[]float32 `san:"maxsize=2"`
637+
SlcPtrStrField *[]string `san:"maxsize=2"`
638+
SlcPtrIntField *[]int `san:"maxsize=2"`
639+
SlcPtrBoolField *[]bool `san:"maxsize=2"`
640+
SlcPtrFloat32PtrField *[]*float32 `san:"maxsize=2"`
641+
SlcPtrStrPtrField *[]*string `san:"maxsize=2"`
642+
SlcPtrIntPtrField *[]*int `san:"maxsize=2"`
643+
SlcPtrBoolPtrField *[]*bool `san:"maxsize=2"`
644+
}
645+
646+
s, _ := New()
647+
648+
float1 := float32(1.1)
649+
float2 := float32(2.2)
650+
float3 := float32(3.3)
651+
str1 := "test1"
652+
str2 := "test2"
653+
str3 := "test3"
654+
int1 := 1
655+
int2 := 2
656+
int3 := 3
657+
bool1 := true
658+
bool2 := false
659+
660+
type args struct {
661+
s interface{}
662+
}
663+
tests := []struct {
664+
name string
665+
args args
666+
wantErr bool
667+
want interface{}
668+
}{
669+
{
670+
name: "Sanitizes a struct that contains many types of slices.",
671+
args: args{
672+
s: &TestStruct{
673+
SlcFloat32Field: []float32{
674+
float1,
675+
float2,
676+
float3,
677+
},
678+
SlcStrField: []string{
679+
str1,
680+
str2,
681+
str3,
682+
},
683+
SlcIntField: []int{
684+
int1,
685+
int2,
686+
int3,
687+
},
688+
SlcBoolField: []bool{
689+
bool1,
690+
bool2,
691+
bool1,
692+
},
693+
SlcFloat32PtrField: []*float32{
694+
&float1,
695+
&float2,
696+
&float3,
697+
},
698+
SlcStrPtrField: []*string{
699+
&str1,
700+
&str2,
701+
&str3,
702+
},
703+
SlcIntPtrField: []*int{
704+
&int1,
705+
&int2,
706+
&int3,
707+
},
708+
SlcBoolPtrField: []*bool{
709+
&bool1,
710+
&bool2,
711+
&bool1,
712+
},
713+
SlcPtrFloat32Field: &[]float32{
714+
float1,
715+
float2,
716+
float3,
717+
},
718+
SlcPtrStrField: &[]string{
719+
str1,
720+
str2,
721+
str3,
722+
},
723+
SlcPtrIntField: &[]int{
724+
int1,
725+
int2,
726+
int3,
727+
},
728+
SlcPtrBoolField: &[]bool{
729+
bool1,
730+
bool2,
731+
bool1,
732+
},
733+
SlcPtrFloat32PtrField: &[]*float32{
734+
&float1,
735+
&float2,
736+
&float3,
737+
},
738+
SlcPtrStrPtrField: &[]*string{
739+
&str1,
740+
&str2,
741+
&str3,
742+
},
743+
SlcPtrIntPtrField: &[]*int{
744+
&int1,
745+
&int2,
746+
&int3,
747+
},
748+
SlcPtrBoolPtrField: &[]*bool{
749+
&bool1,
750+
&bool2,
751+
&bool1,
752+
},
753+
},
754+
},
755+
want: &TestStruct{
756+
SlcFloat32Field: []float32{
757+
float1,
758+
float2,
759+
},
760+
SlcStrField: []string{
761+
str1,
762+
str2,
763+
},
764+
SlcIntField: []int{
765+
int1,
766+
int2,
767+
},
768+
SlcBoolField: []bool{
769+
bool1,
770+
bool2,
771+
},
772+
SlcFloat32PtrField: []*float32{
773+
&float1,
774+
&float2,
775+
},
776+
SlcStrPtrField: []*string{
777+
&str1,
778+
&str2,
779+
},
780+
SlcIntPtrField: []*int{
781+
&int1,
782+
&int2,
783+
},
784+
SlcBoolPtrField: []*bool{
785+
&bool1,
786+
&bool2,
787+
},
788+
SlcPtrFloat32Field: &[]float32{
789+
float1,
790+
float2,
791+
},
792+
SlcPtrStrField: &[]string{
793+
str1,
794+
str2,
795+
},
796+
SlcPtrIntField: &[]int{
797+
int1,
798+
int2,
799+
},
800+
SlcPtrBoolField: &[]bool{
801+
bool1,
802+
bool2,
803+
},
804+
SlcPtrFloat32PtrField: &[]*float32{
805+
&float1,
806+
&float2,
807+
},
808+
SlcPtrStrPtrField: &[]*string{
809+
&str1,
810+
&str2,
811+
},
812+
SlcPtrIntPtrField: &[]*int{
813+
&int1,
814+
&int2,
815+
},
816+
SlcPtrBoolPtrField: &[]*bool{
817+
&bool1,
818+
&bool2,
819+
},
820+
},
821+
},
822+
}
823+
for _, tt := range tests {
824+
t.Run(tt.name, func(t *testing.T) {
825+
if err := s.Sanitize(tt.args.s); (err != nil) != tt.wantErr {
826+
t.Errorf("Sanitize() error = %v, wantErr %v", err, tt.wantErr)
827+
}
828+
if !reflect.DeepEqual(tt.args.s, tt.want) {
829+
t.Errorf("Sanitize() - got %+v but wanted %+v", tt.args.s, tt.want)
830+
}
831+
})
832+
}
833+
}
834+

slice.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package sanitize
2+
3+
import (
4+
"reflect"
5+
"strconv"
6+
)
7+
8+
// sanitizeSliceField sanitizes a slice field. Requires the whole
9+
// reflect.Value for the slice because it needs access to both the Value and
10+
// Type of the slice.
11+
func sanitizeSliceField(s Sanitizer, structValue reflect.Value, idx int) error {
12+
fieldValue := structValue.Field(idx)
13+
14+
tags := s.fieldTags(structValue.Type().Field(idx).Tag)
15+
16+
if fieldValue.Kind() == reflect.Ptr && !fieldValue.IsNil() {
17+
fieldValue = fieldValue.Elem()
18+
}
19+
20+
if _, ok := tags["maxsize"]; ok {
21+
max, err := strconv.ParseInt(tags["maxsize"], 10, 32)
22+
if err != nil {
23+
return err
24+
}
25+
if fieldValue.Len() < int(max) {
26+
return nil
27+
}
28+
fieldValue.Set(fieldValue.Slice(0, int(max)))
29+
}
30+
31+
return nil
32+
}

0 commit comments

Comments
 (0)