@@ -217,6 +217,13 @@ func (c *Config) getPackageConfigMap(ctx context.Context, packageName string) (m
217217 return emptyMap , nil
218218
219219}
220+
221+ // GetPackageConfig returns a struct representation of the package's config
222+ // as provided in yaml. If the package did not specify a config section,
223+ // this method will inject the top-level config into the package's config.
224+ // This is especially useful as it allows us to lazily evaluate a package's
225+ // config. If the package does specify config, this method takes care to merge
226+ // the top-level config with the values specified for this package.
220227func (c * Config ) GetPackageConfig (ctx context.Context , packageName string ) (* Config , error ) {
221228 log := zerolog .Ctx (ctx ).With ().Str ("package-path" , packageName ).Logger ()
222229
@@ -228,13 +235,10 @@ func (c *Config) GetPackageConfig(ctx context.Context, packageName string) (*Con
228235 return pkgConf , nil
229236 }
230237
231- //pkgConfig := reflect.New(reflect.ValueOf(c).Elem().Type()).Interface()
232238 pkgConfig := & Config {}
233239 if err := copier .Copy (pkgConfig , c ); err != nil {
234240 return nil , fmt .Errorf ("failed to copy config: %w" , err )
235241 }
236- //pkgConfigTyped := pkgConfig.(*Config)
237- pkgConfigTyped := pkgConfig
238242
239243 configMap , err := c .getPackageConfigMap (ctx , packageName )
240244 if err != nil {
@@ -245,18 +249,23 @@ func (c *Config) GetPackageConfig(ctx context.Context, packageName string) (*Con
245249 if ! ok {
246250 log .Debug ().Msg ("config section not provided for package" )
247251 configMap ["config" ] = deepCopyConfigMap (c ._cfgAsMap )
248- return pkgConfigTyped , nil
252+ c .pkgConfigCache [packageName ] = pkgConfig
253+ return pkgConfig , nil
249254 }
250255
251- decoder , err := c .getDecoder (pkgConfigTyped )
256+ // We know that the package specified config that is overriding the top-level
257+ // config. We use a mapstructure decoder to decode the values in the yaml
258+ // into the pkgConfig struct. This has the effect of merging top-level
259+ // config with package-level config.
260+ decoder , err := c .getDecoder (pkgConfig )
252261 if err != nil {
253262 return nil , stackerr .NewStackErrf (err , "failed to get decoder" )
254263 }
255264 if err := decoder .Decode (configSection ); err != nil {
256265 return nil , err
257266 }
258- c .pkgConfigCache [packageName ] = pkgConfigTyped
259- return pkgConfigTyped , nil
267+ c .pkgConfigCache [packageName ] = pkgConfig
268+ return pkgConfig , nil
260269}
261270
262271func (c * Config ) ExcludePath (path string ) bool {
@@ -355,16 +364,15 @@ func (c *Config) GetInterfaceConfig(ctx context.Context, packageName string, int
355364 }
356365
357366 // Copy the package-level config to our interface-level config
358- pkgConfigCopy := reflect . New ( reflect . ValueOf ( pkgConfig ). Elem (). Type ()). Interface ()
367+ pkgConfigCopy := & Config {}
359368 if err := copier .Copy (pkgConfigCopy , pkgConfig ); err != nil {
360369 return nil , stackerr .NewStackErrf (err , "failed to create a copy of package config" )
361370 }
362- baseConfigTyped := pkgConfigCopy .(* Config )
363371
364372 interfaceSection , ok := interfacesSection [interfaceName ]
365373 if ! ok {
366374 log .Debug ().Msg ("interface not defined in package configuration" )
367- return []* Config {baseConfigTyped }, nil
375+ return []* Config {pkgConfigCopy }, nil
368376 }
369377
370378 interfaceSectionTyped , ok := interfaceSection .(map [string ]any )
@@ -373,7 +381,7 @@ func (c *Config) GetInterfaceConfig(ctx context.Context, packageName string, int
373381 // the interface but not provide any additional config beyond what
374382 // is provided at the package level
375383 if reflect .ValueOf (& interfaceSection ).Elem ().IsZero () {
376- return []* Config {baseConfigTyped }, nil
384+ return []* Config {pkgConfigCopy }, nil
377385 }
378386 msgString := "bad type provided for interface config"
379387 log .Error ().Msgf (msgString )
@@ -384,11 +392,11 @@ func (c *Config) GetInterfaceConfig(ctx context.Context, packageName string, int
384392 if ok {
385393 log .Debug ().Msg ("config section exists for interface" )
386394 // if `config` is provided, we'll overwrite the values in our
387- // baseConfigTyped struct to act as the "new" base config.
395+ // pkgConfigCopy struct to act as the "new" base config.
388396 // This will allow us to set the default values for the interface
389397 // but override them further for each mock defined in the
390398 // `configs` section.
391- decoder , err := c .getDecoder (baseConfigTyped )
399+ decoder , err := c .getDecoder (pkgConfigCopy )
392400 if err != nil {
393401 return nil , stackerr .NewStackErrf (err , "unable to create mapstructure decoder" )
394402 }
@@ -405,8 +413,8 @@ func (c *Config) GetInterfaceConfig(ctx context.Context, packageName string, int
405413 configsSectionTyped := configsSection .([]any )
406414 for _ , configMap := range configsSectionTyped {
407415 // Create a copy of the package-level config
408- currentInterfaceConfig := reflect .New (reflect .ValueOf (baseConfigTyped ).Elem ().Type ()).Interface ()
409- if err := copier .Copy (currentInterfaceConfig , baseConfigTyped ); err != nil {
416+ currentInterfaceConfig := reflect .New (reflect .ValueOf (pkgConfigCopy ).Elem ().Type ()).Interface ()
417+ if err := copier .Copy (currentInterfaceConfig , pkgConfigCopy ); err != nil {
410418 return nil , stackerr .NewStackErrf (err , "failed to copy package config" )
411419 }
412420
@@ -426,7 +434,7 @@ func (c *Config) GetInterfaceConfig(ctx context.Context, packageName string, int
426434 log .Debug ().Msg ("configs section doesn't exist for interface" )
427435
428436 if len (configs ) == 0 {
429- configs = append (configs , baseConfigTyped )
437+ configs = append (configs , pkgConfigCopy )
430438 }
431439 return configs , nil
432440}
@@ -674,14 +682,14 @@ func contains[T comparable](slice []T, elem T) bool {
674682}
675683
676684func deepCopyConfigMap (src map [string ]any ) map [string ]any {
677- new := map [string ]any {}
685+ newMap := map [string ]any {}
678686 for key , val := range src {
679687 if contains ([]string {"packages" , "config" , "interfaces" }, key ) {
680688 continue
681689 }
682- new [key ] = val
690+ newMap [key ] = val
683691 }
684- return new
692+ return newMap
685693}
686694
687695// mergeInConfig takes care of merging inheritable configuration
0 commit comments