@@ -262,6 +262,75 @@ func ParseNonStrict(modfile []byte, filename string) (*File, error) {
262
262
return parse (modfile , filename , false )
263
263
}
264
264
265
+ // FixLegacy converts a legacy module.cue file as parsed by [ParseLegacy]
266
+ // into a format suitable for parsing with [Parse]. It adds a language.version
267
+ // field and moves all unrecognized fields into custom.legacy.
268
+ //
269
+ // If there is no module field or it is empty, it is set to "test.example@v0".
270
+ //
271
+ // If the file already parses OK with [ParseNonStrict], it returns the
272
+ // result of that.
273
+ func FixLegacy (modfile []byte , filename string ) (* File , error ) {
274
+ f , err := ParseNonStrict (modfile , filename )
275
+ if err == nil {
276
+ // It parses OK so it doesn't need fixing.
277
+ return f , nil
278
+ }
279
+ ctx := cuecontext .New ()
280
+ file , err := parseDataOnlyCUE (ctx , modfile , filename )
281
+ if err != nil {
282
+ return nil , errors .Wrapf (err , token .NoPos , "invalid module.cue file syntax" )
283
+ }
284
+ v := ctx .BuildFile (file )
285
+ if err := v .Validate (cue .Concrete (true )); err != nil {
286
+ return nil , errors .Wrapf (err , token .NoPos , "invalid module.cue file value" )
287
+ }
288
+ var allFields map [string ]any
289
+ if err := v .Decode (& allFields ); err != nil {
290
+ return nil , err
291
+ }
292
+ mpath := "test.example@v0"
293
+ if m , ok := allFields ["module" ]; ok {
294
+ if mpath1 , ok := m .(string ); ok && mpath1 != "" {
295
+ mpath = mpath1
296
+ } else if ! ok {
297
+ return nil , fmt .Errorf ("module field has unexpected type %T" , m )
298
+ }
299
+ // TODO decide what to do if the module path isn't OK according to the new rules.
300
+ }
301
+ customLegacy := make (map [string ]any )
302
+ for k , v := range allFields {
303
+ if k != "module" {
304
+ customLegacy [k ] = v
305
+ }
306
+ }
307
+ var custom map [string ]map [string ]any
308
+ if len (customLegacy ) > 0 {
309
+ custom = map [string ]map [string ]any {
310
+ "legacy" : customLegacy ,
311
+ }
312
+ }
313
+ f = & File {
314
+ Module : mpath ,
315
+ Language : & Language {
316
+ Version : cueversion .LanguageVersion (),
317
+ },
318
+ Custom : custom ,
319
+ }
320
+ // Round-trip through [Parse] so that we get exactly the same
321
+ // result as a later parse of the same data will. This also
322
+ // adds a major version to the module path if needed.
323
+ data , err := f .Format ()
324
+ if err != nil {
325
+ return nil , fmt .Errorf ("cannot format fixed file: %v" , err )
326
+ }
327
+ f , err = ParseNonStrict (data , "fixed-" + filename )
328
+ if err != nil {
329
+ return nil , fmt .Errorf ("cannot round-trip fixed module file %q: %v" , data , err )
330
+ }
331
+ return f , nil
332
+ }
333
+
265
334
func parse (modfile []byte , filename string , strict bool ) (* File , error ) {
266
335
// Unfortunately we need a new context. See the note inside [moduleSchemaDo].
267
336
ctx := cuecontext .New ()
0 commit comments