@@ -285,27 +285,29 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
285285 return LoadWithContext (context .Background (), configDetails , options ... )
286286}
287287
288- // LoadWithContext reads a ConfigDetails and returns a fully loaded configuration
288+ // LoadWithContext reads a ConfigDetails and returns a fully loaded configuration as a compose-go Project
289289func LoadWithContext (ctx context.Context , configDetails types.ConfigDetails , options ... func (* Options )) (* types.Project , error ) {
290- if len (configDetails .ConfigFiles ) < 1 {
291- return nil , errors .New ("No files specified" )
290+ opts := toOptions (& configDetails , options )
291+ dict , err := loadModelWithContext (ctx , & configDetails , opts )
292+ if err != nil {
293+ return nil , err
292294 }
295+ return modelToProject (dict , opts , configDetails )
296+ }
293297
294- opts := & Options {
295- Interpolate : & interp.Options {
296- Substitute : template .Substitute ,
297- LookupValue : configDetails .LookupEnv ,
298- TypeCastMapping : interpolateTypeCastMapping ,
299- },
300- ResolvePaths : true ,
301- }
298+ // LoadModelWithContext reads a ConfigDetails and returns a fully loaded configuration as a yaml dictionary
299+ func LoadModelWithContext (ctx context.Context , configDetails types.ConfigDetails , options ... func (* Options )) (map [string ]any , error ) {
300+ opts := toOptions (& configDetails , options )
301+ return loadModelWithContext (ctx , & configDetails , opts )
302+ }
302303
303- for _ , op := range options {
304- op (opts )
304+ // LoadModelWithContext reads a ConfigDetails and returns a fully loaded configuration as a yaml dictionary
305+ func loadModelWithContext (ctx context.Context , configDetails * types.ConfigDetails , opts * Options ) (map [string ]any , error ) {
306+ if len (configDetails .ConfigFiles ) < 1 {
307+ return nil , errors .New ("No files specified" )
305308 }
306- opts .ResourceLoaders = append (opts .ResourceLoaders , localResourceLoader {configDetails .WorkingDir })
307309
308- err := projectName (configDetails , opts )
310+ err := projectName (* configDetails , opts )
309311 if err != nil {
310312 return nil , err
311313 }
@@ -318,7 +320,24 @@ func LoadWithContext(ctx context.Context, configDetails types.ConfigDetails, opt
318320 configDetails .Environment [consts .ComposeProjectName ] = opts .projectName
319321 }
320322
321- return load (ctx , configDetails , opts , nil )
323+ return load (ctx , * configDetails , opts , nil )
324+ }
325+
326+ func toOptions (configDetails * types.ConfigDetails , options []func (* Options )) * Options {
327+ opts := & Options {
328+ Interpolate : & interp.Options {
329+ Substitute : template .Substitute ,
330+ LookupValue : configDetails .LookupEnv ,
331+ TypeCastMapping : interpolateTypeCastMapping ,
332+ },
333+ ResolvePaths : true ,
334+ }
335+
336+ for _ , op := range options {
337+ op (opts )
338+ }
339+ opts .ResourceLoaders = append (opts .ResourceLoaders , localResourceLoader {configDetails .WorkingDir })
340+ return opts
322341}
323342
324343func loadYamlModel (ctx context.Context , config types.ConfigDetails , opts * Options , ct * cycleTracker , included []string ) (map [string ]interface {}, error ) {
@@ -458,7 +477,7 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
458477 return dict , nil
459478}
460479
461- func load (ctx context.Context , configDetails types.ConfigDetails , opts * Options , loaded []string ) (* types. Project , error ) {
480+ func load (ctx context.Context , configDetails types.ConfigDetails , opts * Options , loaded []string ) (map [ string ] interface {} , error ) {
462481 mainFile := configDetails .ConfigFiles [0 ].Filename
463482 for _ , f := range loaded {
464483 if f == mainFile {
@@ -481,13 +500,26 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,
481500 return nil , errors .New ("project name must not be empty" )
482501 }
483502
503+ if ! opts .SkipNormalization {
504+ dict , err = Normalize (dict , configDetails .Environment )
505+ if err != nil {
506+ return nil , err
507+ }
508+ }
509+
510+ return dict , nil
511+ }
512+
513+ // modelToProject binds a canonical yaml dict into compose-go structs
514+ func modelToProject (dict map [string ]interface {}, opts * Options , configDetails types.ConfigDetails ) (* types.Project , error ) {
484515 project := & types.Project {
485516 Name : opts .projectName ,
486517 WorkingDir : configDetails .WorkingDir ,
487518 Environment : configDetails .Environment ,
488519 }
489520 delete (dict , "name" ) // project name set by yaml must be identified by caller as opts.projectName
490521
522+ var err error
491523 dict , err = processExtensions (dict , tree .NewPath (), opts .KnownExtensions )
492524 if err != nil {
493525 return nil , err
@@ -498,13 +530,6 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,
498530 return nil , err
499531 }
500532
501- if ! opts .SkipNormalization {
502- err := Normalize (project )
503- if err != nil {
504- return nil , err
505- }
506- }
507-
508533 if opts .ConvertWindowsPaths {
509534 for i , service := range project .Services {
510535 for j , volume := range service .Volumes {
@@ -531,7 +556,6 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,
531556 return nil , err
532557 }
533558 }
534-
535559 return project , nil
536560}
537561
0 commit comments