Skip to content

constraint.StringCharacters

marrow16 edited this page Jan 21, 2023 · 7 revisions

Prev Home Next

Constraint: StringCharacters

Description

Check that a string contains only allowable characters (and does not contain any disallowed characters)

Note: By default, this constraint is non-strict - if the value being checked is not a string, this constraint does not fail (unless the Strict field is set)

V8n Tag Abbreviation

strchars

Fields

Field Type Description
AllowRanges []unicode.RangeTable the ranges of characters (runes) that are allowed - if this slice is non-empty, each character must be in at least one of these
NamedAllowRanges []string the named ranges of characters (runes) that are allowed - if this slice is non-empty, each character must be in at least one of these
DisallowRanges []unicode.RangeTable the ranges of characters (runes) that are not allowed - if any character in the string is in any of these ranges then the constraint is violated
NamedDisallowRanges []string the named ranges of characters (runes) that are not allowed - if any character is in any of these ranges then the constraint is violated
Message string the violation message to be used if the constraint fails. If empty, the default message is used
Stop bool when set to true, Stop prevents further validation checks on the property if this constraint fails
Strict bool when set to true, fails if the value being checked is not a correct type

For the NamedAllowRanges and NamedDisallowRanges fields, the range names can be:

  • "BMP", "SMP" or "SIP" (Basic Multilingual Plane, Supplementary Multilingual Plane or Supplementary Ideographic Plane respectively)
  • any name from unicode.Categories prefixed with "Category-"
  • any name from unicode.Scripts prefixed with "Script-"
  • any name from unicode.Properties prefixed with "Property-"
  • any name from unicode.FoldCategory prefixed with "FoldCategory-"
  • any name from unicode.FoldScript prefixed with "FoldScript-"

Examples

Programmatic example...
package main

import (
    "fmt"
    "unicode"

    "github.com/marrow16/valix"
)

func main() {
    validator := &valix.Validator{
        Properties: valix.Properties{
            "foo": {
                Type: valix.JsonString,
                Constraints: valix.Constraints{
                    &valix.StringCharacters{
                        AllowRanges: []unicode.RangeTable{
                            *unicode.Number,
                        },
                        Message: "Must not any other chars but unicode numerics",
                    },
                },
            },
        },
    }

    ok, violations, _ := validator.ValidateString(`{"foo": "not valid"}`)
    fmt.Printf("Passed? %v\n", ok)
    for i, v := range violations {
        fmt.Printf("Violation[%d] Message: %s, Property: %s, Path: %s\n", i+1, v.Message, v.Property, v.Path)
    }

    ok, violations, _ = validator.ValidateString(`{"foo": "0123456789¹²³¼½¾"}`)
    fmt.Printf("Passed? %v\n", ok)
    for i, v := range violations {
        fmt.Printf("Violation[%d] Message: %s, Property: %s, Path: %s\n", i+1, v.Message, v.Property, v.Path)
    }
}

try on go-playground

It is possible to initialise range tables within v8n tags...

However, even the simplest form can become very verbose - as the following example demonstrates for just normal numerics and superscript 1-3...

package main

import (
    "fmt"

    "github.com/marrow16/valix"
)

type MyStruct struct {
    Foo string `json:"foo" v8n:"&StringCharacters{AllowRanges:[{\"R16\":[{\"Lo\":48,\"Hi\":57,\"Stride\":1},{\"Lo\":178,\"Hi\":179,\"Stride\":1},{\"Lo\":185,\"Hi\":185,\"Stride\":1}]}], Message:'Must not any other chars but unicode numerics'}"`
}

var validator = valix.MustCompileValidatorFor(MyStruct{}, nil)

func main() {
    my := &MyStruct{}

    ok, violations, _ := validator.ValidateStringInto(`{"foo": "not valid"}`, my)
    fmt.Printf("Passed? %v\n", ok)
    for i, v := range violations {
        fmt.Printf("Violation[%d] Message: %s, Property: %s, Path: %s\n", i+1, v.Message, v.Property, v.Path)
    }

    ok, violations, _ = validator.ValidateStringInto(`{"foo": "0123456789¹²³"}`, my)
    fmt.Printf("Passed? %v\n", ok)
    for i, v := range violations {
        fmt.Printf("Violation[%d] Message: %s, Property: %s, Path: %s\n", i+1, v.Message, v.Property, v.Path)
    }
}

try on go-playground

Therefore, it's recommended to register specific unicode allowed characters as new constraints - then use those constraints in v8n tags...

package main

import (
    "fmt"
    "github.com/marrow16/valix"
    "unicode"
)

type MyStruct struct {
    Foo string `json:"foo" v8n:"&unicodeNumerics{Message:'Overridden default message'}"`
}

var validator *valix.Validator

func init() {
    valix.RegisterNamedConstraint("unicodeNumerics", &valix.StringCharacters{
        AllowRanges: []unicode.RangeTable{
            {
                R16: []unicode.Range16{
                    {0x0030, 0x0039, 1},
                    {0x00b2, 0x00b3, 1},
                    {0x00b9, 0x00b9, 1},
                },
            },
        },
        Message: "Characters must be digits or superscript digits",
    })
    validator = valix.MustCompileValidatorFor(MyStruct{}, nil)
}

func main() {
    my := &MyStruct{}

    ok, violations, _ := validator.ValidateStringInto(`{"foo": "not valid"}`, my)
    fmt.Printf("Passed? %v\n", ok)
    for i, v := range violations {
        fmt.Printf("Violation[%d] Message: %s, Property: %s, Path: %s\n", i+1, v.Message, v.Property, v.Path)
    }

    ok, violations, _ = validator.ValidateStringInto(`{"foo": "0123456789¹²³"}`, my)
    fmt.Printf("Passed? %v\n", ok)
    for i, v := range violations {
        fmt.Printf("Violation[%d] Message: %s, Property: %s, Path: %s\n", i+1, v.Message, v.Property, v.Path)
    }
}

try on go-playground

Clone this wiki locally