11package publiccode
22
33import (
4+ "bufio"
45 "bytes"
56 "context"
67 "errors"
@@ -30,21 +31,37 @@ import (
3031 publiccodeValidator "github.com/italia/publiccode-parser-go/v5/validators"
3132)
3233
33- // Build Validator and Translator once at package init.
34- var (
35- sharedValidate * validator.Validate
36- sharedTrans ut.Translator
37- )
34+ // fetchIPACodes downloads the IPA codes list from the given URL and returns it
35+ // as a set. The format is expected to match the Agid export: one code per line.
36+ func fetchIPACodes (client * http.Client , rawURL string ) (map [string ]struct {}, error ) {
37+ req , err := http .NewRequestWithContext (context .Background (), http .MethodGet , rawURL , nil )
38+ if err != nil {
39+ return nil , fmt .Errorf ("building IPA codes request for %q: %w" , rawURL , err )
40+ }
3841
39- func init () {
40- sharedValidate = publiccodeValidator .New ()
42+ resp , err := client .Do (req )
43+ if err != nil {
44+ return nil , fmt .Errorf ("fetching IPA codes from %q: %w" , rawURL , err )
45+ }
4146
42- enLocale := en .New ()
43- uni := ut .New (enLocale , enLocale )
47+ defer resp .Body .Close ()
48+
49+ if resp .StatusCode != http .StatusOK {
50+ return nil , fmt .Errorf ("fetching IPA codes from %q: unexpected HTTP status %d" , rawURL , resp .StatusCode ) //nolint:err113,lll // dynamic status code
51+ }
52+
53+ codes := make (map [string ]struct {}, 24000 )
54+ scanner := bufio .NewScanner (resp .Body )
55+
56+ for scanner .Scan () {
57+ codes [strings .ToLower (scanner .Text ())] = struct {}{}
58+ }
59+
60+ if err := scanner .Err (); err != nil {
61+ return nil , fmt .Errorf ("reading IPA codes from %q: %w" , rawURL , err )
62+ }
4463
45- sharedTrans , _ = uni .GetTranslator ("en" )
46- _ = en_translations .RegisterDefaultTranslations (sharedValidate , sharedTrans )
47- _ = publiccodeValidator .RegisterLocalErrorMessages (sharedValidate , sharedTrans )
64+ return codes , nil
4865}
4966
5067var reMapKey = regexp .MustCompile (`\[([[:alpha:]]+)\]` )
@@ -75,6 +92,15 @@ type ParserConfig struct {
7592 // Timeout is the maximum duration for each HTTP request during external checks.
7693 // Defaults to 30s if zero.
7794 Timeout time.Duration
95+
96+ // IPACodesURL, if set, causes the parser to fetch a fresh list of Italian
97+ // Public Administration codes from this URL at creation time, instead of
98+ // using the embedded snapshot. The expected format matches the Agid export:
99+ // one code per line (https://www.indicepa.gov.it).
100+ //
101+ // Leave empty (default) to use the embedded snapshot, which is updated
102+ // periodically via the repo's automated workflow.
103+ IPACodesURL string
78104}
79105
80106const defaultHTTPTimeout = 30 * time .Second
@@ -88,6 +114,8 @@ type Parser struct {
88114 baseURL * url.URL
89115 client * http.Client
90116 httpclient * httpclient.Client
117+ validate * validator.Validate
118+ trans ut.Translator
91119}
92120
93121// Domain is a single code hosting service.
@@ -112,13 +140,34 @@ func NewParser(config ParserConfig) (*Parser, error) {
112140
113141 httpClient := & http.Client {Timeout : timeout }
114142 vcsurl .Client = httpClient
143+
144+ ipaCodes := publiccodeValidator .DefaultIPACodes ()
145+
146+ if config .IPACodesURL != "" {
147+ var err error
148+ if ipaCodes , err = fetchIPACodes (httpClient , config .IPACodesURL ); err != nil {
149+ return nil , err
150+ }
151+ }
152+
153+ validate := publiccodeValidator .New (ipaCodes )
154+
155+ enLocale := en .New ()
156+ uni := ut .New (enLocale , enLocale )
157+
158+ trans , _ := uni .GetTranslator ("en" )
159+ _ = en_translations .RegisterDefaultTranslations (validate , trans )
160+ _ = publiccodeValidator .RegisterLocalErrorMessages (validate , trans )
161+
115162 p := Parser {
116163 disableNetwork : config .DisableNetwork ,
117164 disableExternalChecks : config .DisableExternalChecks ,
118165 domain : config .Domain ,
119166 branch : config .Branch ,
120167 client : httpClient ,
121168 httpclient : httpclient .NewClient (httpClient ),
169+ validate : validate ,
170+ trans : trans ,
122171 }
123172
124173 if config .BaseURL != "" {
@@ -289,7 +338,7 @@ func (p *Parser) parseStream(in io.Reader, fileURL *url.URL) (PublicCode, error)
289338 ve = append (ve , decodeResults ... )
290339 }
291340
292- err = sharedValidate .Struct (publiccode )
341+ err = p . validate .Struct (publiccode )
293342 if err != nil {
294343 var validationErrs validator.ValidationErrors
295344 if errors .As (err , & validationErrs ) {
@@ -301,7 +350,7 @@ func (p *Parser) parseStream(in io.Reader, fileURL *url.URL) (PublicCode, error)
301350
302351 ve = append (ve , ValidationError {
303352 Key : key ,
304- Description : err .Translate (sharedTrans ),
353+ Description : err .Translate (p . trans ),
305354 Line : line ,
306355 Column : column ,
307356 })
0 commit comments