-
Notifications
You must be signed in to change notification settings - Fork 347
/
csv.go
129 lines (102 loc) · 2.6 KB
/
csv.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package lockfile
import (
"encoding/csv"
"errors"
"fmt"
"io"
"sort"
"strings"
)
var errCSVRecordNotEnoughFields = errors.New("not enough fields (expected at least four)")
var errCSVRecordMissingPackageField = errors.New("field 3 is empty (must be the name of a package)")
var errCSVRecordMissingCommitField = errors.New("field 4 is empty (must be a commit)")
func fromCSVRecord(lines []string) (PackageDetails, error) {
if len(lines) < 4 {
return PackageDetails{}, errCSVRecordNotEnoughFields
}
ecosystem := Ecosystem(lines[0])
compareAs := Ecosystem(lines[1])
name := lines[2]
version := lines[3]
commit := ""
if compareAs == "" {
compareAs = ecosystem
}
if ecosystem == "" {
if version == "" {
return PackageDetails{}, errCSVRecordMissingCommitField
}
commit = version
version = ""
}
if name == "" {
return PackageDetails{}, errCSVRecordMissingPackageField
}
return PackageDetails{
Name: name,
Version: version,
Ecosystem: ecosystem,
CompareAs: compareAs,
Commit: commit,
}, nil
}
func fromCSV(reader io.Reader) ([]PackageDetails, error) {
var packages []PackageDetails
i := 0
r := csv.NewReader(reader)
for {
i++
record, err := r.Read()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return packages, fmt.Errorf("%w", err)
}
details, err := fromCSVRecord(record)
if err != nil {
return packages, fmt.Errorf("row %d: %w", i, err)
}
packages = append(packages, details)
}
sort.Slice(packages, func(i, j int) bool {
if packages[i].Name == packages[j].Name {
return packages[i].Version < packages[j].Version
}
return packages[i].Name < packages[j].Name
})
return packages, nil
}
type CSVExtractor struct{}
func (e CSVExtractor) ShouldExtract(_ string) bool {
// the csv extractor should never implicitly extract a file
return false
}
func (e CSVExtractor) Extract(f DepFile) ([]PackageDetails, error) {
return fromCSV(f)
}
var _ Extractor = CSVExtractor{}
func FromCSVRows(filePath string, parseAs string, rows []string) (Lockfile, error) {
packages, err := fromCSV(strings.NewReader(strings.Join(rows, "\n")))
return Lockfile{
FilePath: filePath,
ParsedAs: parseAs,
Packages: packages,
}, err
}
func FromCSVFile(pathToCSV string, parseAs string) (Lockfile, error) {
file, err := OpenLocalDepFile(pathToCSV)
if err != nil {
return Lockfile{}, fmt.Errorf("could not read %s: %w", pathToCSV, err)
}
defer file.Close()
packages, err := fromCSV(file)
if err != nil {
err = fmt.Errorf("%s: %w", pathToCSV, err)
}
return Lockfile{
FilePath: pathToCSV,
ParsedAs: parseAs,
Packages: packages,
}, err
}