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 Toolchain *Toolchain
17 Godebug []*Godebug
18 Use []*Use
19 Replace []*Replace
20
21 Syntax *FileSyntax
22 }
23
24
25 type Use struct {
26 Path string
27 ModulePath string
28 Syntax *Line
29 }
30
31
32
33
34
35
36
37
38
39
40 func ParseWork(file string, data []byte, fix VersionFixer) (*WorkFile, error) {
41 fs, err := parse(file, data)
42 if err != nil {
43 return nil, err
44 }
45 f := &WorkFile{
46 Syntax: fs,
47 }
48 var errs ErrorList
49
50 for _, x := range fs.Stmt {
51 switch x := x.(type) {
52 case *Line:
53 f.add(&errs, x, x.Token[0], x.Token[1:], fix)
54
55 case *LineBlock:
56 if len(x.Token) > 1 {
57 errs = append(errs, Error{
58 Filename: file,
59 Pos: x.Start,
60 Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
61 })
62 continue
63 }
64 switch x.Token[0] {
65 default:
66 errs = append(errs, Error{
67 Filename: file,
68 Pos: x.Start,
69 Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
70 })
71 continue
72 case "godebug", "use", "replace":
73 for _, l := range x.Line {
74 f.add(&errs, l, x.Token[0], l.Token, fix)
75 }
76 }
77 }
78 }
79
80 if len(errs) > 0 {
81 return nil, errs
82 }
83 return f, nil
84 }
85
86
87
88
89
90 func (f *WorkFile) Cleanup() {
91 w := 0
92 for _, r := range f.Use {
93 if r.Path != "" {
94 f.Use[w] = r
95 w++
96 }
97 }
98 f.Use = f.Use[:w]
99
100 w = 0
101 for _, r := range f.Replace {
102 if r.Old.Path != "" {
103 f.Replace[w] = r
104 w++
105 }
106 }
107 f.Replace = f.Replace[:w]
108
109 f.Syntax.Cleanup()
110 }
111
112 func (f *WorkFile) AddGoStmt(version string) error {
113 if !GoVersionRE.MatchString(version) {
114 return fmt.Errorf("invalid language version %q", version)
115 }
116 if f.Go == nil {
117 stmt := &Line{Token: []string{"go", version}}
118 f.Go = &Go{
119 Version: version,
120 Syntax: stmt,
121 }
122
123
124 i := 0
125 for i = 0; i < len(f.Syntax.Stmt); i++ {
126 if _, ok := f.Syntax.Stmt[i].(*CommentBlock); !ok {
127 break
128 }
129 }
130 f.Syntax.Stmt = append(append(f.Syntax.Stmt[:i:i], stmt), f.Syntax.Stmt[i:]...)
131 } else {
132 f.Go.Version = version
133 f.Syntax.updateLine(f.Go.Syntax, "go", version)
134 }
135 return nil
136 }
137
138 func (f *WorkFile) AddToolchainStmt(name string) error {
139 if !ToolchainRE.MatchString(name) {
140 return fmt.Errorf("invalid toolchain name %q", name)
141 }
142 if f.Toolchain == nil {
143 stmt := &Line{Token: []string{"toolchain", name}}
144 f.Toolchain = &Toolchain{
145 Name: name,
146 Syntax: stmt,
147 }
148
149
150
151 i := 0
152 for i = 0; i < len(f.Syntax.Stmt); i++ {
153 if line, ok := f.Syntax.Stmt[i].(*Line); ok && len(line.Token) > 0 && line.Token[0] == "go" {
154 i++
155 goto Found
156 }
157 }
158 for i = 0; i < len(f.Syntax.Stmt); i++ {
159 if _, ok := f.Syntax.Stmt[i].(*CommentBlock); !ok {
160 break
161 }
162 }
163 Found:
164 f.Syntax.Stmt = append(append(f.Syntax.Stmt[:i:i], stmt), f.Syntax.Stmt[i:]...)
165 } else {
166 f.Toolchain.Name = name
167 f.Syntax.updateLine(f.Toolchain.Syntax, "toolchain", name)
168 }
169 return nil
170 }
171
172
173 func (f *WorkFile) DropGoStmt() {
174 if f.Go != nil {
175 f.Go.Syntax.markRemoved()
176 f.Go = nil
177 }
178 }
179
180
181 func (f *WorkFile) DropToolchainStmt() {
182 if f.Toolchain != nil {
183 f.Toolchain.Syntax.markRemoved()
184 f.Toolchain = nil
185 }
186 }
187
188
189
190
191
192
193
194 func (f *WorkFile) AddGodebug(key, value string) error {
195 need := true
196 for _, g := range f.Godebug {
197 if g.Key == key {
198 if need {
199 g.Value = value
200 f.Syntax.updateLine(g.Syntax, "godebug", key+"="+value)
201 need = false
202 } else {
203 g.Syntax.markRemoved()
204 *g = Godebug{}
205 }
206 }
207 }
208
209 if need {
210 f.addNewGodebug(key, value)
211 }
212 return nil
213 }
214
215
216
217 func (f *WorkFile) addNewGodebug(key, value string) {
218 line := f.Syntax.addLine(nil, "godebug", key+"="+value)
219 g := &Godebug{
220 Key: key,
221 Value: value,
222 Syntax: line,
223 }
224 f.Godebug = append(f.Godebug, g)
225 }
226
227 func (f *WorkFile) DropGodebug(key string) error {
228 for _, g := range f.Godebug {
229 if g.Key == key {
230 g.Syntax.markRemoved()
231 *g = Godebug{}
232 }
233 }
234 return nil
235 }
236
237 func (f *WorkFile) AddUse(diskPath, modulePath string) error {
238 need := true
239 for _, d := range f.Use {
240 if d.Path == diskPath {
241 if need {
242 d.ModulePath = modulePath
243 f.Syntax.updateLine(d.Syntax, "use", AutoQuote(diskPath))
244 need = false
245 } else {
246 d.Syntax.markRemoved()
247 *d = Use{}
248 }
249 }
250 }
251
252 if need {
253 f.AddNewUse(diskPath, modulePath)
254 }
255 return nil
256 }
257
258 func (f *WorkFile) AddNewUse(diskPath, modulePath string) {
259 line := f.Syntax.addLine(nil, "use", AutoQuote(diskPath))
260 f.Use = append(f.Use, &Use{Path: diskPath, ModulePath: modulePath, Syntax: line})
261 }
262
263 func (f *WorkFile) SetUse(dirs []*Use) {
264 need := make(map[string]string)
265 for _, d := range dirs {
266 need[d.Path] = d.ModulePath
267 }
268
269 for _, d := range f.Use {
270 if modulePath, ok := need[d.Path]; ok {
271 d.ModulePath = modulePath
272 } else {
273 d.Syntax.markRemoved()
274 *d = Use{}
275 }
276 }
277
278
279
280 for diskPath, modulePath := range need {
281 f.AddNewUse(diskPath, modulePath)
282 }
283 f.SortBlocks()
284 }
285
286 func (f *WorkFile) DropUse(path string) error {
287 for _, d := range f.Use {
288 if d.Path == path {
289 d.Syntax.markRemoved()
290 *d = Use{}
291 }
292 }
293 return nil
294 }
295
296 func (f *WorkFile) AddReplace(oldPath, oldVers, newPath, newVers string) error {
297 return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers)
298 }
299
300 func (f *WorkFile) DropReplace(oldPath, oldVers string) error {
301 for _, r := range f.Replace {
302 if r.Old.Path == oldPath && r.Old.Version == oldVers {
303 r.Syntax.markRemoved()
304 *r = Replace{}
305 }
306 }
307 return nil
308 }
309
310 func (f *WorkFile) SortBlocks() {
311 f.removeDups()
312
313 for _, stmt := range f.Syntax.Stmt {
314 block, ok := stmt.(*LineBlock)
315 if !ok {
316 continue
317 }
318 sort.SliceStable(block.Line, func(i, j int) bool {
319 return lineLess(block.Line[i], block.Line[j])
320 })
321 }
322 }
323
324
325
326
327
328
329
330
331
332
333 func (f *WorkFile) removeDups() {
334 removeDups(f.Syntax, nil, &f.Replace)
335 }
336
View as plain text