Skip to content

Commit 4644084

Browse files
committed
wip
1 parent 373b7f4 commit 4644084

File tree

8 files changed

+168
-176
lines changed

8 files changed

+168
-176
lines changed

fields.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
spdxValidator "github.com/kyoh86/go-spdx/spdx"
99
"github.com/alranel/go-vcsurl/v2"
1010

11-
urlutil "github.com/italia/publiccode-parser-go/v3/internal"
11+
urlutil "github.com/italia/publiccode-parser-go/v4/internal"
1212
)
1313

1414
// validateFields validates publiccode with additional rules not validatable

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module github.com/italia/publiccode-parser-go/v3
1+
module github.com/italia/publiccode-parser-go/v4
22

33
require (
44
github.com/alranel/go-vcsurl/v2 v2.0.2

parser.go

+154-150
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,36 @@ import (
1818
"github.com/go-playground/validator/v10"
1919
"github.com/alranel/go-vcsurl/v2"
2020

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"
2323
)
2424

25-
// Parser is a helper class for parsing publiccode.yml files.
26-
type Parser struct {
27-
PublicCode PublicCode
28-
25+
type ParserConfig struct {
2926
// DisableNetwork disables all network tests (URL existence and Oembed). This
3027
// results in much faster parsing.
31-
DisableNetwork bool
28+
DisableNetwork bool
3229

3330
// Domain will have domain specific settings, including basic auth if provided
3431
// this will avoid strong quota limit imposed by code hosting platform
35-
Domain Domain
32+
Domain Domain
3633

3734
// The name of the branch used to check for existence of the files referenced
3835
// in the publiccode.yml
39-
Branch string
36+
Branch string
4037

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
4447

45-
// The URL pointing to the publiccode.yml file.
48+
// The URL used as base of relative files in publiccode.yml (eg. authorsFile)
4649
// It can be a local file with the 'file' scheme.
47-
file *url.URL
50+
baseURL *url.URL
4851
}
4952

5053
// Domain is a single code hosting service.
@@ -56,132 +59,55 @@ type Domain struct {
5659
}
5760

5861
// 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+
// }
8368

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+
// }
9571

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,
10280
}
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
10981

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
13685
}
13786
}
13887

139-
return ""
88+
return &parser, nil
14089
}
14190

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
16294

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
16899
}
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
174100

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"))
177103
return ve
178104
}
179105

180106
// First, decode the YAML into yaml.Node so we can access line and column
181107
// numbers.
182108
var node yaml.Node
183109

184-
d := yaml.NewDecoder(bytes.NewReader(in))
110+
d := yaml.NewDecoder(in)
185111
d.KnownFields(true)
186112
err := d.Decode(&node)
187113

@@ -323,58 +249,136 @@ func (p *Parser) ParseBytes(in []byte) error {
323249
return ve
324250
}
325251

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+
}
329258

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)
332261
if err != nil {
333262
return err
334263
}
335264
} else {
336-
resp, err := http.Get(p.file.String())
265+
resp, err := http.Get(uri)
337266
if err != nil {
338267
return err
339268
}
340269
defer resp.Body.Close()
341270

342-
data, err = io.ReadAll(resp.Body)
343-
if err != nil {
344-
return err
345-
}
271+
stream = resp.Body
346272
}
347273

348-
return p.ParseBytes(data)
274+
return p.ParseStream(data)
349275
}
350276

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)
355280
}
356281

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]
361285

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+
}
365289
}
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
369304
}
370305
}
371306

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+
}
373316
}
374317

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+
}
378382
}
379383

380384
// TODO doc

0 commit comments

Comments
 (0)