@@ -18,33 +18,36 @@ import (
18
18
"github.com/go-playground/validator/v10"
19
19
"github.com/alranel/go-vcsurl/v2"
20
20
21
- publiccodeValidator "github.com/italia/publiccode-parser-go/v3 /validators"
22
- urlutil "github.com/italia/publiccode-parser-go/v3 /internal"
21
+ publiccodeValidator "github.com/italia/publiccode-parser-go/v4 /validators"
22
+ urlutil "github.com/italia/publiccode-parser-go/v4 /internal"
23
23
)
24
24
25
- // Parser is a helper class for parsing publiccode.yml files.
26
- type Parser struct {
27
- PublicCode PublicCode
28
-
25
+ type ParserConfig struct {
29
26
// DisableNetwork disables all network tests (URL existence and Oembed). This
30
27
// results in much faster parsing.
31
- DisableNetwork bool
28
+ DisableNetwork bool
32
29
33
30
// Domain will have domain specific settings, including basic auth if provided
34
31
// this will avoid strong quota limit imposed by code hosting platform
35
- Domain Domain
32
+ Domain Domain
36
33
37
34
// The name of the branch used to check for existence of the files referenced
38
35
// in the publiccode.yml
39
- Branch string
36
+ Branch string
40
37
41
- // The URL used as based of relative files in publiccode.yml (eg. authorsFile)
42
- // It can be a local file with the 'file' scheme.
43
- baseURL * url.URL
38
+ // TODO: doc
39
+ BaseURL string
40
+ }
41
+
42
+ // Parser is a helper class for parsing publiccode.yml files.
43
+ type Parser struct {
44
+ disableNetwork bool
45
+ domain Domain
46
+ branch string
44
47
45
- // The URL pointing to the publiccode.yml file.
48
+ // The URL used as base of relative files in publiccode.yml (eg. authorsFile)
46
49
// It can be a local file with the 'file' scheme.
47
- file * url.URL
50
+ baseURL * url.URL
48
51
}
49
52
50
53
// Domain is a single code hosting service.
@@ -56,132 +59,55 @@ type Domain struct {
56
59
}
57
60
58
61
// ParseInDomain wrapper func to be in domain env
59
- func (p * Parser ) ParseInDomain (in []byte , host string , utf []string , ba []string ) error {
60
- p .Domain = Domain {
61
- Host : host ,
62
- UseTokenFor : utf ,
63
- BasicAuth : ba ,
64
- }
65
-
66
- return p .ParseBytes (in )
67
- }
68
-
69
- func getNodes (key string , node * yaml.Node ) (* yaml.Node , * yaml.Node ) {
70
- for i := 0 ; i < len (node .Content ); i += 2 {
71
- childNode := * node .Content [i ]
72
-
73
- if childNode .Value == key {
74
- return & childNode , node .Content [i + 1 ]
75
- }
76
- }
77
-
78
- return nil , nil
79
- }
80
-
81
- func getPositionInFile (key string , node yaml.Node ) (int , int ) {
82
- var n * yaml.Node = & node
62
+ // func (p *Parser) ParseInDomain(in []byte, host string, utf []string, ba []string) error {
63
+ // p.Domain = Domain{
64
+ // Host: host,
65
+ // UseTokenFor: utf,
66
+ // BasicAuth: ba,
67
+ // }
83
68
84
- keys := strings .Split (key , "." )
85
- for _ , path := range keys [:len (keys ) - 1 ] {
86
- _ , n = getNodes (path , n )
87
-
88
- // This should not happen, but let's be defensive
89
- if (n == nil ) {
90
- return 0 , 0
91
- }
92
- }
93
-
94
- parentNode := n
69
+ // return p.ParseBytes(in)
70
+ // }
95
71
96
- n , _ = getNodes (keys [len (keys ) - 1 ], n )
97
-
98
- if (n != nil ) {
99
- return n .Line , n .Column
100
- } else {
101
- return parentNode .Line , parentNode .Column
72
+ // NewParser initializes a new Parser object and returns it.
73
+ // TODO
74
+ func NewParser (config ParserConfig ) (* Parser , error ) {
75
+ // func NewParserWithPath(file string, path string) (*Parser, error) {
76
+ parser := Parser {
77
+ disableNetwork : config .DisableNetwork ,
78
+ domain : config .Domain ,
79
+ branch : config .Branch ,
102
80
}
103
- }
104
-
105
- // getKeyAtLine returns the key name at line "line" for the YAML document
106
- // represented at parentNode.
107
- func getKeyAtLine (parentNode yaml.Node , line int , path string ) string {
108
- var key = path
109
81
110
- for i , currNode := range parentNode .Content {
111
- // If this node is a mapping and the index is odd it means
112
- // we are not looking at a key, but at its value. Skip it.
113
- if parentNode .Kind == yaml .MappingNode && i % 2 != 0 && currNode .Kind == yaml .ScalarNode {
114
- continue
115
- }
116
-
117
- // This node is a key of a mapping type
118
- if parentNode .Kind == yaml .MappingNode && i % 2 == 0 {
119
- if path == "" {
120
- key = currNode .Value
121
- } else {
122
- key = fmt .Sprintf ("%s.%s" , path , currNode .Value )
123
- }
124
- }
125
-
126
- // We want the scalar node (ie. key) not the mapping node which
127
- // doesn't have a tag name even if it has the same line number
128
- if currNode .Line == line && parentNode .Kind == yaml .MappingNode && currNode .Kind == yaml .ScalarNode {
129
- return key
130
- }
131
-
132
- if currNode .Kind != yaml .ScalarNode {
133
- if k := getKeyAtLine (* currNode , line , key ); k != "" {
134
- return k
135
- }
82
+ if config .BaseURL != "" {
83
+ if p .baseURL , err := toURL (config .BaseURL ); err != nil {
84
+ return nil , err
136
85
}
137
86
}
138
87
139
- return ""
88
+ return & parser , nil
140
89
}
141
90
142
- func toValidationError (errorText string , node * yaml.Node ) ValidationError {
143
- r := regexp .MustCompile (`(line ([0-9]+): )` )
144
- matches := r .FindStringSubmatch (errorText )
145
-
146
- line := 0
147
- if (len (matches ) > 1 ) {
148
- line , _ = strconv .Atoi (matches [2 ])
149
- errorText = strings .ReplaceAll (errorText , matches [1 ], "" )
150
- }
151
-
152
- // Transform unmarshalling errors messages to a user friendlier message
153
- r = regexp .MustCompile ("^cannot unmarshal" )
154
- if r .MatchString (errorText ) {
155
- errorText = "wrong type for this field"
156
- }
157
-
158
- var key string
159
- if node != nil {
160
- key = getKeyAtLine (* node , line , "" )
161
- }
91
+ // ParseStreams reads the data and tries to parse it. Returns an error if fails.
92
+ func (p * Parser ) ParseStream (in io.Reader ) (PublicCode , error ) {
93
+ var ve ValidationResults
162
94
163
- return ValidationError {
164
- Key : key ,
165
- Description : errorText ,
166
- Line : line ,
167
- Column : 1 ,
95
+ bytes , err := io .ReadAll (file )
96
+ if err != nil {
97
+ ve = append (ve , newValidationError ("" , fmt .Sprintf ("Can't read the stream: %v" , err )))
98
+ return ve
168
99
}
169
- }
170
-
171
- // ParseBytes loads the yaml bytes and tries to parse it. Return an error if fails.
172
- func (p * Parser ) ParseBytes (in []byte ) error {
173
- var ve ValidationResults
174
100
175
- if ! utf8 .Valid (in ) {
176
- ve = append (ve ,newValidationError ("" , "Invalid UTF-8" ))
101
+ if ! utf8 .Valid (bytes ) {
102
+ ve = append (ve , newValidationError ("" , "Invalid UTF-8" ))
177
103
return ve
178
104
}
179
105
180
106
// First, decode the YAML into yaml.Node so we can access line and column
181
107
// numbers.
182
108
var node yaml.Node
183
109
184
- d := yaml .NewDecoder (bytes . NewReader ( in ) )
110
+ d := yaml .NewDecoder (in )
185
111
d .KnownFields (true )
186
112
err := d .Decode (& node )
187
113
@@ -323,58 +249,136 @@ func (p *Parser) ParseBytes(in []byte) error {
323
249
return ve
324
250
}
325
251
326
- func (p * Parser ) Parse () error {
327
- var data []byte
328
- var err error
252
+ func (p * Parser ) Parse (uri string ) (PublicCode , error ) {
253
+ var stream io.Reader
254
+
255
+ if u , err = toURL (file ); err != nil {
256
+ return nil , err
257
+ }
329
258
330
- if p . file .Scheme == "file" {
331
- data , err = os .ReadFile ( p . file .Path )
259
+ if u .Scheme == "file" {
260
+ stream , err : = os .Open ( u .Path )
332
261
if err != nil {
333
262
return err
334
263
}
335
264
} else {
336
- resp , err := http .Get (p . file . String () )
265
+ resp , err := http .Get (uri )
337
266
if err != nil {
338
267
return err
339
268
}
340
269
defer resp .Body .Close ()
341
270
342
- data , err = io .ReadAll (resp .Body )
343
- if err != nil {
344
- return err
345
- }
271
+ stream = resp .Body
346
272
}
347
273
348
- return p .ParseBytes (data )
274
+ return p .ParseStream (data )
349
275
}
350
276
351
- // NewParser initializes a new Parser object and returns it.
352
- // TODO
353
- func NewParser (file string ) (* Parser , error ) {
354
- return NewParserWithPath (file , "" )
277
+ // ToYAML converts parser.PublicCode into YAML again.
278
+ func (p * Parser ) ToYAML () ([]byte , error ) {
279
+ return yaml .Marshal (p .PublicCode )
355
280
}
356
281
357
- // TODO doc
358
- // empty string disables it and enables remote
359
- func NewParserWithPath (file string , path string ) (* Parser , error ) {
360
- var p Parser
282
+ func getNodes (key string , node * yaml.Node ) (* yaml.Node , * yaml.Node ) {
283
+ for i := 0 ; i < len (node .Content ); i += 2 {
284
+ childNode := * node .Content [i ]
361
285
362
- var err error
363
- if p . file , err = toURL ( file ); err != nil {
364
- return nil , err
286
+ if childNode . Value == key {
287
+ return & childNode , node . Content [ i + 1 ]
288
+ }
365
289
}
366
- if path != "" {
367
- if p .baseURL , err = toURL (path ); err != nil {
368
- return nil , err
290
+
291
+ return nil , nil
292
+ }
293
+
294
+ func getPositionInFile (key string , node yaml.Node ) (int , int ) {
295
+ var n * yaml.Node = & node
296
+
297
+ keys := strings .Split (key , "." )
298
+ for _ , path := range keys [:len (keys ) - 1 ] {
299
+ _ , n = getNodes (path , n )
300
+
301
+ // This should not happen, but let's be defensive
302
+ if (n == nil ) {
303
+ return 0 , 0
369
304
}
370
305
}
371
306
372
- return & p , nil
307
+ parentNode := n
308
+
309
+ n , _ = getNodes (keys [len (keys ) - 1 ], n )
310
+
311
+ if (n != nil ) {
312
+ return n .Line , n .Column
313
+ } else {
314
+ return parentNode .Line , parentNode .Column
315
+ }
373
316
}
374
317
375
- // ToYAML converts parser.PublicCode into YAML again.
376
- func (p * Parser ) ToYAML () ([]byte , error ) {
377
- return yaml .Marshal (p .PublicCode )
318
+ // getKeyAtLine returns the key name at line "line" for the YAML document
319
+ // represented at parentNode.
320
+ func getKeyAtLine (parentNode yaml.Node , line int , path string ) string {
321
+ var key = path
322
+
323
+ for i , currNode := range parentNode .Content {
324
+ // If this node is a mapping and the index is odd it means
325
+ // we are not looking at a key, but at its value. Skip it.
326
+ if parentNode .Kind == yaml .MappingNode && i % 2 != 0 && currNode .Kind == yaml .ScalarNode {
327
+ continue
328
+ }
329
+
330
+ // This node is a key of a mapping type
331
+ if parentNode .Kind == yaml .MappingNode && i % 2 == 0 {
332
+ if path == "" {
333
+ key = currNode .Value
334
+ } else {
335
+ key = fmt .Sprintf ("%s.%s" , path , currNode .Value )
336
+ }
337
+ }
338
+
339
+ // We want the scalar node (ie. key) not the mapping node which
340
+ // doesn't have a tag name even if it has the same line number
341
+ if currNode .Line == line && parentNode .Kind == yaml .MappingNode && currNode .Kind == yaml .ScalarNode {
342
+ return key
343
+ }
344
+
345
+ if currNode .Kind != yaml .ScalarNode {
346
+ if k := getKeyAtLine (* currNode , line , key ); k != "" {
347
+ return k
348
+ }
349
+ }
350
+ }
351
+
352
+ return ""
353
+ }
354
+
355
+ func toValidationError (errorText string , node * yaml.Node ) ValidationError {
356
+ r := regexp .MustCompile (`(line ([0-9]+): )` )
357
+ matches := r .FindStringSubmatch (errorText )
358
+
359
+ line := 0
360
+ if (len (matches ) > 1 ) {
361
+ line , _ = strconv .Atoi (matches [2 ])
362
+ errorText = strings .ReplaceAll (errorText , matches [1 ], "" )
363
+ }
364
+
365
+ // Transform unmarshalling errors messages to a user friendlier message
366
+ r = regexp .MustCompile ("^cannot unmarshal" )
367
+ if r .MatchString (errorText ) {
368
+ errorText = "wrong type for this field"
369
+ }
370
+
371
+ var key string
372
+ if node != nil {
373
+ key = getKeyAtLine (* node , line , "" )
374
+ }
375
+
376
+ return ValidationError {
377
+ Key : key ,
378
+ Description : errorText ,
379
+ Line : line ,
380
+ Column : 1 ,
381
+ }
378
382
}
379
383
380
384
// TODO doc
0 commit comments