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