| 1 | package steward |
| 2 | |
| 3 | import ( |
| 4 | "reflect" |
| 5 | "unicode" |
| 6 | "unicode/utf8" |
| 7 | |
| 8 | "github.com/soverenio/vanilla/throw" |
| 9 | |
| 10 | "sourcecraft.dev/bigbes/auxilia/collect" |
| 11 | "sourcecraft.dev/bigbes/auxilia/fn" |
| 12 | ) |
| 13 | |
| 14 | var _ Asset = &configurationAsset{} |
| 15 | |
| 16 | type configurationAsset struct { |
| 17 | value any |
| 18 | subConfigurations []reflect.Value |
| 19 | } |
| 20 | |
| 21 | func MustConfigurationAsset(value any) Asset { |
| 22 | return fn.Must1(ConfigurationAsset(value)) |
| 23 | } |
| 24 | |
| 25 | func MustNewConfigurationAsset(value any) Asset { return MustConfigurationAsset(value) } |
| 26 | |
| 27 | func ConfigurationAsset(value any) (Asset, error) { |
| 28 | cfgAsset := &configurationAsset{value: value} |
| 29 | return cfgAsset, cfgAsset.populateVerifyTop() |
| 30 | } |
| 31 | |
| 32 | func NewConfigurationAsset(value any) (Asset, error) { return ConfigurationAsset(value) } |
| 33 | |
| 34 | func (c *configurationAsset) asset() {} |
| 35 | |
| 36 | func (c *configurationAsset) Name() string { |
| 37 | return reflect.TypeOf(c.value).String() |
| 38 | } |
| 39 | |
| 40 | func (c *configurationAsset) populateVerifyTop() error { |
| 41 | topValue := reflect.ValueOf(c.value) |
| 42 | c.subConfigurations = append(c.subConfigurations, topValue) |
| 43 | return c.populateVerify(topValue, true) |
| 44 | } |
| 45 | |
| 46 | func (c *configurationAsset) saveSubConfiguration(fieldDef reflect.StructField, value reflect.Value) { |
| 47 | if fieldDef.Type.Kind() != reflect.Struct { |
| 48 | return |
| 49 | } else if _, ok := fieldDef.Tag.Lookup("config-section"); !ok { |
| 50 | return |
| 51 | } |
| 52 | |
| 53 | c.subConfigurations = append(c.subConfigurations, value) |
| 54 | } |
| 55 | |
| 56 | func (c *configurationAsset) populateVerify(value reflect.Value, populate bool) error { |
| 57 | switch value.Kind() { |
| 58 | case reflect.Struct: |
| 59 | ok := true |
| 60 | |
| 61 | structFieldIteratorFromType(value).Filter(func(pair structField) bool { |
| 62 | r, _ := utf8.DecodeRuneInString(pair.First().Name) |
| 63 | if r == utf8.RuneError { |
| 64 | return false |
| 65 | } |
| 66 | return !unicode.IsLower(r) |
| 67 | }).Apply(func(pair collect.Tuple2[reflect.StructField, reflect.Value]) { |
| 68 | c.saveSubConfiguration(pair.Values()) |
| 69 | |
| 70 | ok = ok && (c.populateVerify(pair.Second(), populate) == nil) |
| 71 | }) |
| 72 | |
| 73 | if !ok { |
| 74 | return throw.New("failed") |
| 75 | } |
| 76 | return nil |
| 77 | case reflect.Array: |
| 78 | return nil |
| 79 | case reflect.Slice: |
| 80 | return nil |
| 81 | case reflect.Map: |
| 82 | return nil |
| 83 | case reflect.Pointer: |
| 84 | if value.IsNil() { |
| 85 | return nil |
| 86 | } |
| 87 | return c.populateVerify(value.Elem(), populate) |
| 88 | case reflect.Bool, reflect.String: |
| 89 | return nil |
| 90 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| 91 | return nil |
| 92 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
| 93 | return nil |
| 94 | case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: |
| 95 | return nil |
| 96 | default: |
| 97 | return throw.New("unexpected type for configurationAsset asset: " + value.Kind().String()) |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | func (c *configurationAsset) TryInject(object any) (injected bool) { |
| 102 | objectValue := reflect.ValueOf(object) |
| 103 | |
| 104 | for _, config := range c.subConfigurations { |
| 105 | if !typesEqual(objectValue.Type(), config.Type()) { |
| 106 | continue |
| 107 | } |
| 108 | |
| 109 | objectValue.Elem().Set(config) |
| 110 | return true |
| 111 | } |
| 112 | |
| 113 | return false |
| 114 | } |
| 115 | |
| 116 | func (c *configurationAsset) TryInjectTo(component *serviceAsset, field string) (injected bool) { |
| 117 | objectValue := component.reflectedValue.Elem().FieldByName(field) |
| 118 | |
| 119 | for _, config := range c.subConfigurations { |
| 120 | if !typesEqual(objectValue.Type(), config.Type()) { |
| 121 | continue |
| 122 | } |
| 123 | |
| 124 | objectValue.Set(config) |
| 125 | |
| 126 | return true |
| 127 | } |
| 128 | |
| 129 | return false |
| 130 | } |
| 131 | |