diff --git a/.gitignore b/.gitignore index 6e43fac..d7edbc3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,5 +26,6 @@ _testmain.go *.test *.out *.txt +/**/*.DS_Store cover.html README.html diff --git a/README.md b/README.md index 8b730b6..6bd2251 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ Baked-in Validations | - | - | | dir | Directory | | file | File path | +| image | Image | | isdefault | Is Default | | len | Length | | max | Maximum | diff --git a/baked_in.go b/baked_in.go index f2f0939..c21190b 100644 --- a/baked_in.go +++ b/baked_in.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "io" "net" "net/url" "os" @@ -20,6 +21,7 @@ import ( "golang.org/x/crypto/sha3" "golang.org/x/text/language" + "github.com/gabriel-vasile/mimetype" urn "github.com/leodido/go-urn" ) @@ -136,6 +138,7 @@ var ( "endswith": endsWith, "startsnotwith": startsNotWith, "endsnotwith": endsNotWith, + "image": isImage, "isbn": isISBN, "isbn10": isISBN10, "isbn13": isISBN13, @@ -1404,6 +1407,74 @@ func isFile(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +func isImage(fl FieldLevel) bool { + mimetypes := map[string]bool{ + "image/bmp": true, + "image/cis-cod": true, + "image/gif": true, + "image/ief": true, + "image/jpeg": true, + "image/jp2": true, + "image/jpx": true, + "image/jpm": true, + "image/pipeg": true, + "image/png": true, + "image/svg+xml": true, + "image/tiff": true, + "image/webp": true, + "image/x-cmu-raster": true, + "image/x-cmx": true, + "image/x-icon": true, + "image/x-portable-anymap": true, + "image/x-portable-bitmap": true, + "image/x-portable-graymap": true, + "image/x-portable-pixmap": true, + "image/x-rgb": true, + "image/x-xbitmap": true, + "image/x-xpixmap": true, + "image/x-xwindowdump": true, + } + field := fl.Field() + + switch field.Kind() { + case reflect.String: + filePath := field.String() + fileInfo, err := os.Stat(filePath) + + if err != nil { + return false + } + + if fileInfo.IsDir() { + return false + } + + file, err := os.OpenFile(filePath, os.O_RDWR, 0644) + if err != nil { + return false + } + defer file.Close() + + mime, err := mimetype.DetectReader(file) + if err != nil { + return false + } + + _, err = file.Seek(0, io.SeekStart) + if err != nil { + panic(fmt.Sprintf("Could not reset seek start for file. Error: %s", err)) + } + + if _, ok := mimetypes[mime.String()]; ok { + return true + } + + return false + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + // isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number. func isE164(fl FieldLevel) bool { return e164Regex.MatchString(fl.Field().String()) diff --git a/doc.go b/doc.go index 7341c67..2118190 100644 --- a/doc.go +++ b/doc.go @@ -873,6 +873,14 @@ This is done using os.Stat, which is a platform independent function. Usage: file +Image path + +This validates that a string value contains a valid file path and that +the file exists on the machine and is an image. +This is done using os.Stat and github.com/gabriel-vasile/mimetype + + Usage: image + URL String This validates that a string value contains a valid url diff --git a/go.mod b/go.mod index e281ec0..7d27280 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.1 github.com/go-playground/assert/v2 v2.0.1 github.com/go-playground/locales v0.14.0 github.com/go-playground/universal-translator v0.18.0 @@ -12,7 +13,6 @@ require ( github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/stretchr/testify v1.7.0 // indirect golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 - golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect golang.org/x/text v0.3.7 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/go.sum b/go.sum index a1c43c3..a8d0557 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q= +github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -31,13 +33,15 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/testdata/music.mp3 b/testdata/music.mp3 new file mode 100644 index 0000000..c810c80 Binary files /dev/null and b/testdata/music.mp3 differ diff --git a/translations/ar/ar.go b/translations/ar/ar.go index 3886120..038635a 100644 --- a/translations/ar/ar.go +++ b/translations/ar/ar.go @@ -1346,6 +1346,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "يجب أن تكون {0} صورة صالحة", + override: false, + }, } for _, t := range translations { diff --git a/translations/ar/ar_test.go b/translations/ar/ar_test.go index 93adfe9..6e2a418 100644 --- a/translations/ar/ar_test.go +++ b/translations/ar/ar_test.go @@ -149,6 +149,7 @@ func TestTranslations(t *testing.T) { PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + Image string `validate:"image"` } var test Test @@ -676,6 +677,10 @@ func TestTranslations(t *testing.T) { ns: "Test.PostCodeByField", expected: "لا يتطابق PostCodeByField مع تنسيق الرمز البريدي للبلد في حقل PostCodeCountry", }, + { + ns: "Test.Image", + expected: "يجب أن تكون Image صورة صالحة", + }, } for _, tt := range tests { diff --git a/translations/en/en.go b/translations/en/en.go index ee05f91..2817702 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -1356,6 +1356,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} must be a valid boolean value", override: false, }, + { + tag: "image", + translation: "{0} must be a valid image", + override: false, + }, } for _, t := range translations { diff --git a/translations/en/en_test.go b/translations/en/en_test.go index 9cb6deb..5cc29c8 100644 --- a/translations/en/en_test.go +++ b/translations/en/en_test.go @@ -152,6 +152,7 @@ func TestTranslations(t *testing.T) { PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` BooleanString string `validate:"boolean"` + Image string `validate:"image"` } var test Test @@ -690,6 +691,10 @@ func TestTranslations(t *testing.T) { ns: "Test.BooleanString", expected: "BooleanString must be a valid boolean value", }, + { + ns: "Test.Image", + expected: "Image must be a valid image", + }, } for _, tt := range tests { diff --git a/translations/es/es.go b/translations/es/es.go index 2635408..8907868 100644 --- a/translations/es/es.go +++ b/translations/es/es.go @@ -1326,6 +1326,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} debe ser una imagen válida", + override: false, + }, } for _, t := range translations { diff --git a/translations/es/es_test.go b/translations/es/es_test.go index 814f2ab..72b2966 100644 --- a/translations/es/es_test.go +++ b/translations/es/es_test.go @@ -142,6 +142,7 @@ func TestTranslations(t *testing.T) { UniqueSlice []string `validate:"unique"` UniqueArray [3]string `validate:"unique"` UniqueMap map[string]string `validate:"unique"` + Image string `validate:"image"` } var test Test @@ -637,6 +638,10 @@ func TestTranslations(t *testing.T) { ns: "Test.UniqueMap", expected: "UniqueMap debe contener valores únicos", }, + { + ns: "Test.Image", + expected: "Image debe ser una imagen válida", + }, } for _, tt := range tests { diff --git a/translations/fa/fa.go b/translations/fa/fa.go index b7c100b..32fc69e 100644 --- a/translations/fa/fa.go +++ b/translations/fa/fa.go @@ -1341,6 +1341,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} باید یک تصویر معتبر باشد", + override: false, + }, } for _, t := range translations { diff --git a/translations/fa/fa_test.go b/translations/fa/fa_test.go index 1acb8cf..456ebe4 100644 --- a/translations/fa/fa_test.go +++ b/translations/fa/fa_test.go @@ -148,6 +148,7 @@ func TestTranslations(t *testing.T) { PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + Image string `validate:"image"` } var test Test @@ -671,8 +672,12 @@ func TestTranslations(t *testing.T) { ns: "Test.PostCodeByField", expected: "PostCodeByField یک کدپستی معتبر کشور فیلد PostCodeCountry نیست", }, + { + ns: "Test.Image", + expected: "Image باید یک تصویر معتبر باشد", + }, } - + for _, tt := range tests { var fe validator.FieldError diff --git a/translations/fr/fr.go b/translations/fr/fr.go index c455195..eb40e81 100644 --- a/translations/fr/fr.go +++ b/translations/fr/fr.go @@ -1316,6 +1316,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} doit être une image valide", + override: false, + }, } for _, t := range translations { diff --git a/translations/fr/fr_test.go b/translations/fr/fr_test.go index f1ba280..73a1849 100644 --- a/translations/fr/fr_test.go +++ b/translations/fr/fr_test.go @@ -139,6 +139,7 @@ func TestTranslations(t *testing.T) { StrPtrGte *string `validate:"gte=10"` OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` + Image string `validate:"image"` } var test Test @@ -619,6 +620,10 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfInt doit être l'un des choix suivants [5 63]", }, + { + ns: "Test.Image", + expected: "Image doit être une image valide", + }, } for _, tt := range tests { diff --git a/translations/id/id.go b/translations/id/id.go index 08b6ad5..f881a53 100644 --- a/translations/id/id.go +++ b/translations/id/id.go @@ -1316,6 +1316,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} harus berupa gambar yang valid", + override: false, + }, } for _, t := range translations { diff --git a/translations/id/id_test.go b/translations/id/id_test.go index 5d80940..6806e10 100644 --- a/translations/id/id_test.go +++ b/translations/id/id_test.go @@ -139,6 +139,7 @@ func TestTranslations(t *testing.T) { StrPtrGte *string `validate:"gte=10"` OneOfString string `validate:"oneof=merah hijau"` OneOfInt int `validate:"oneof=5 63"` + Image string `validate:"image"` } var test Test @@ -619,6 +620,10 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfInt harus berupa salah satu dari [5 63]", }, + { + ns: "Test.Image", + expected: "Image harus berupa gambar yang valid", + }, } for _, tt := range tests { diff --git a/translations/it/it.go b/translations/it/it.go index 0b46fc4..f1cfcef 100644 --- a/translations/it/it.go +++ b/translations/it/it.go @@ -1205,6 +1205,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er override: false, customTransFunc: customTransFuncV1, }, + { + tag: "image", + translation: "{0} deve essere un'immagine valida", + override: false, + }, } for _, t := range translations { diff --git a/translations/it/it_test.go b/translations/it/it_test.go index 98d2ab5..12fe78c 100644 --- a/translations/it/it_test.go +++ b/translations/it/it_test.go @@ -154,6 +154,7 @@ func TestTranslations(t *testing.T) { PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + Image string `validate:"image"` } var test Test @@ -706,6 +707,10 @@ func TestTranslations(t *testing.T) { ns: "Test.PostCodeByField", expected: "PostCodeByField non corrisponde al formato del codice postale dello stato nel campo PostCodeCountry", }, + { + ns: "Test.Image", + expected: "Image deve essere un'immagine valida", + }, } for _, tt := range tests { diff --git a/translations/ja/ja.go b/translations/ja/ja.go index 1cc67a4..7ed5e0c 100644 --- a/translations/ja/ja.go +++ b/translations/ja/ja.go @@ -1372,6 +1372,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} は有効な画像でなければなりません", + override: false, + }, } for _, t := range translations { diff --git a/translations/ja/ja_test.go b/translations/ja/ja_test.go index 7950251..79b0971 100644 --- a/translations/ja/ja_test.go +++ b/translations/ja/ja_test.go @@ -139,6 +139,7 @@ func TestTranslations(t *testing.T) { StrPtrGte *string `validate:"gte=10"` OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` + Image string `validate:"image"` } var test Test @@ -619,6 +620,10 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfIntは[5 63]のうちのいずれかでなければなりません", }, + { + ns: "Test.Image", + expected: "Image は有効な画像でなければなりません", + }, } for _, tt := range tests { diff --git a/translations/nl/nl.go b/translations/nl/nl.go index ca7c554..a49f185 100644 --- a/translations/nl/nl.go +++ b/translations/nl/nl.go @@ -1316,6 +1316,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} moet een geldige afbeelding zijn", + override: false, + }, } for _, t := range translations { diff --git a/translations/nl/nl_test.go b/translations/nl/nl_test.go index 137e0c4..9cfeb5b 100644 --- a/translations/nl/nl_test.go +++ b/translations/nl/nl_test.go @@ -139,6 +139,7 @@ func TestTranslations(t *testing.T) { StrPtrGte *string `validate:"gte=10"` OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` + Image string `validate:"image"` } var test Test @@ -619,6 +620,10 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfInt moet een van de volgende zijn [5 63]", }, + { + ns: "Test.Image", + expected: "Image moet een geldige afbeelding zijn", + }, } for _, tt := range tests { diff --git a/translations/pt/pt.go b/translations/pt/pt.go index 4748331..31fb1a2 100644 --- a/translations/pt/pt.go +++ b/translations/pt/pt.go @@ -1356,6 +1356,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} deve ser uma imagem válida", + override: false, + }, } for _, t := range translations { diff --git a/translations/pt/pt_test.go b/translations/pt/pt_test.go index 0422ab0..96c8318 100644 --- a/translations/pt/pt_test.go +++ b/translations/pt/pt_test.go @@ -147,6 +147,7 @@ func TestTranslations(t *testing.T) { LowercaseString string `validate:"lowercase"` UppercaseString string `validate:"uppercase"` Datetime string `validate:"datetime=2006-01-02"` + Image string `validate:"image"` } var test Test @@ -662,6 +663,10 @@ func TestTranslations(t *testing.T) { ns: "Test.Datetime", expected: "Datetime não está no formato 2006-01-02", }, + { + ns: "Test.Image", + expected: "Image deve ser uma imagem válida", + }, } for _, tt := range tests { diff --git a/translations/pt_BR/pt_BR.go b/translations/pt_BR/pt_BR.go index d6883aa..ad501da 100644 --- a/translations/pt_BR/pt_BR.go +++ b/translations/pt_BR/pt_BR.go @@ -1321,6 +1321,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} deve ser um valor booleano válido", override: false, }, + { + tag: "image", + translation: "{0} deve ser uma imagen válido", + override: false, + }, } for _, t := range translations { diff --git a/translations/pt_BR/pt_BR_test.go b/translations/pt_BR/pt_BR_test.go index 426f246..68a8752 100644 --- a/translations/pt_BR/pt_BR_test.go +++ b/translations/pt_BR/pt_BR_test.go @@ -140,6 +140,7 @@ func TestTranslations(t *testing.T) { OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` BooleanString string `validate:"boolean"` + Image string `validate:"image"` } var test Test @@ -625,6 +626,10 @@ func TestTranslations(t *testing.T) { ns: "Test.BooleanString", expected: "BooleanString deve ser um valor booleano válido", }, + { + ns: "Test.Image", + expected: "Image deve ser uma imagen válido", + }, } for _, tt := range tests { diff --git a/translations/ru/ru.go b/translations/ru/ru.go index 25400c5..5cafb08 100644 --- a/translations/ru/ru.go +++ b/translations/ru/ru.go @@ -1439,6 +1439,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} должно быть допустимым изображением", + override: false, + }, } for _, t := range translations { diff --git a/translations/ru/ru_test.go b/translations/ru/ru_test.go index c74f0d5..223966f 100644 --- a/translations/ru/ru_test.go +++ b/translations/ru/ru_test.go @@ -164,6 +164,7 @@ func TestTranslations(t *testing.T) { UniqueSlice []string `validate:"unique"` UniqueArray [3]string `validate:"unique"` UniqueMap map[string]string `validate:"unique"` + Image string `validate:"image"` } var test Test @@ -745,6 +746,10 @@ func TestTranslations(t *testing.T) { ns: "Test.UniqueMap", expected: "UniqueMap должен содержать уникальные значения", }, + { + ns: "Test.Image", + expected: "Image должно быть допустимым изображением", + }, } for _, tt := range tests { diff --git a/translations/tr/tr.go b/translations/tr/tr.go index 2e88a20..7c86258 100644 --- a/translations/tr/tr.go +++ b/translations/tr/tr.go @@ -1321,6 +1321,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} geçerli bir resim olmalıdır", + override: false, + }, } for _, t := range translations { diff --git a/translations/tr/tr_test.go b/translations/tr/tr_test.go index b72329e..e18ac6a 100644 --- a/translations/tr/tr_test.go +++ b/translations/tr/tr_test.go @@ -142,6 +142,7 @@ func TestTranslations(t *testing.T) { UniqueSlice []string `validate:"unique"` UniqueArray [3]string `validate:"unique"` UniqueMap map[string]string `validate:"unique"` + Image string `validate:"image"` } var test Test @@ -637,6 +638,10 @@ func TestTranslations(t *testing.T) { ns: "Test.UniqueMap", expected: "UniqueMap benzersiz değerler içermelidir", }, + { + ns: "Test.Image", + expected: "Image geçerli bir resim olmalıdır", + }, } for _, tt := range tests { diff --git a/translations/vi/vi.go b/translations/vi/vi.go index 009ba4c..170938d 100644 --- a/translations/vi/vi.go +++ b/translations/vi/vi.go @@ -1341,6 +1341,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} phải là một hình ảnh hợp lệ", + override: false, + }, } for _, t := range translations { diff --git a/translations/vi/vi_test.go b/translations/vi/vi_test.go index 6e12866..d26c42d 100644 --- a/translations/vi/vi_test.go +++ b/translations/vi/vi_test.go @@ -148,6 +148,7 @@ func TestTranslations(t *testing.T) { PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + Image string `validate:"image"` } var test Test @@ -671,6 +672,10 @@ func TestTranslations(t *testing.T) { ns: "Test.PostCodeByField", expected: "PostCodeByField sai định dạng postcode của quốc gia tương ứng thuộc trường PostCodeCountry", }, + { + ns: "Test.Image", + expected: "Image phải là một hình ảnh hợp lệ", + }, } for _, tt := range tests { diff --git a/translations/zh/zh.go b/translations/zh/zh.go index 80165d0..7f45681 100644 --- a/translations/zh/zh.go +++ b/translations/zh/zh.go @@ -1428,6 +1428,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} 必须是有效图像", + override: false, + }, } for _, t := range translations { diff --git a/translations/zh/zh_test.go b/translations/zh/zh_test.go index cf76590..aaf512d 100644 --- a/translations/zh/zh_test.go +++ b/translations/zh/zh_test.go @@ -148,6 +148,7 @@ func TestTranslations(t *testing.T) { LowercaseString string `validate:"lowercase"` UppercaseString string `validate:"uppercase"` Datetime string `validate:"datetime=2006-01-02"` + Image string `validate:"image"` } var test Test @@ -676,6 +677,10 @@ func TestTranslations(t *testing.T) { ns: "Test.Datetime", expected: "Datetime的格式必须是2006-01-02", }, + { + ns: "Test.Image", + expected: "Image 必须是有效图像", + }, } for _, tt := range tests { diff --git a/translations/zh_tw/zh_tw.go b/translations/zh_tw/zh_tw.go index c39a8d7..40cbec2 100644 --- a/translations/zh_tw/zh_tw.go +++ b/translations/zh_tw/zh_tw.go @@ -1324,6 +1324,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} 必須是有效圖像", + override: false, + }, } for _, t := range translations { diff --git a/translations/zh_tw/zh_tw_test.go b/translations/zh_tw/zh_tw_test.go index 129c51a..f15f9f2 100644 --- a/translations/zh_tw/zh_tw_test.go +++ b/translations/zh_tw/zh_tw_test.go @@ -140,6 +140,7 @@ func TestTranslations(t *testing.T) { OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` Datetime string `validate:"datetime=2006-01-02"` + Image string `validate:"image"` } var test Test @@ -626,6 +627,10 @@ func TestTranslations(t *testing.T) { ns: "Test.Datetime", expected: "Datetime與2006-01-02格式不匹配", }, + { + ns: "Test.Image", + expected: "Image 必須是有效圖像", + }, } for _, tt := range tests { diff --git a/validator_test.go b/validator_test.go index 1ebdea5..ca68e99 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8,6 +8,10 @@ import ( "encoding/base64" "encoding/json" "fmt" + "image" + "image/png" + "image/jpeg" + "os" "path/filepath" "reflect" "strings" @@ -5650,6 +5654,108 @@ func TestFileValidation(t *testing.T) { }, "Bad field type int") } +func TestImageValidation(t *testing.T) { + validate := New() + + paths := map[string]string{ + "empty": "", + "directory": "testdata", + "missing": filepath.Join("testdata", "none.png"), + "png": filepath.Join("testdata", "image.png"), + "jpeg": filepath.Join("testdata", "image.jpg"), + "mp3": filepath.Join("testdata", "music.mp3"), + } + + tests := []struct { + title string + param string + expected bool + createImage func() + destroyImage func() + }{ + { + "empty path", + paths["empty"], false, + func () {}, + func () {}, + }, + { + "directory, not a file", + paths["directory"], + false, + func () {}, + func () {}, + }, + { + "missing file", + paths["missing"], + false, + func () {}, + func () {}, + }, + { + "valid png", + paths["png"], + true, + func () { + img := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{10, 10}}) + f, _ := os.Create(paths["png"]) + err := png.Encode(f, img) + if err != nil { + panic(fmt.Sprintf("Could not encode file in PNG. Error: %s", err)) + } + }, + func () { + os.Remove(paths["png"]) + }, + }, + { + "valid jpeg", + paths["jpeg"], + true, + func () { + var opt jpeg.Options + img := image.NewGray(image.Rect(0, 0, 10, 10)) + f, _ := os.Create(paths["jpeg"]) + err := jpeg.Encode(f, img, &opt) + if err != nil { + panic(fmt.Sprintf("Could not encode file in JPEG. Error: %s", err)) + } + }, + func () { + os.Remove(paths["jpeg"]) + }, + }, + { + "valid mp3", + paths["mp3"], + false, + func () {}, + func () {}, + }, + } + + for _, test := range tests { + test.createImage() + errs := validate.Var(test.param, "image") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Test: '%s' failed Error: %s", test.title, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Test: '%s' failed Error: %s", test.title, errs) + } + } + test.destroyImage() + } + + PanicMatches(t, func() { + _ = validate.Var(6, "file") + }, "Bad field type int") +} + func TestEthereumAddressValidation(t *testing.T) { validate := New()