Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
* Reducing repeating code
* Fix `StringCharacters` constraint (should not use ptrs to `unicode.RangeTable`)
* Fix small bugs in tag parsing
* Added docs on public vars and consts
* Made pattern preset consts public
* Added `PropertyValidator.ToV8nTagString()` method (converts to v8n tag representation)
  • Loading branch information
marrow16 committed Oct 9, 2022
1 parent 89b6fba commit ac53dc6
Show file tree
Hide file tree
Showing 27 changed files with 2,337 additions and 999 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4151,15 +4151,15 @@ Valix provides a rich set of pre-defined common constraints - listed here for re
<table>
<tr>
<td>
<code>AllowRanges</code> <em>[]*unicode.RangeTable</em>
<code>AllowRanges</code> <em>[]unicode.RangeTable</em>
</td>
<td>
the ranges of characters (runes) that are allowed - each character must be in at least one of these
</td>
</tr>
<tr>
<td>
<code>DisallowRanges</code> <em>[]*unicode.RangeTable</em>
<code>DisallowRanges</code> <em>[]unicode.RangeTable</em>
</td>
<td>
the ranges of characters (runes) that are not allowed - if any character is in any of these ranges then the constraint is violated
Expand Down Expand Up @@ -6366,10 +6366,10 @@ var MySet = &valix.ConstraintSet{
return true, ""
}, ""),
&valix.StringCharacters{
AllowRanges: []*unicode.RangeTable{
AllowRanges: []unicode.RangeTable{
{R16: []unicode.Range16{{'0', 'z', 1}}},
},
DisallowRanges: []*unicode.RangeTable{
DisallowRanges: []unicode.RangeTable{
{R16: []unicode.Range16{{0x003a, 0x0040, 1}}},
{R16: []unicode.Range16{{0x005b, 0x005e, 1}}},
{R16: []unicode.Range16{{0x0060, 0x0060, 1}}},
Expand Down
116 changes: 66 additions & 50 deletions constraint_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,56 +239,56 @@ func defaultConstraints() map[string]Constraint {
"iso4217-numeric": &StringValidCurrencyCode{NumericOnly: true},
"lang": &StringValidLanguageCode{},
// preset patterns...
presetTokenAlpha: &StringPresetPattern{Preset: presetTokenAlpha},
presetTokenAlphaNumeric: &StringPresetPattern{Preset: presetTokenAlphaNumeric},
presetTokenBarcode: &StringPresetPattern{Preset: presetTokenBarcode},
presetTokenBase64: &StringPresetPattern{Preset: presetTokenBase64},
presetTokenBase64URL: &StringPresetPattern{Preset: presetTokenBase64URL},
presetTokenCard: &StringPresetPattern{Preset: presetTokenCard},
presetTokenCMYK: &StringPresetPattern{Preset: presetTokenCMYK},
presetTokenCMYK300: &StringPresetPattern{Preset: presetTokenCMYK300},
presetTokenE164: &StringPresetPattern{Preset: presetTokenE164},
presetTokenEAN: &StringPresetPattern{Preset: presetTokenEAN},
presetTokenEAN8: &StringPresetPattern{Preset: presetTokenEAN8},
presetTokenEAN13: &StringPresetPattern{Preset: presetTokenEAN13},
presetTokenDUN14: &StringPresetPattern{Preset: presetTokenDUN14},
presetTokenEAN14: &StringPresetPattern{Preset: presetTokenEAN14},
presetTokenEAN18: &StringPresetPattern{Preset: presetTokenEAN18},
presetTokenEAN99: &StringPresetPattern{Preset: presetTokenEAN99},
presetTokenHexadecimal: &StringPresetPattern{Preset: presetTokenHexadecimal},
presetTokenHsl: &StringPresetPattern{Preset: presetTokenHsl},
presetTokenHsla: &StringPresetPattern{Preset: presetTokenHsla},
presetTokenHtmlColor: &StringPresetPattern{Preset: presetTokenHtmlColor},
presetTokenInteger: &StringPresetPattern{Preset: presetTokenInteger},
presetTokenISBN: &StringPresetPattern{Preset: presetTokenISBN},
presetTokenISBN10: &StringPresetPattern{Preset: presetTokenISBN10},
presetTokenISBN13: &StringPresetPattern{Preset: presetTokenISBN13},
presetTokenISSN: &StringPresetPattern{Preset: presetTokenISSN},
presetTokenISSN8: &StringPresetPattern{Preset: presetTokenISSN8},
presetTokenISSN13: &StringPresetPattern{Preset: presetTokenISSN13},
presetTokenNumeric: &StringPresetPattern{Preset: presetTokenNumeric},
presetTokenNumericE: &StringPresetPattern{Preset: presetTokenNumericE},
presetTokenNumericX: &StringPresetPattern{Preset: presetTokenNumericX},
presetTokenPublication: &StringPresetPattern{Preset: presetTokenPublication},
presetTokenRgb: &StringPresetPattern{Preset: presetTokenRgb},
presetTokenRgba: &StringPresetPattern{Preset: presetTokenRgba},
presetTokenRgbIcc: &StringPresetPattern{Preset: presetTokenRgbIcc},
presetTokenULID: &StringPresetPattern{Preset: presetTokenULID},
presetTokenUPC: &StringPresetPattern{Preset: presetTokenUPC},
presetTokenUPCA: &StringPresetPattern{Preset: presetTokenUPCA},
presetTokenUPCE: &StringPresetPattern{Preset: presetTokenUPCE},
presetTokenUuid: &StringPresetPattern{Preset: presetTokenUuid},
presetTokenUUID: &StringPresetPattern{Preset: presetTokenUUID},
presetTokenUuid1: &StringPresetPattern{Preset: presetTokenUuid1},
presetTokenUUID1: &StringPresetPattern{Preset: presetTokenUUID1},
presetTokenUuid2: &StringPresetPattern{Preset: presetTokenUuid2},
presetTokenUUID2: &StringPresetPattern{Preset: presetTokenUUID2},
presetTokenUuid3: &StringPresetPattern{Preset: presetTokenUuid3},
presetTokenUUID3: &StringPresetPattern{Preset: presetTokenUUID3},
presetTokenUuid4: &StringPresetPattern{Preset: presetTokenUuid4},
presetTokenUUID4: &StringPresetPattern{Preset: presetTokenUUID4},
presetTokenUuid5: &StringPresetPattern{Preset: presetTokenUuid5},
presetTokenUUID5: &StringPresetPattern{Preset: presetTokenUUID5},
PresetAlpha: &StringPresetPattern{Preset: PresetAlpha},
PresetAlphaNumeric: &StringPresetPattern{Preset: PresetAlphaNumeric},
PresetBarcode: &StringPresetPattern{Preset: PresetBarcode},
PresetBase64: &StringPresetPattern{Preset: PresetBase64},
PresetBase64URL: &StringPresetPattern{Preset: PresetBase64URL},
PresetCard: &StringPresetPattern{Preset: PresetCard},
PresetCMYK: &StringPresetPattern{Preset: PresetCMYK},
PresetCMYK300: &StringPresetPattern{Preset: PresetCMYK300},
PresetE164: &StringPresetPattern{Preset: PresetE164},
PresetEAN: &StringPresetPattern{Preset: PresetEAN},
PresetEAN8: &StringPresetPattern{Preset: PresetEAN8},
PresetEAN13: &StringPresetPattern{Preset: PresetEAN13},
PresetDUN14: &StringPresetPattern{Preset: PresetDUN14},
PresetEAN14: &StringPresetPattern{Preset: PresetEAN14},
PresetEAN18: &StringPresetPattern{Preset: PresetEAN18},
PresetEAN99: &StringPresetPattern{Preset: PresetEAN99},
PresetHexadecimal: &StringPresetPattern{Preset: PresetHexadecimal},
PresetHsl: &StringPresetPattern{Preset: PresetHsl},
PresetHsla: &StringPresetPattern{Preset: PresetHsla},
PresetHtmlColor: &StringPresetPattern{Preset: PresetHtmlColor},
PresetInteger: &StringPresetPattern{Preset: PresetInteger},
PresetISBN: &StringPresetPattern{Preset: PresetISBN},
PresetISBN10: &StringPresetPattern{Preset: PresetISBN10},
PresetISBN13: &StringPresetPattern{Preset: PresetISBN13},
PresetISSN: &StringPresetPattern{Preset: PresetISSN},
PresetISSN8: &StringPresetPattern{Preset: PresetISSN8},
PresetISSN13: &StringPresetPattern{Preset: PresetISSN13},
PresetNumeric: &StringPresetPattern{Preset: PresetNumeric},
PresetNumericE: &StringPresetPattern{Preset: PresetNumericE},
PresetNumericX: &StringPresetPattern{Preset: PresetNumericX},
PresetPublication: &StringPresetPattern{Preset: PresetPublication},
PresetRgb: &StringPresetPattern{Preset: PresetRgb},
PresetRgba: &StringPresetPattern{Preset: PresetRgba},
PresetRgbIcc: &StringPresetPattern{Preset: PresetRgbIcc},
PresetULID: &StringPresetPattern{Preset: PresetULID},
PresetUPC: &StringPresetPattern{Preset: PresetUPC},
PresetUPCA: &StringPresetPattern{Preset: PresetUPCA},
PresetUPCE: &StringPresetPattern{Preset: PresetUPCE},
PresetUuid: &StringPresetPattern{Preset: PresetUuid},
PresetUUID: &StringPresetPattern{Preset: PresetUUID},
PresetUuid1: &StringPresetPattern{Preset: PresetUuid1},
PresetUUID1: &StringPresetPattern{Preset: PresetUUID1},
PresetUuid2: &StringPresetPattern{Preset: PresetUuid2},
PresetUUID2: &StringPresetPattern{Preset: PresetUUID2},
PresetUuid3: &StringPresetPattern{Preset: PresetUuid3},
PresetUUID3: &StringPresetPattern{Preset: PresetUUID3},
PresetUuid4: &StringPresetPattern{Preset: PresetUuid4},
PresetUUID4: &StringPresetPattern{Preset: PresetUUID4},
PresetUuid5: &StringPresetPattern{Preset: PresetUuid5},
PresetUUID5: &StringPresetPattern{Preset: PresetUUID5},
}
}

Expand Down Expand Up @@ -341,6 +341,22 @@ func (r *constraintRegistry) get(name string) (Constraint, bool) {
return c, ok
}

// this method is potentially slow - but is only used by PropertyValidator.ToV8nTagString
// Note: finds only the first with matching name!
func (r *constraintRegistry) search(name string) (alias string, constraint Constraint, found bool) {
defer r.sync.Unlock()
r.sync.Lock()
for a, c := range r.namedConstraints {
if name == reflect.TypeOf(c).Elem().Name() {
alias = a
constraint = c
found = true
break
}
}
return
}

// reset for testing
func (r *constraintRegistry) reset() {
defer r.sync.Unlock()
Expand Down
12 changes: 6 additions & 6 deletions constraint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ func TestConstraintSet(t *testing.T) {
return true, ""
}, ""),
&StringCharacters{
AllowRanges: []*unicode.RangeTable{
AllowRanges: []unicode.RangeTable{
{R16: []unicode.Range16{{'0', 'z', 1}}},
},
DisallowRanges: []*unicode.RangeTable{
DisallowRanges: []unicode.RangeTable{
{R16: []unicode.Range16{{0x003a, 0x0040, 1}}},
{R16: []unicode.Range16{{0x005b, 0x005e, 1}}},
{R16: []unicode.Range16{{0x0060, 0x0060, 1}}},
Expand Down Expand Up @@ -144,10 +144,10 @@ func TestConstraintSetNoMsg(t *testing.T) {
return true, ""
}, "Must start with a capital"),
&StringCharacters{
AllowRanges: []*unicode.RangeTable{
AllowRanges: []unicode.RangeTable{
{R16: []unicode.Range16{{'0', 'z', 1}}},
},
DisallowRanges: []*unicode.RangeTable{
DisallowRanges: []unicode.RangeTable{
{R16: []unicode.Range16{{0x003a, 0x0040, 1}}},
{R16: []unicode.Range16{{0x005b, 0x005e, 1}}},
{R16: []unicode.Range16{{0x0060, 0x0060, 1}}},
Expand Down Expand Up @@ -211,10 +211,10 @@ func TestConstraintSetCeases(t *testing.T) {
return true, ""
}, "Must start with a capital"),
&StringCharacters{
AllowRanges: []*unicode.RangeTable{
AllowRanges: []unicode.RangeTable{
{R16: []unicode.Range16{{'0', 'z', 1}}},
},
DisallowRanges: []*unicode.RangeTable{
DisallowRanges: []unicode.RangeTable{
{R16: []unicode.Range16{{0x003a, 0x0040, 1}}},
{R16: []unicode.Range16{{0x005b, 0x005e, 1}}},
{R16: []unicode.Range16{{0x0060, 0x0060, 1}}},
Expand Down
76 changes: 58 additions & 18 deletions constraint_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,34 +184,31 @@ func truncateTime(t time.Time, truncTime bool) time.Time {
return t
}

var _Bmp = &unicode.RangeTable{
R16: []unicode.Range16{
{0x0000, 0xffff, 1},
},
}
var _Smp = &unicode.RangeTable{
R32: []unicode.Range32{
{0x10000, 0x1ffff, 1},
},
}
var _Sip = &unicode.RangeTable{
R32: []unicode.Range32{
{0x20000, 0x2ffff, 1},
},
}
var (
// UnicodeBMP is a unicode.RangeTable that represents the Unicode BMP (Basic Multilingual Plane)
//
// For use with StringCharacters constraint
UnicodeBMP = _Bmp
UnicodeBMP = unicode.RangeTable{
R16: []unicode.Range16{
{0x0000, 0xffff, 1},
},
}
// UnicodeSMP is a unicode.RangeTable that represents the Unicode SMP (Supplementary Multilingual Plane)
//
// For use with StringCharacters constraint
UnicodeSMP = _Smp
UnicodeSMP = unicode.RangeTable{
R32: []unicode.Range32{
{0x10000, 0x1ffff, 1},
},
}
// UnicodeSIP is a unicode.RangeTable that represents the Unicode SIP (Supplementary Ideographic Plane)
//
// For use with StringCharacters constraint
UnicodeSIP = _Sip
UnicodeSIP = unicode.RangeTable{
R32: []unicode.Range32{
{0x20000, 0x2ffff, 1},
},
}
)

var (
Expand Down Expand Up @@ -585,3 +582,46 @@ func parseDurationNumber(str string) (fv *float64, result bool) {
}
return
}

type stringConstraint interface {
Constraint
checkString(str string, vcx *ValidatorContext) bool
}

func checkStringConstraint(v interface{}, vcx *ValidatorContext, c stringConstraint, strict bool, stop bool) (bool, string) {
if str, ok := v.(string); ok {
if !c.checkString(str, vcx) {
vcx.CeaseFurtherIf(stop)
return false, c.GetMessage(vcx)
}
} else if strict {
vcx.CeaseFurtherIf(stop)
return false, c.GetMessage(vcx)
}
return true, ""
}

type dateCompareConstraint interface {
Constraint
compareDates(value time.Time, other time.Time) bool
}

func checkDateCompareConstraint(value string, other interface{}, vcx *ValidatorContext, c dateCompareConstraint, excTime bool, stop bool) (bool, string) {
if cdt, ok := stringToDatetime(value, excTime); ok {
if dt, ok := isTime(other, excTime); ok && c.compareDates(*cdt, dt) {
return true, ""
}
}
vcx.CeaseFurtherIf(stop)
return false, c.GetMessage(vcx)
}

func checkDateCompareOtherPropertyConstraint(v interface{}, otherPtyName string, vcx *ValidatorContext, c dateCompareConstraint, excTime bool, stop bool) (bool, string) {
if other, ok := getOtherPropertyDatetime(otherPtyName, vcx, excTime, false); ok {
if dt, ok := isTime(v, excTime); ok && c.compareDates(dt, *other) {
return true, ""
}
}
vcx.CeaseFurtherIf(stop)
return false, c.GetMessage(vcx)
}
4 changes: 2 additions & 2 deletions constraint_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,15 +378,15 @@ func TestCoerceJsonNumberToFloat(t *testing.T) {
}

func TestIsUniqueCompare(t *testing.T) {
distinctList := []interface{}{}
distinctList := make([]interface{}, 0)
unique := isUniqueCompare("foo", true, &distinctList)
require.True(t, unique)
unique = isUniqueCompare("Foo", true, &distinctList)
require.False(t, unique)
unique = isUniqueCompare("Foo", false, &distinctList)
require.True(t, unique)

distinctList = []interface{}{}
distinctList = make([]interface{}, 0)
unique = isUniqueCompare(1.0, false, &distinctList)
require.True(t, unique)
unique = isUniqueCompare(json.Number("1"), false, &distinctList)
Expand Down
Loading

0 comments on commit ac53dc6

Please sign in to comment.