1
2
3
4
5 package modfile
6
7 import (
8 "fmt"
9 "sort"
10 "strings"
11 )
12
13
14 type WorkFile struct {
15 Go *Go
16 Use []*Use
17 Replace []*Replace
18
19 Syntax *FileSyntax
20 }
21
22
23 type Use struct {
24 Path string
25 ModulePath string
26 Syntax *Line
27 }
28
29
30
31
32
33
34
35
36
37
38 func ParseWork(file string, data []byte, fix VersionFixer) (*WorkFile, error) {
39 fs, err := parse(file, data)
40 if err != nil {
41 return nil, err
42 }
43 f := &WorkFile{
44 Syntax: fs,
45 }
46 var errs ErrorList
47
48 for _, x := range fs.Stmt {
49 switch x := x.(type) {
50 case *Line:
51 f.add(&errs, x, x.Token[0], x.Token[1:], fix)
52
53 case *LineBlock:
54 if len(x.Token) > 1 {
55 errs = append(errs, Error{
56 Filename: file,
57 Pos: x.Start,
58 Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
59 })
60 continue
61 }
62 switch x.Token[0] {
63 default:
64 errs = append(errs, Error{
65 Filename: file,
66 Pos: x.Start,
67 Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
68 })
69 continue
70 case "use", "replace":
71 for _, l := range x.Line {
72 f.add(&errs, l, x.Token[0], l.Token, fix)
73 }
74 }
75 }
76 }
77
78 if len(errs) > 0 {
79 return nil, errs
80 }
81 return f, nil
82 }
83
84
85
86
87
88 func (f *WorkFile) Cleanup() {
89 w := 0
90 for _, r := range f.Use {
91 if r.Path != "" {
92 f.Use[w] = r
93 w++
94 }
95 }
96 f.Use = f.Use[:w]
97
98 w = 0
99 for _, r := range f.Replace {
100 if r.Old.Path != "" {
101 f.Replace[w] = r
102 w++
103 }
104 }
105 f.Replace = f.Replace[:w]
106
107 f.Syntax.Cleanup()
108 }
109
110 func (f *WorkFile) AddGoStmt(version string) error {
111 if !GoVersionRE.MatchString(version) {
112 return fmt.Errorf("invalid language version string %q", version)
113 }
114 if f.Go == nil {
115 stmt := &Line{Token: []string{"go", version}}
116 f.Go = &Go{
117 Version: version,
118 Syntax: stmt,
119 }
120
121
122 i := 0
123 for i = 0; i < len(f.Syntax.Stmt); i++ {
124 if _, ok := f.Syntax.Stmt[i].(*CommentBlock); !ok {
125 break
126 }
127 }
128 f.Syntax.Stmt = append(append(f.Syntax.Stmt[:i:i], stmt), f.Syntax.Stmt[i:]...)
129 } else {
130 f.Go.Version = version
131 f.Syntax.updateLine(f.Go.Syntax, "go", version)
132 }
133 return nil
134 }
135
136 func (f *WorkFile) AddUse(diskPath, modulePath string) error {
137 need := true
138 for _, d := range f.Use {
139 if d.Path == diskPath {
140 if need {
141 d.ModulePath = modulePath
142 f.Syntax.updateLine(d.Syntax, "use", AutoQuote(diskPath))
143 need = false
144 } else {
145 d.Syntax.markRemoved()
146 *d = Use{}
147 }
148 }
149 }
150
151 if need {
152 f.AddNewUse(diskPath, modulePath)
153 }
154 return nil
155 }
156
157 func (f *WorkFile) AddNewUse(diskPath, modulePath string) {
158 line := f.Syntax.addLine(nil, "use", AutoQuote(diskPath))
159 f.Use = append(f.Use, &Use{Path: diskPath, ModulePath: modulePath, Syntax: line})
160 }
161
162 func (f *WorkFile) SetUse(dirs []*Use) {
163 need := make(map[string]string)
164 for _, d := range dirs {
165 need[d.Path] = d.ModulePath
166 }
167
168 for _, d := range f.Use {
169 if modulePath, ok := need[d.Path]; ok {
170 d.ModulePath = modulePath
171 } else {
172 d.Syntax.markRemoved()
173 *d = Use{}
174 }
175 }
176
177
178
179 for diskPath, modulePath := range need {
180 f.AddNewUse(diskPath, modulePath)
181 }
182 f.SortBlocks()
183 }
184
185 func (f *WorkFile) DropUse(path string) error {
186 for _, d := range f.Use {
187 if d.Path == path {
188 d.Syntax.markRemoved()
189 *d = Use{}
190 }
191 }
192 return nil
193 }
194
195 func (f *WorkFile) AddReplace(oldPath, oldVers, newPath, newVers string) error {
196 return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers)
197 }
198
199 func (f *WorkFile) DropReplace(oldPath, oldVers string) error {
200 for _, r := range f.Replace {
201 if r.Old.Path == oldPath && r.Old.Version == oldVers {
202 r.Syntax.markRemoved()
203 *r = Replace{}
204 }
205 }
206 return nil
207 }
208
209 func (f *WorkFile) SortBlocks() {
210 f.removeDups()
211
212 for _, stmt := range f.Syntax.Stmt {
213 block, ok := stmt.(*LineBlock)
214 if !ok {
215 continue
216 }
217 sort.SliceStable(block.Line, func(i, j int) bool {
218 return lineLess(block.Line[i], block.Line[j])
219 })
220 }
221 }
222
223
224
225
226
227
228
229
230
231
232 func (f *WorkFile) removeDups() {
233 removeDups(f.Syntax, nil, &f.Replace)
234 }
235
View as plain text