diff --git a/CHANGELOG.md b/CHANGELOG.md index 7038b3e..37bd7a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,9 @@ input. likewise for `MY_LAT`/`MY_LON`. It’s possible to know latitude without also knowing longitude, but modern technology usually gives both together, so you probably had a copy-paste error. +* `validate` handles the `CNTY_ALT`/`MY_CNTY_ALT` field, currently only used for + New Zealand regions. Currently doesn’t require that each enumeration name + only appears once. [CQ Zones](https://mapability.com/ei8ic/maps/cqzone.php) and [ITU Zones](https://mapability.com/ei8ic/maps/ituzone.php): diff --git a/adif/spec/validate.go b/adif/spec/validate.go index bb76a2e..5dd3e36 100644 --- a/adif/spec/validate.go +++ b/adif/spec/validate.go @@ -86,33 +86,34 @@ type ValidationContext struct { type FieldValidator func(value string, f Field, ctx ValidationContext) Validation var TypeValidators = map[string]FieldValidator{ - "Boolean": ValidateBoolean, - "Character": ValidateCharacter, - "Enumeration": ValidateEnumeration, - "IntlCharacter": ValidateIntlCharacter, - "Date": ValidateDate, - "Digit": ValidateDigit, - "GridSquare": gridsquareValidator(8), - "GridSquareExt": gridsquareValidator(4), - "GridSquareList": listValidator(gridsquareValidator(8)), - "Integer": ValidateNumber, - "IntlString": ValidateIntlString, - "IntlMultilineString": ValidateIntlString, - "IOTARefNo": formatValidator("IOTA reference", iotaPat), - "Location": ValidateLocation, - "MultilineString": ValidateString, - "Number": ValidateNumber, - "POTARef": formatValidator("POTA reference", potaPat), - "POTARefList": listValidator(formatValidator("POTA reference", potaPat)), - "PositiveInteger": ValidateNumber, - "SOTARef": formatValidator("SOTA reference", sotaPat), - "String": ValidateString, - "Time": ValidateTime, - "WWFFRef": formatValidator("WWFF reference", wwffPat), - "AwardList": ValidateNoop, // TODO - "CreditList": ValidateNoop, // TODO - "SecondarySubdivisionList": ValidateNoop, // TODO - "SponsoredAwardList": ValidateNoop, // TODO + BooleanDataType.Name: ValidateBoolean, + CharacterDataType.Name: ValidateCharacter, + EnumerationDataType.Name: ValidateEnumeration, + IntlCharacterDataType.Name: ValidateIntlCharacter, + DateDataType.Name: ValidateDate, + DigitDataType.Name: ValidateDigit, + GridSquareDataType.Name: gridsquareValidator(8), + GridSquareExtDataType.Name: gridsquareValidator(4), + GridSquareListDataType.Name: listValidator(gridsquareValidator(8), ","), + IntegerDataType.Name: ValidateNumber, + IntlStringDataType.Name: ValidateIntlString, + IntlMultilineStringDataType.Name: ValidateIntlString, + IOTARefNoDataType.Name: formatValidator("IOTA reference", iotaPat), + LocationDataType.Name: ValidateLocation, + MultilineStringDataType.Name: ValidateString, + NumberDataType.Name: ValidateNumber, + POTARefDataType.Name: formatValidator("POTA reference", potaPat), + POTARefListDataType.Name: listValidator(formatValidator("POTA reference", potaPat), ","), + PositiveIntegerDataType.Name: ValidateNumber, + SecondaryAdministrativeSubdivisionListAltDataType.Name: listValidator(enumValidator(SecondaryAdministrativeSubdivisionAltEnumeration), ";"), + SOTARefDataType.Name: formatValidator("SOTA reference", sotaPat), + StringDataType.Name: ValidateString, + TimeDataType.Name: ValidateTime, + WWFFRefDataType.Name: formatValidator("WWFF reference", wwffPat), + AwardListDataType.Name: ValidateNoop, // TODO + CreditListDataType.Name: ValidateNoop, // TODO + SecondarySubdivisionListDataType.Name: ValidateNoop, // TODO + SponsoredAwardListDataType.Name: ValidateNoop, // TODO } func ValidateNoop(value string, f Field, ctx ValidationContext) Validation { return valid() } @@ -409,27 +410,33 @@ func ValidateEnumeration(val string, f Field, ctx ValidationContext) Validation if f.EnumScope != "" { return ValidateEnumScope(val, f, ctx) } - vals := e.Value(val) - if len(vals) == 0 { - fn := errorf - if ctx.UnknownEnumValueWarning { - fn = warningf - } - return fn("%s unknown value %q for enumeration %s", f.Name, val, e.Name) - } - if f.Name == ContField.Name { - if d := ctx.FieldValue(DxccField.Name); d != "" { - if c := ContinentFor(d); !strings.EqualFold(val, c.Abbreviation) { - return warningf("continent %s does not match DXCC %s continent %s", val, d, c.Abbreviation) + return enumValidator(e)(val, f, ctx) +} + +func enumValidator(e Enumeration) FieldValidator { + return func(val string, f Field, ctx ValidationContext) Validation { + vals := e.Value(val) + if len(vals) == 0 { + fn := errorf + if ctx.UnknownEnumValueWarning { + fn = warningf } + return fn("%s unknown value %q for enumeration %s", f.Name, val, e.Name) } - if d := ctx.FieldValue(CountryField.Name); d != "" { - if c := ContinentFor(d); !strings.EqualFold(val, c.Abbreviation) { - return warningf("continent %s does not match country %s continent %s", val, d, c.Abbreviation) + if f.Name == ContField.Name { + if d := ctx.FieldValue(DxccField.Name); d != "" { + if c := ContinentFor(d); !strings.EqualFold(val, c.Abbreviation) { + return warningf("continent %s does not match DXCC %s continent %s", val, d, c.Abbreviation) + } + } + if d := ctx.FieldValue(CountryField.Name); d != "" { + if c := ContinentFor(d); !strings.EqualFold(val, c.Abbreviation) { + return warningf("continent %s does not match country %s continent %s", val, d, c.Abbreviation) + } } } + return valid() } - return valid() } func ValidateEnumScope(val string, f Field, ctx ValidationContext) Validation { @@ -515,12 +522,12 @@ func gridsquareValidator(maxLen int) FieldValidator { } } -func listValidator(fv FieldValidator) FieldValidator { +func listValidator(fv FieldValidator, delim string) FieldValidator { return func(val string, f Field, ctx ValidationContext) Validation { if val == "" { return valid() } - for _, v := range strings.Split(val, ",") { + for _, v := range strings.Split(val, delim) { if res := fv(v, f, ctx); res.Validity != Valid { return res } diff --git a/adif/spec/validate_test.go b/adif/spec/validate_test.go index 1f6fda5..ed67fe1 100644 --- a/adif/spec/validate_test.go +++ b/adif/spec/validate_test.go @@ -377,6 +377,9 @@ func TestValidateEnumeration(t *testing.T) { {field: DxccField, value: "US", want: InvalidError}, {field: QsoCompleteField, value: "!", want: InvalidError}, {field: ContField, value: "Europe", want: InvalidError}, + {field: CntyAltField, value: "NZ_Regions:Hawkes Bay/Wairoa", want: Valid}, + {field: MyCntyAltField, value: "NZ_Regions:Wanganui-Manawatu/Palmerston North;NZ_Regions:Hawkes Bay/Wairoa", want: Valid}, + {field: CntyAltField, value: "Roman_Empire:Thracia/Byzantium", want: InvalidError}, // unknown enum value } for _, tc := range tests { testValidator(t, tc, emptyCtx, "ValidateEnumeration")