@@ -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,12 +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 ]())
109- if err != nil {
92+ if err := assertNoEnabledField [T ](); err != nil {
11093 panic (err )
11194 }
11295 return Optional [T ]{value : value , flavor : defaultFlavor }
@@ -149,8 +132,7 @@ func (o *Optional[T]) Get() *T {
149132// - T is not a struct OR
150133// - T has a field with the mapstructure tag "enabled".
151134func (o * Optional [T ]) GetOrInsertDefault () * T {
152- err := errors .Join (assertStructKind [T ](), assertNoEnabledField [T ]())
153- if err != nil {
135+ if err := assertNoEnabledField [T ](); err != nil {
154136 panic (err )
155137 }
156138
@@ -167,7 +149,10 @@ func (o *Optional[T]) GetOrInsertDefault() *T {
167149 return o .Get ()
168150}
169151
170- var _ confmap.Unmarshaler = (* Optional [any ])(nil )
152+ var (
153+ _ confmap.Unmarshaler = (* Optional [any ])(nil )
154+ _ xconfmap.ScalarUnmarshaler = (* Optional [any ])(nil )
155+ )
171156
172157// Unmarshal the configuration into the Optional value.
173158//
@@ -205,7 +190,7 @@ func (o *Optional[T]) Unmarshal(conf *confmap.Conf) error {
205190 }
206191 }
207192
208- if err := conf .Unmarshal (& o .value , xconfmap .WithForceUnmarshaler ()); err != nil {
193+ if err := conf .Unmarshal (& o .value , xconfmap .WithForceUnmarshaler (), xconfmap . WithScalarMarshaler () ); err != nil {
209194 return err
210195 }
211196
@@ -221,7 +206,36 @@ func (o *Optional[T]) Unmarshal(conf *confmap.Conf) error {
221206 return nil
222207}
223208
224- var _ confmap.Marshaler = (* Optional [any ])(nil )
209+ // UnmarshalScalar unmarshals a scalar value into the Optional.
210+ //
211+ // A `nil` value will set the Optional to None, disabling it as setting
212+ // `enabled: false` for a struct-type Optional or `null` for a pointer field
213+ // would.
214+ func (o * Optional [T ]) UnmarshalScalar (val any ) error {
215+ if val == nil {
216+ var zero T
217+ o .value = zero
218+ o .flavor = noneFlavor
219+ return nil
220+ }
221+
222+ v , ok := val .(T )
223+ if ! ok {
224+ return fmt .Errorf ("val is %T, not %T" , val , v )
225+ }
226+ o .value = v
227+ o .flavor = someFlavor
228+ return nil
229+ }
230+
231+ func (o * Optional [T ]) ScalarType () any {
232+ return o .value
233+ }
234+
235+ var (
236+ _ confmap.Marshaler = (* Optional [any ])(nil )
237+ _ xconfmap.ScalarMarshaler = (* Optional [any ])(nil )
238+ )
225239
226240// Marshal the Optional value into the configuration.
227241// If the Optional is None or Default, it does not marshal anything.
@@ -230,22 +244,26 @@ var _ confmap.Marshaler = (*Optional[any])(nil)
230244// T must be derefenceable to a type with struct kind.
231245// Scalar values are not supported.
232246func (o Optional [T ]) Marshal (conf * confmap.Conf ) error {
233- if err := assertStructKind [T ](); err != nil {
234- return err
235- }
236-
237247 if o .flavor == noneFlavor || o .flavor == defaultFlavor {
238248 // Optional is None or Default, do not marshal anything.
239249 return conf .Marshal (map [string ]any (nil ))
240250 }
241251
242- if err := conf .Marshal (o .value ); err != nil {
252+ if err := conf .Marshal (o .value , xconfmap . WithScalarMarshaler () ); err != nil {
243253 return fmt .Errorf ("configoptional: failed to marshal Optional value: %w" , err )
244254 }
245255
246256 return nil
247257}
248258
259+ func (o Optional [T ]) GetScalarValue () (any , error ) {
260+ if o .flavor == noneFlavor || o .flavor == defaultFlavor {
261+ return nil , nil
262+ }
263+
264+ return o .value , nil
265+ }
266+
249267var _ xconfmap.Validator = (* Optional [any ])(nil )
250268
251269// Validate implements [xconfmap.Validator]. This is required because the
0 commit comments