package planr import ( "github.com/BurntSushi/toml" ) /* TODO: Every property defined within the defaults currently has to implement the "inherit" method to conditionally inherit a property in relation to a parent. (Ostensibly so that test cases can override default configuration.) This is pedantic because most properties will end up writing boilerplate amounting to: parent.inherit() ... if config.property == nil { config.property = config.parent.property } This library provides copying behavior between structs with common properties using reflection. It seems like a slight abuse of reflection... But, it could be retro-fitted to implement this behavior if a "onlyCopyZeroFields" option was provided. > "github.com/jinzhu/copier" */ // Inheritable configuration can inherit properties defined in a // defaults file. This happens on a per-directory basis so multiple // tests can share common configuration. // // The parent will always be of the same type as the child and an // assertion is required to define the proper behavior. type InheritableConfig interface { Inherit(parent interface{}) } // Program-wide configuration which is recognized // in defaults.toml type Defaults struct { Description string Points *float32 Adapter string /* The TOML library only parses exported fields. The Adapters field is an intermediate mapping individual adapters to their locally defined configuration. After they individually process the configuration, it is mapped to the adapters_ field. See: decodeAdapters() */ Adapters *map[string] toml.Primitive adapters_ map[string] InheritableConfig /* The configs_ field is necessary to properly implement the Inherit method using a common interface. */ configs_ *[]AdapterConfig } // The default configuration must be able in inherit from // other defaults further up the tree // // This provides multiple levels of configurability func (child *Defaults) Inherit(p interface{}) { parent := p.(Defaults) // Inherit properties which haven't been configured if child.Points == nil { child.Points = parent.Points } if child.Adapter == "" { child.Adapter = parent.Adapter } if child.Description == "" { child.Description = parent.Description } // Call the inherit method as defined by the adapters // If an adapter is undefined, inherit the parent configuration // // _configs represents all adapters (registered to a runner) for _, adapter := range *parent.configs_ { parent_adapter, parent_exists := parent.adapters_[adapter.Name] child_adapter, child_exists := child.adapters_[adapter.Name] if parent_exists { if child_exists { child_adapter.Inherit(parent_adapter) } else { child.adapters_[adapter.Name] = parent_adapter } } } } // Parses the intermediate adapters Adapters containing TOML primitives // according to methods registered with the runner // Once parsed, they are stored alongside the registered name to determine // which adapter will receive the configuration func (defaults *Defaults) decodeAdapters( adapters []AdapterConfig, asDefault bool, ) error { defaults.configs_ = &adapters defaults.adapters_ = make(map[string]InheritableConfig) if defaults.Adapters != nil { for _, config := range adapters { primitive, exists := (*defaults.Adapters)[config.Name] if exists { var parsed InheritableConfig var err error if asDefault { parsed, err = config.ParseDefaultConfig(primitive) } else { parsed, err = config.ParseConfig(primitive) } if err != nil { return err } defaults.adapters_[config.Name] = parsed } } } return nil } // Decode defaults.toml func DecodeRubricDefaults(path string, adapterCfg []AdapterConfig) (Defaults, error) { defaults := Defaults { } if _, err := toml.DecodeFile(path, &defaults); err != nil { return defaults, err } if err := defaults.decodeAdapters(adapterCfg, true); err != nil { return defaults, err } return defaults, nil } // Decode an individual unit func DecodeRubricConfig(path string, adapterCfg []AdapterConfig) (TestCaseConfig, error) { config := TestCaseConfig { } if _, err := toml.DecodeFile(path, &config); err != nil { return config, nil } if err := config.decodeAdapters(adapterCfg, false); err != nil { return config, err } return config, nil }