diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7d28067..85e2b85 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,5 +16,5 @@ jobs: go-version-file: go.mod - uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 with: - version: v2.11.3 + version: v2.15.0 args: --timeout 3m diff --git a/.golangci.yaml b/.golangci.yaml index ab1234f..489f633 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -27,12 +27,14 @@ linters: - godox # Not terribly useful and ends up in too much boilerplate - exhaustruct + - exhaustruct_v5 # https://github.com/italia/developers-italia-api/issues/190) # Don't feel about chasing this one down - musttag # Meh - noinlineerr # Deprecated + - gomodguard - wsl # More false positive than actual issues diff --git a/marshallers.go b/marshallers.go index 7dad7c0..e68b7ca 100644 --- a/marshallers.go +++ b/marshallers.go @@ -20,7 +20,7 @@ func (u *URL) UnmarshalYAML(unmarshal func(any) error) error { return fmt.Errorf("parsing URL %q: %w", s, err) } - *u = (URL)(*urlp) + *u = URL(*urlp) return nil } diff --git a/parser.go b/parser.go index 31ac101..f470ef8 100644 --- a/parser.go +++ b/parser.go @@ -213,8 +213,7 @@ func (p *Parser) Parse(uri string) (PublicCode, error) { func (p *Parser) parseStream(in io.Reader, fileURL *url.URL) (PublicCode, error) { publiccode, err := p.parseAndValidate(in, fileURL) - var results ValidationResults - if errors.As(err, &results) { + if results, ok := errors.AsType[ValidationResults](err); ok { setFileName(results, fileNameFromURL(fileURL)) } @@ -284,8 +283,7 @@ func (p *Parser) parseAndValidate(in io.Reader, fileURL *url.URL) (PublicCode, e // detect structural issues (multi-document, empty file, syntax errors). file, err := parser.ParseBytes(b, 0) if err != nil { - var se *yaml.SyntaxError - if errors.As(err, &se) { + if se, ok := errors.AsType[*yaml.SyntaxError](err); ok { line := 0 if tok := se.GetToken(); tok != nil { line = tok.Position.Line @@ -401,8 +399,7 @@ func (p *Parser) parseAndValidate(in io.Reader, fileURL *url.URL) (PublicCode, e err = sharedValidate.Struct(publiccode) if err != nil { - var validationErrs validator.ValidationErrors - if errors.As(err, &validationErrs) { + if validationErrs, ok := errors.AsType[validator.ValidationErrors](err); ok { for _, err := range validationErrs { key := strings.SplitN(err.Namespace(), ".", 2)[1] key = reMapKey.ReplaceAllString(key, ".$1") @@ -472,8 +469,7 @@ func (p *Parser) parseAndValidate(in io.Reader, fileURL *url.URL) (PublicCode, e } if err = validateFields(publiccode, p, !p.disableNetwork, currentBaseURL); err != nil { - var vr ValidationResults - if errors.As(err, &vr) { + if vr, ok := errors.AsType[ValidationResults](err); ok { for _, result := range vr { var valErr ValidationError diff --git a/publiccode-parser/publiccode_parser.go b/publiccode-parser/publiccode_parser.go index 9768bdf..5293c40 100644 --- a/publiccode-parser/publiccode_parser.go +++ b/publiccode-parser/publiccode_parser.go @@ -159,11 +159,9 @@ func hasValidationErrors(results error) bool { return false } - var vr publiccode.ValidationResults - if errors.As(results, &vr) { + if vr, ok := errors.AsType[publiccode.ValidationResults](results); ok { for _, res := range vr { - var ve publiccode.ValidationError - if errors.As(res, &ve) { + if _, ok := errors.AsType[publiccode.ValidationError](res); ok { return true } } diff --git a/v0.go b/v0.go index 7faa958..efc5f58 100644 --- a/v0.go +++ b/v0.go @@ -56,18 +56,18 @@ type PublicCodeV0 struct { MainCopyrightOwner *string `json:"mainCopyrightOwner,omitempty" yaml:"mainCopyrightOwner,omitempty"` RepoOwner *string `json:"repoOwner,omitempty" yaml:"repoOwner,omitempty"` AuthorsFile *string `json:"authorsFile,omitempty" yaml:"authorsFile,omitempty"` - } `yaml:"legal" json:"legal"` + } `json:"legal" yaml:"legal"` Maintenance struct { Type string `json:"type" validate:"required,oneof=internal contract community none" yaml:"type"` Contractors *[]ContractorV0 `json:"contractors,omitempty" validate:"required_if=Type contract,excluded_unless=Type contract,omitempty,dive" yaml:"contractors,omitempty"` Contacts *[]ContactV0 `json:"contacts,omitempty" validate:"required_if=Type community,required_if=Type internal,omitempty,dive" yaml:"contacts,omitempty"` - } `yaml:"maintenance" json:"maintenance"` + } `json:"maintenance" yaml:"maintenance"` Localisation struct { LocalisationReady *bool `json:"localisationReady" validate:"required" yaml:"localisationReady"` AvailableLanguages []string `json:"availableLanguages" validate:"required,gt=0,dive,bcp47_strict_language_tag" yaml:"availableLanguages"` - } `yaml:"localisation" json:"localisation"` + } `json:"localisation" yaml:"localisation"` DependsOn *struct { Open *[]DependencyV0 `json:"open,omitempty" validate:"omitempty,dive" yaml:"open,omitempty"` @@ -151,7 +151,7 @@ type ITSectionV0 struct { Riuso struct { CodiceIPA string `json:"codiceIPA,omitempty" validate:"omitempty,is_italian_ipa_code" yaml:"codiceIPA,omitempty"` - } `yaml:"riuso,omitempty" json:"riuso,omitzero"` + } `json:"riuso,omitzero" yaml:"riuso,omitempty"` Piattaforme struct { SPID bool `json:"spid" yaml:"spid"` @@ -159,7 +159,7 @@ type ITSectionV0 struct { CIE bool `json:"cie" yaml:"cie"` ANPR bool `json:"anpr" yaml:"anpr"` Io bool `json:"io" yaml:"io"` - } `yaml:"piattaforme" json:"piattaforme"` + } `json:"piattaforme" yaml:"piattaforme"` } func (p PublicCodeV0) Version() uint { diff --git a/v1.go b/v1.go index af6b58f..ec8cd05 100644 --- a/v1.go +++ b/v1.go @@ -48,18 +48,18 @@ type PublicCodeV1 struct { Legal struct { License string `json:"license" validate:"required,is_spdx_expression" yaml:"license"` MainCopyrightOwner *string `json:"mainCopyrightOwner,omitempty" yaml:"mainCopyrightOwner,omitempty"` - } `yaml:"legal" json:"legal"` + } `json:"legal" yaml:"legal"` Maintenance struct { Type string `json:"type" validate:"required,oneof=internal contract community none" yaml:"type"` Contractors *[]ContractorV1 `json:"contractors,omitempty" validate:"required_if=Type contract,excluded_unless=Type contract,omitempty,dive" yaml:"contractors,omitempty"` Contacts *[]ContactV1 `json:"contacts,omitempty" validate:"required_if=Type community,required_if=Type internal,omitempty,dive" yaml:"contacts,omitempty"` - } `yaml:"maintenance" json:"maintenance"` + } `json:"maintenance" yaml:"maintenance"` Localisation struct { LocalisationReady *bool `json:"localisationReady" validate:"required" yaml:"localisationReady"` AvailableLanguages []string `json:"availableLanguages" validate:"required,gt=0,dive,bcp47_strict_language_tag" yaml:"availableLanguages"` - } `yaml:"localisation" json:"localisation"` + } `json:"localisation" yaml:"localisation"` DependsOn *struct { Open *[]DependencyV1 `json:"open,omitempty" validate:"omitempty,dive" yaml:"open,omitempty"` diff --git a/validations.go b/validations.go index fd2b3d0..0ceb024 100644 --- a/validations.go +++ b/validations.go @@ -140,7 +140,7 @@ func (p *Parser) fileExists(u url.URL, network bool) (bool, error) { // If we have an absolute local path, perform validation on it, otherwise do it // on the remote URL if any. If none are available, validation is skipped. if u.Scheme == "file" { - _, err := os.Stat(u.Path) //nolint:gosec // G703: path is from a validated file:// URL + _, err := os.Stat(u.Path) if err != nil { err = fmt.Errorf("no such file: %s", netutil.DisplayURL(&u)) //nolint:err113 // dynamic message with path context } diff --git a/validators/common.go b/validators/common.go index 3af7284..a383644 100644 --- a/validators/common.go +++ b/validators/common.go @@ -55,7 +55,7 @@ func uMin(fl validator.FieldLevel) bool { func isHTTPURL(fl validator.FieldLevel) bool { validate := sharedValidator - if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok { + if stringer, ok := reflect.TypeAssert[fmt.Stringer](fl.Field()); ok { err := validate.Var(stringer.String(), "http_url") return err == nil @@ -70,7 +70,7 @@ func isHTTPURL(fl validator.FieldLevel) bool { func isURL(fl validator.FieldLevel) bool { validate := sharedValidator - if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok { + if stringer, ok := reflect.TypeAssert[fmt.Stringer](fl.Field()); ok { err := validate.Var(stringer.String(), "url") return err == nil diff --git a/validators/validator.go b/validators/validator.go index 80d04ed..8009c19 100644 --- a/validators/validator.go +++ b/validators/validator.go @@ -32,7 +32,7 @@ func New() *validator.Validate { validate.RegisterAlias("date", "datetime=2006-01-02") validate.RegisterTagNameFunc(func(fld reflect.StructField) string { - name := strings.SplitN(fld.Tag.Get("yaml"), ",", 2)[0] + name, _, _ := strings.Cut(fld.Tag.Get("yaml"), ",") if name == "-" { return "" }