@@ -48,20 +48,6 @@ func deref(t reflect.Type) reflect.Type {
4848 return t
4949}
5050
51- // assertStructKind checks if T can be dereferenced into a type with struct kind.
52- //
53- // We assert this because our unmarshaling logic currently only supports structs.
54- // This can be removed if we ever support scalar values.
55- func assertStructKind [T any ]() error {
56- var instance T
57- t := deref (reflect .TypeOf (instance ))
58- if t .Kind () != reflect .Struct {
59- return fmt .Errorf ("configoptional: %q does not have a struct kind" , t )
60- }
61-
62- return nil
63- }
64-
6551// assertNoEnabledField checks that a struct type
6652// does not have a field with a mapstructure tag "enabled".
6753//
@@ -101,11 +87,9 @@ func Some[T any](value T) Optional[T] {
10187
10288// Default creates an Optional with a default value for unmarshaling.
10389//
104- // It panics if
105- // - T is not a struct OR
106- // - T has a field with the mapstructure tag "enabled".
90+ // It panics if T has a field with the mapstructure tag "enabled".
10791func Default [T any ](value T ) Optional [T ] {
108- err := errors .Join (assertStructKind [ T ](), assertNoEnabledField [T ]())
92+ err := errors .Join (assertNoEnabledField [T ]())
10993 if err != nil {
11094 panic (err )
11195 }
@@ -149,7 +133,7 @@ func (o *Optional[T]) Get() *T {
149133// - T is not a struct OR
150134// - T has a field with the mapstructure tag "enabled".
151135func (o * Optional [T ]) GetOrInsertDefault () * T {
152- err := errors .Join (assertStructKind [ T ](), assertNoEnabledField [T ]())
136+ err := errors .Join (assertNoEnabledField [T ]())
153137 if err != nil {
154138 panic (err )
155139 }
@@ -167,7 +151,10 @@ func (o *Optional[T]) GetOrInsertDefault() *T {
167151 return o .Get ()
168152}
169153
170- var _ confmap.Unmarshaler = (* Optional [any ])(nil )
154+ var (
155+ _ confmap.Unmarshaler = (* Optional [any ])(nil )
156+ _ xconfmap.ScalarUnmarshaler = (* Optional [any ])(nil )
157+ )
171158
172159// Unmarshal the configuration into the Optional value.
173160//
@@ -205,7 +192,7 @@ func (o *Optional[T]) Unmarshal(conf *confmap.Conf) error {
205192 }
206193 }
207194
208- if err := conf .Unmarshal (& o .value , xconfmap .WithForceUnmarshaler ()); err != nil {
195+ if err := conf .Unmarshal (& o .value , xconfmap .WithForceUnmarshaler (), xconfmap . WithScalarMarshaler () ); err != nil {
209196 return err
210197 }
211198
@@ -221,7 +208,36 @@ func (o *Optional[T]) Unmarshal(conf *confmap.Conf) error {
221208 return nil
222209}
223210
224- var _ confmap.Marshaler = (* Optional [any ])(nil )
211+ // UnmarshalScalar unmarshals a scalar value into the Optional.
212+ //
213+ // A `nil` value will set the Optional to None, disabling it as setting
214+ // `enabled: false` for a struct-type Optional or `null` for a pointer field
215+ // would.
216+ func (o * Optional [T ]) UnmarshalScalar (val any ) error {
217+ if val == nil {
218+ var zero T
219+ o .value = zero
220+ o .flavor = noneFlavor
221+ return nil
222+ }
223+
224+ v , ok := val .(T )
225+ if ! ok {
226+ return fmt .Errorf ("val is %T, not %T" , val , v )
227+ }
228+ o .value = v
229+ o .flavor = someFlavor
230+ return nil
231+ }
232+
233+ func (o * Optional [T ]) ScalarType () any {
234+ return o .value
235+ }
236+
237+ var (
238+ _ confmap.Marshaler = (* Optional [any ])(nil )
239+ _ xconfmap.ScalarMarshaler = (* Optional [any ])(nil )
240+ )
225241
226242// Marshal the Optional value into the configuration.
227243// If the Optional is None or Default, it does not marshal anything.
@@ -230,22 +246,26 @@ var _ confmap.Marshaler = (*Optional[any])(nil)
230246// T must be derefenceable to a type with struct kind.
231247// Scalar values are not supported.
232248func (o Optional [T ]) Marshal (conf * confmap.Conf ) error {
233- if err := assertStructKind [T ](); err != nil {
234- return err
235- }
236-
237249 if o .flavor == noneFlavor || o .flavor == defaultFlavor {
238250 // Optional is None or Default, do not marshal anything.
239251 return conf .Marshal (map [string ]any (nil ))
240252 }
241253
242- if err := conf .Marshal (o .value ); err != nil {
254+ if err := conf .Marshal (o .value , xconfmap . WithScalarMarshaler () ); err != nil {
243255 return fmt .Errorf ("configoptional: failed to marshal Optional value: %w" , err )
244256 }
245257
246258 return nil
247259}
248260
261+ func (o Optional [T ]) GetScalarValue () (any , error ) {
262+ if o .flavor == noneFlavor || o .flavor == defaultFlavor {
263+ return nil , nil
264+ }
265+
266+ return o .value , nil
267+ }
268+
249269var _ xconfmap.Validator = (* Optional [any ])(nil )
250270
251271// Validate implements [xconfmap.Validator]. This is required because the
0 commit comments