@@ -6,6 +6,7 @@ package json
6
6
7
7
import (
8
8
"fmt"
9
+ "reflect"
9
10
10
11
"github.com/go-json-experiment/json/internal"
11
12
"github.com/go-json-experiment/json/internal/jsonflags"
@@ -245,20 +246,47 @@ func WithUnmarshalers(v *Unmarshalers) Options {
245
246
return (* unmarshalersOption )(v )
246
247
}
247
248
249
+ // WithOption constructs a user-defined option value.
250
+ // Later occurrences of an option of a particular type overrides
251
+ // prior occurrences of an option of the exact same type.
252
+ // The type T must be a declared type in a package or
253
+ // a pointer to such a type.
254
+ //
255
+ // A user-defined option can be constructed using:
256
+ //
257
+ // var v mypkg.MyType = ...
258
+ // opts := json.WithOption(v)
259
+ //
260
+ // The option value can be retrieved using:
261
+ //
262
+ // v, ok := json.GetOption(opts, json.WithOption[mypkg.MyType])
263
+ func WithOption [T any ](v T ) Options {
264
+ t := reflect .TypeFor [T ]()
265
+ if t .PkgPath () == "" && (t .Kind () != reflect .Pointer || t .Elem ().PkgPath () == "" ) {
266
+ panic (fmt .Sprintf ("%T must a declared type or a pointer to such a type" , v ))
267
+ }
268
+ // TODO: Limit this to non-interface types?
269
+ return userOption [T ]{v }
270
+ }
271
+
248
272
// These option types are declared here instead of "jsonopts"
249
273
// to avoid a dependency on "reflect" from "jsonopts".
250
274
type (
251
275
marshalersOption Marshalers
252
276
unmarshalersOption Unmarshalers
277
+ userOption [T any ] struct { v T }
253
278
)
254
279
255
280
func (* marshalersOption ) JSONOptions (internal.NotForPublicUse ) {}
256
281
func (* unmarshalersOption ) JSONOptions (internal.NotForPublicUse ) {}
282
+ func (userOption [T ]) JSONOptions (internal.NotForPublicUse ) {}
283
+ func (userOption [T ]) key () any { return reflect .TypeFor [T ]() }
284
+ func (v userOption [T ]) val () any { return v .v }
257
285
258
286
// Inject support into "jsonopts" to handle these types.
259
287
func init () {
260
288
jsonopts .GetUnknownOption = func (src * jsonopts.Struct , zero jsonopts.Options ) (any , bool ) {
261
- switch zero .(type ) {
289
+ switch zero := zero .(type ) {
262
290
case * marshalersOption :
263
291
if ! src .Flags .Has (jsonflags .Marshalers ) {
264
292
return (* Marshalers )(nil ), false
@@ -269,6 +297,14 @@ func init() {
269
297
return (* Unmarshalers )(nil ), false
270
298
}
271
299
return src .Unmarshalers .(* Unmarshalers ), true
300
+ case interface {
301
+ key () any
302
+ val () any
303
+ }: // implemented by [userOption]
304
+ if v , ok := src .UserValues [zero .key ()]; ok {
305
+ return v , true
306
+ }
307
+ return zero .val (), false
272
308
default :
273
309
panic (fmt .Sprintf ("unknown option %T" , zero ))
274
310
}
@@ -281,6 +317,14 @@ func init() {
281
317
case * unmarshalersOption :
282
318
dst .Flags .Set (jsonflags .Unmarshalers | 1 )
283
319
dst .Unmarshalers = (* Unmarshalers )(src )
320
+ case interface {
321
+ key () any
322
+ val () any
323
+ }: // implemented by [userOption]
324
+ if dst .UserValues == nil {
325
+ dst .UserValues = make (map [any ]any )
326
+ }
327
+ dst .UserValues [src .key ()] = src .val ()
284
328
default :
285
329
panic (fmt .Sprintf ("unknown option %T" , src ))
286
330
}
0 commit comments