diff --git a/go.mod b/go.mod index 2c47779..7a90445 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,16 @@ module github.com/issue9/webfilter go 1.21 require ( + github.com/google/uuid v1.6.0 github.com/issue9/assert/v4 v4.1.1 - github.com/issue9/web v0.87.4 + github.com/issue9/version v1.0.8 + github.com/issue9/web v0.87.7 ) require ( - github.com/issue9/cache v0.9.2 // indirect + github.com/issue9/cache v0.10.0 // indirect github.com/issue9/config v0.6.2 // indirect + github.com/issue9/errwrap v0.3.2 // indirect github.com/issue9/localeutil v0.26.5 // indirect github.com/issue9/scheduled v0.19.3 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index d452315..ba04b1c 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,20 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/issue9/assert/v4 v4.1.1 h1:OhPE8SB8n/qZCNGLQa+6MQtr/B3oON0JAVj68k8jJlc= github.com/issue9/assert/v4 v4.1.1/go.mod h1:v7qDRXi7AsaZZNh8eAK2rkLJg5/clztqQGA1DRv9Lv4= -github.com/issue9/cache v0.9.2 h1:tMvXPhGKBXbSIMHi5qzsdwmQjf7qGYBo1W/LLTDHw9s= -github.com/issue9/cache v0.9.2/go.mod h1:gFTzhzGGAc6Dpon9fbWtJNCjeG3QiLs+bFc8j5z98Qg= +github.com/issue9/cache v0.10.0 h1:pgw4kbAFUeUu9pOKcNSdc+BhhXwdRFzjwAvWAX2BWHc= +github.com/issue9/cache v0.10.0/go.mod h1:0s9j7qiKv4uWYqz0D2N2H7bIBvmtD+903h5GqnxW6i4= github.com/issue9/config v0.6.2 h1:znXvsk6gh0wm+fTEn0zUjjramKuOLY8Jt0ZTxp4GIkc= github.com/issue9/config v0.6.2/go.mod h1:S97FVtZim4rPB+M49EoOuBajaqY4ULQbqltZmZ99+Dc= +github.com/issue9/errwrap v0.3.2 h1:7KEme9Pfe75M+sIMcPCn/DV90wjnOcRbO4DXVAHj3Fw= +github.com/issue9/errwrap v0.3.2/go.mod h1:KcCLuUGiffjooLCUjL89r1cyO8/HT/VRcQrneO53N3A= github.com/issue9/localeutil v0.26.5 h1:e78b6cOOtgzfb4g4U9uPLC8QyK6Lux+s7ZiQe+6iM1A= github.com/issue9/localeutil v0.26.5/go.mod h1:BJXJwcAT9CyyVZOlqfmq+B5FcPbqGxGjYnTYbVuiMM8= github.com/issue9/scheduled v0.19.3 h1:hI/8gh1SMH62inPVsrZazRBxc91RQT/YbnWr1S9se08= github.com/issue9/scheduled v0.19.3/go.mod h1:4OTuwC3QMRJLGRV8ImmRS2bqX58bDjKi2OAuzHcgJuQ= -github.com/issue9/web v0.87.4 h1:BD6+ZjSCnlYS6G8j0xOJxmjsHkhaOAPP0Oe5OW3z+3M= -github.com/issue9/web v0.87.4/go.mod h1:5nkr+8J/LD1WAyjA/4gmWgi3IOxRV37cR6c3EJ062b8= +github.com/issue9/version v1.0.8 h1:IsNdDYdV8UGDGwwgp8H4RszJE0Ko26HjWg9pZzyOivs= +github.com/issue9/version v1.0.8/go.mod h1:w8bQwODBOG5+iaS3qIJbElxxpp3Uo4x5F39qKBqwpdc= +github.com/issue9/web v0.87.7 h1:Z2EUzAs/84x3EFZr7EuTxnlXcHE1X2of67eIq+7vfaE= +github.com/issue9/web v0.87.7/go.mod h1:6RK59GaungHYQgTx4WDSSE0O9GJTT4RynhXgiQnPzJo= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/validator/semver.go b/validator/semver.go new file mode 100644 index 0000000..f70198a --- /dev/null +++ b/validator/semver.go @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2022-2024 caixw +// +// SPDX-License-Identifier: MIT + +package validator + +import "github.com/issue9/version" + +// Semver [semver] 版本号验证 +// +// [semver]: https://semver.org/lang/zh-CN/ +func Semver(ver string) bool { return version.SemVerValid(ver) } + +// SemverCompatible 创建一个验证 [semver] 是否兼容 val 的验证器 +func SemverCompatible(ver string) func(string) bool { + semver, err := version.SemVer(ver) + if err != nil { + panic(err) + } + + return func(v string) bool { + ok, err := semver.CompatibleString(v) + return err == nil && ok + } +} + +// SemverGreat 判断版本号是否大于 ver +func SemverGreat(ver string) func(string) bool { + semver, err := version.SemVer(ver) + if err != nil { + panic(err) + } + + return func(val string) bool { + num, err := semver.CompareString(val) + return err == nil && num < 0 + } +} + +func SemverGreatEqual(ver string) func(string) bool { + semver, err := version.SemVer(ver) + if err != nil { + panic(err) + } + + return func(val string) bool { + num, err := semver.CompareString(val) + return err == nil && num <= 0 + } +} + +func SemverLess(ver string) func(string) bool { + semver, err := version.SemVer(ver) + if err != nil { + panic(err) + } + + return func(val string) bool { + num, err := semver.CompareString(val) + return err == nil && num > 0 + } +} + +func SemverLessEqual(ver string) func(string) bool { + semver, err := version.SemVer(ver) + if err != nil { + panic(err) + } + + return func(val string) bool { + num, err := semver.CompareString(val) + return err == nil && num >= 0 + } +} diff --git a/validator/semver_test.go b/validator/semver_test.go new file mode 100644 index 0000000..a8f42b3 --- /dev/null +++ b/validator/semver_test.go @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2022-2024 caixw +// +// SPDX-License-Identifier: MIT + +package validator + +import ( + "testing" + + "github.com/issue9/assert/v4" +) + +func TestSemver(t *testing.T) { + a := assert.New(t, false) + a.True(Semver("1.0.0")). + False(Not(Semver)("1.0.0")) +} + +func TestSemverCompatible(t *testing.T) { + a := assert.New(t, false) + + v := SemverCompatible("1.0.0") + a.True(v("1.1.1")).False(v("2.0.1")) +} + +func TestSemverGreat(t *testing.T) { + a := assert.New(t, false) + + v := SemverGreat("1.0.0") + a.True(v("1.1.1")).False(v("1.0.0")) + + v = SemverGreatEqual("1.0.0") + a.True(v("1.1.1")).True(v("1.0.0")) +} + +func TestSemverLess(t *testing.T) { + a := assert.New(t, false) + + v := SemverLess("2.0.0") + a.True(v("1.1.1")).False(v("2.0.0")) + + v = SemverLessEqual("2.0.0") + a.True(v("1.1.1")).True(v("2.0.0")) +} diff --git a/validator/string.go b/validator/string.go index c1d689c..19c70b3 100644 --- a/validator/string.go +++ b/validator/string.go @@ -10,6 +10,7 @@ import ( "net/url" "strings" + "github.com/google/uuid" "github.com/issue9/webfilter/gb11643" "github.com/issue9/webfilter/gb32100" "github.com/issue9/webfilter/internal/isbn" @@ -38,17 +39,13 @@ func Email(val string) bool { } func IP4(val string) bool { - if ip, err := netip.ParseAddr(val); err == nil { - return ip.Is4() - } - return false + ip, err := netip.ParseAddr(val) + return err == nil && ip.Is4() } func IP6(val string) bool { - if ip, err := netip.ParseAddr(val); err == nil { - return ip.Is6() - } - return false + ip, err := netip.ParseAddr(val) + return err == nil && ip.Is6() } func IP(val string) bool { @@ -85,9 +82,7 @@ func BankCard(val string) bool { return Luhn(val) } func Luhn(val string) bool { return luhn.IsValid([]byte(val)) } // HexColor 判断一个字符串是否为合法的 16 进制颜色表示法 -func HexColor(val string) bool { return hexColor([]byte(val)) } - -func hexColor(val []byte) bool { +func HexColor(val string) bool { if len(val) != 4 && len(val) != 7 { return false } @@ -96,7 +91,12 @@ func hexColor(val []byte) bool { return false } - for _, v := range val[1:] { + return Hex(val[1:]) +} + +// Hex 是否符合 16 进制数字 +func Hex(val string) bool { + for _, v := range val { switch { case '0' <= v && v <= '9': case 'a' <= v && v <= 'f': @@ -116,7 +116,8 @@ func EndWith(suffix string) func(string) bool { return func(s string) bool { return strings.HasSuffix(s, suffix) } } -func Ascii(s string) bool { +// ASCII ASCII 码 +func ASCII(s string) bool { for _, c := range s { if c > 127 { return false @@ -125,6 +126,7 @@ func Ascii(s string) bool { return true } +// Alpha 全部都是英文字符 func Alpha(s string) bool { for _, c := range s { if c < 'a' || c > 'z' && (c < 'A' || c > 'Z') { @@ -162,10 +164,11 @@ func CNMobile(val string) bool { return false } - return isNumber(val[1:]) + return Digit(val[1:]) } -func isNumber(val string) bool { +// Digit 判断字符串是否都为数字 +func Digit(val string) bool { for _, c := range val[1:] { if c < '0' || c > '9' { return false @@ -173,3 +176,6 @@ func isNumber(val string) bool { } return true } + +// UUID 验证 UUID 格式是否正确 +func UUID(val string) bool { return uuid.Validate(val) == nil } diff --git a/validator/string_test.go b/validator/string_test.go index b8eabec..2e2da58 100644 --- a/validator/string_test.go +++ b/validator/string_test.go @@ -93,11 +93,11 @@ func TestURL(t *testing.T) { a.True(URL("https://[::1]/path/index.go?arg1=val1")) } -func TestAscii(t *testing.T) { +func TestASCII(t *testing.T) { a := assert.New(t, false) - a.True(Ascii("abc")) - a.False(Ascii("\u1000")) + a.True(ASCII("abc")) + a.False(ASCII("\u1000")) } func TestAlpha(t *testing.T) { diff --git a/validator/validator.go b/validator/validator.go index 7f53ce5..4f711e9 100644 --- a/validator/validator.go +++ b/validator/validator.go @@ -7,7 +7,10 @@ // [web.filter]: https://pkg.go.dev/github.com/issue9/web#Filter package validator -import "reflect" +import ( + "encoding/json" + "reflect" +) // And 以与的形式串联多个验证器函数 func And[T any](v ...func(T) bool) func(T) bool { @@ -48,3 +51,5 @@ func Equal[T comparable](v T) func(T) bool { func NilOr[T any](v func(T) bool) func(T) bool { return Or(func(v T) bool { return reflect.ValueOf(v).IsNil() }, v) } + +func JSON(val []byte) bool { return json.Valid(val) }