diff --git a/README.md b/README.md index 91e787d..cc915ee 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ type Person struct { This package currently provides the following checkers: +- [ascii](doc/checkers/ascii.md) checks if the given string consists of only ASCII characters. - [max](doc/checkers/max.md) checks if the given value is less than the given maximum. - [max-length](doc/checkers/maxlength.md) checks if the length of the given value is less than the given maximum length. - [min](doc/checkers/min.md) checks if the given value is greather than the given minimum. diff --git a/ascii.go b/ascii.go new file mode 100644 index 0000000..c6c0523 --- /dev/null +++ b/ascii.go @@ -0,0 +1,37 @@ +package checker + +import ( + "reflect" + "unicode" +) + +// CheckerAscii is the name of the checker. +const CheckerAscii = "ascii" + +// ResultNotAscii indicates that the given string contains non-ASCII characters. +const ResultNotAscii = "NOT_ASCII" + +// IsAscii checks if the given string consists of only ASCII characters. +func IsAscii(value string) Result { + for i := 0; i < len(value); i++ { + if value[i] > unicode.MaxASCII { + return ResultNotAscii + } + } + + return ResultValid +} + +// makeAscii makes a checker function for the ascii checker. +func makeAscii(_ string) CheckFunc { + return checkAscii +} + +// checkAscii checks if the given string consists of only ASCII characters. +func checkAscii(value, _ reflect.Value) Result { + if value.Kind() != reflect.String { + panic("string expected") + } + + return IsAscii(value.String()) +} diff --git a/ascii_test.go b/ascii_test.go new file mode 100644 index 0000000..14afc0f --- /dev/null +++ b/ascii_test.go @@ -0,0 +1,57 @@ +package checker + +import "testing" + +func TestIsAsciiInvalid(t *testing.T) { + if IsAscii("𝄞 Music!") == ResultValid { + t.Fail() + } +} + +func TestIsAsciiValid(t *testing.T) { + if IsAscii("Checker") != ResultValid { + t.Fail() + } +} + +func TestCheckAsciiNonString(t *testing.T) { + defer FailIfNoPanic(t) + + type User struct { + Age int `checkers:"ascii"` + } + + user := &User{} + + Check(user) +} + +func TestCheckAsciiInvalid(t *testing.T) { + type User struct { + Username string `checkers:"ascii"` + } + + user := &User{ + Username: "𝄞 Music!", + } + + _, valid := Check(user) + if valid { + t.Fail() + } +} + +func TestCheckAsciiValid(t *testing.T) { + type User struct { + Username string `checkers:"ascii"` + } + + user := &User{ + Username: "checker", + } + + _, valid := Check(user) + if !valid { + t.Fail() + } +} diff --git a/checker.go b/checker.go index 2763402..cc714d8 100644 --- a/checker.go +++ b/checker.go @@ -31,6 +31,7 @@ const ResultValid Result = "VALID" // makers provides mapping to maker function for the checkers. var makers = map[string]MakeFunc{ + CheckerAscii: makeAscii, CheckerMax: makeMax, CheckerMaxLength: makeMaxLength, CheckerMin: makeMin, diff --git a/checker_test.go b/checker_test.go index ab72455..8c93d88 100644 --- a/checker_test.go +++ b/checker_test.go @@ -6,11 +6,7 @@ import ( ) func TestInitCheckersUnknown(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fail() - } - }() + defer FailIfNoPanic(t) initCheckers("unknown") } @@ -92,11 +88,7 @@ func TestCheckValid(t *testing.T) { } func TestCheckNoStruct(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fail() - } - }() + defer FailIfNoPanic(t) s := "unknown" Check(s) @@ -133,11 +125,7 @@ func TestCheckNestedStruct(t *testing.T) { } func TestNumberOfInvalid(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fail() - } - }() + defer FailIfNoPanic(t) s := "invalid" diff --git a/doc/checkers/ascii.md b/doc/checkers/ascii.md new file mode 100644 index 0000000..7ef5ca4 --- /dev/null +++ b/doc/checkers/ascii.md @@ -0,0 +1,28 @@ +# ASCII Checker + +The ```ascii``` checkr checks if the given string consists of only ASCII characters. If the string contains non-ASCII characters, the checker will return the ```NOT_ASCII``` result. Here is an example: + +```golang +type User struct { + Username string `checkers:"ascii"` +} + +user := &User{ + Username: "checker", +} + +_, valid := Check(user) +if !valid { + // Send the mistakes back to the user +} +``` + +In your custom checkers, you can call the ```ascii``` checker function ```IsAscii``` to validate the user input. Here is an example: + +```golang +result := IsAscii("Checker") + +if result != ResultValid { + // Send the mistakes back to the user +} +``` diff --git a/max_test.go b/max_test.go index 4c3afaf..af1f4a4 100644 --- a/max_test.go +++ b/max_test.go @@ -11,11 +11,7 @@ func TestIsMaxValid(t *testing.T) { } func TestCheckMaxInvalidConfig(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fail() - } - }() + defer FailIfNoPanic(t) type Order struct { Quantity int `checkers:"max:AB"` diff --git a/maxlength_test.go b/maxlength_test.go index 496dca4..12529cc 100644 --- a/maxlength_test.go +++ b/maxlength_test.go @@ -11,11 +11,7 @@ func TestIsMaxLengthValid(t *testing.T) { } func TestCheckMaxLengthInvalidConfig(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fail() - } - }() + defer FailIfNoPanic(t) type User struct { Password string `checkers:"max-length:AB"` diff --git a/min_test.go b/min_test.go index dbe0d47..d3a4adc 100644 --- a/min_test.go +++ b/min_test.go @@ -11,11 +11,7 @@ func TestIsMinValid(t *testing.T) { } func TestCheckMinInvalidConfig(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fail() - } - }() + defer FailIfNoPanic(t) type User struct { Age int `checkers:"min:AB"` diff --git a/minlength_test.go b/minlength_test.go index 5c748c7..e5b2cf8 100644 --- a/minlength_test.go +++ b/minlength_test.go @@ -11,11 +11,7 @@ func TestIsMinLengthValid(t *testing.T) { } func TestCheckMinLengthInvalidConfig(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fail() - } - }() + defer FailIfNoPanic(t) type User struct { Password string `checkers:"min-length:AB"` diff --git a/same_test.go b/same_test.go index b2a1db8..657f3b9 100644 --- a/same_test.go +++ b/same_test.go @@ -40,11 +40,7 @@ func TestSameInvalid(t *testing.T) { } func TestSameWithoutParent(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fail() - } - }() + defer FailIfNoPanic(t) type User struct { Password string @@ -60,11 +56,7 @@ func TestSameWithoutParent(t *testing.T) { } func TestSameInvalidName(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fail() - } - }() + defer FailIfNoPanic(t) type User struct { Password string diff --git a/test_helper.go b/test_helper.go new file mode 100644 index 0000000..da7ecd5 --- /dev/null +++ b/test_helper.go @@ -0,0 +1,13 @@ +//go:build !test +// +build !test + +package checker + +import "testing" + +// FailIfNoPanic fails if test didn't panic. Use this function with the defer. +func FailIfNoPanic(t *testing.T) { + if r := recover(); r == nil { + t.Fail() + } +} diff --git a/test_helper_test.go b/test_helper_test.go new file mode 100644 index 0000000..e5e5d11 --- /dev/null +++ b/test_helper_test.go @@ -0,0 +1,13 @@ +package checker + +import "testing" + +func TestFailIfNoPanicValid(t *testing.T) { + defer FailIfNoPanic(t) + panic("") +} + +func TestFailIfNoPanicInvalid(t *testing.T) { + defer FailIfNoPanic(t) + defer FailIfNoPanic(nil) +}