Source file src/encoding/json/internal/jsonopts/options.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.jsonv2
     6  
     7  package jsonopts
     8  
     9  import (
    10  	"encoding/json/internal"
    11  	"encoding/json/internal/jsonflags"
    12  )
    13  
    14  // Options is the common options type shared across json packages.
    15  type Options interface {
    16  	// JSONOptions is exported so related json packages can implement Options.
    17  	JSONOptions(internal.NotForPublicUse)
    18  }
    19  
    20  // Struct is the combination of all options in struct form.
    21  // This is efficient to pass down the call stack and to query.
    22  type Struct struct {
    23  	Flags jsonflags.Flags
    24  
    25  	CoderValues
    26  	ArshalValues
    27  }
    28  
    29  type CoderValues struct {
    30  	Indent       string // jsonflags.Indent
    31  	IndentPrefix string // jsonflags.IndentPrefix
    32  	ByteLimit    int64  // jsonflags.ByteLimit
    33  	DepthLimit   int    // jsonflags.DepthLimit
    34  }
    35  
    36  type ArshalValues struct {
    37  	// The Marshalers and Unmarshalers fields use the any type to avoid a
    38  	// concrete dependency on *json.Marshalers and *json.Unmarshalers,
    39  	// which would in turn create a dependency on the "reflect" package.
    40  
    41  	Marshalers   any // jsonflags.Marshalers
    42  	Unmarshalers any // jsonflags.Unmarshalers
    43  
    44  	Format      string
    45  	FormatDepth int
    46  }
    47  
    48  // DefaultOptionsV2 is the set of all options that define default v2 behavior.
    49  var DefaultOptionsV2 = Struct{
    50  	Flags: jsonflags.Flags{
    51  		Presence: uint64(jsonflags.AllFlags & ^jsonflags.WhitespaceFlags),
    52  		Values:   uint64(0),
    53  	},
    54  }
    55  
    56  // DefaultOptionsV1 is the set of all options that define default v1 behavior.
    57  var DefaultOptionsV1 = Struct{
    58  	Flags: jsonflags.Flags{
    59  		Presence: uint64(jsonflags.AllFlags & ^jsonflags.WhitespaceFlags),
    60  		Values:   uint64(jsonflags.DefaultV1Flags),
    61  	},
    62  }
    63  
    64  func (*Struct) JSONOptions(internal.NotForPublicUse) {}
    65  
    66  // GetUnknownOption is injected by the "json" package to handle Options
    67  // declared in that package so that "jsonopts" can handle them.
    68  var GetUnknownOption = func(*Struct, Options) (any, bool) { panic("unknown option") }
    69  
    70  func GetOption[T any](opts Options, setter func(T) Options) (T, bool) {
    71  	// Collapse the options to *Struct to simplify lookup.
    72  	structOpts, ok := opts.(*Struct)
    73  	if !ok {
    74  		var structOpts2 Struct
    75  		structOpts2.Join(opts)
    76  		structOpts = &structOpts2
    77  	}
    78  
    79  	// Lookup the option based on the return value of the setter.
    80  	var zero T
    81  	switch opt := setter(zero).(type) {
    82  	case jsonflags.Bools:
    83  		v := structOpts.Flags.Get(opt)
    84  		ok := structOpts.Flags.Has(opt)
    85  		return any(v).(T), ok
    86  	case Indent:
    87  		if !structOpts.Flags.Has(jsonflags.Indent) {
    88  			return zero, false
    89  		}
    90  		return any(structOpts.Indent).(T), true
    91  	case IndentPrefix:
    92  		if !structOpts.Flags.Has(jsonflags.IndentPrefix) {
    93  			return zero, false
    94  		}
    95  		return any(structOpts.IndentPrefix).(T), true
    96  	case ByteLimit:
    97  		if !structOpts.Flags.Has(jsonflags.ByteLimit) {
    98  			return zero, false
    99  		}
   100  		return any(structOpts.ByteLimit).(T), true
   101  	case DepthLimit:
   102  		if !structOpts.Flags.Has(jsonflags.DepthLimit) {
   103  			return zero, false
   104  		}
   105  		return any(structOpts.DepthLimit).(T), true
   106  	default:
   107  		v, ok := GetUnknownOption(structOpts, opt)
   108  		return v.(T), ok
   109  	}
   110  }
   111  
   112  // JoinUnknownOption is injected by the "json" package to handle Options
   113  // declared in that package so that "jsonopts" can handle them.
   114  var JoinUnknownOption = func(*Struct, Options) { panic("unknown option") }
   115  
   116  func (dst *Struct) Join(srcs ...Options) {
   117  	dst.join(false, srcs...)
   118  }
   119  
   120  func (dst *Struct) JoinWithoutCoderOptions(srcs ...Options) {
   121  	dst.join(true, srcs...)
   122  }
   123  
   124  func (dst *Struct) join(excludeCoderOptions bool, srcs ...Options) {
   125  	for _, src := range srcs {
   126  		switch src := src.(type) {
   127  		case nil:
   128  			continue
   129  		case jsonflags.Bools:
   130  			if excludeCoderOptions {
   131  				src &= ^jsonflags.AllCoderFlags
   132  			}
   133  			dst.Flags.Set(src)
   134  		case Indent:
   135  			if excludeCoderOptions {
   136  				continue
   137  			}
   138  			dst.Flags.Set(jsonflags.Multiline | jsonflags.Indent | 1)
   139  			dst.Indent = string(src)
   140  		case IndentPrefix:
   141  			if excludeCoderOptions {
   142  				continue
   143  			}
   144  			dst.Flags.Set(jsonflags.Multiline | jsonflags.IndentPrefix | 1)
   145  			dst.IndentPrefix = string(src)
   146  		case ByteLimit:
   147  			if excludeCoderOptions {
   148  				continue
   149  			}
   150  			dst.Flags.Set(jsonflags.ByteLimit | 1)
   151  			dst.ByteLimit = int64(src)
   152  		case DepthLimit:
   153  			if excludeCoderOptions {
   154  				continue
   155  			}
   156  			dst.Flags.Set(jsonflags.DepthLimit | 1)
   157  			dst.DepthLimit = int(src)
   158  		case *Struct:
   159  			srcFlags := src.Flags // shallow copy the flags
   160  			if excludeCoderOptions {
   161  				srcFlags.Clear(jsonflags.AllCoderFlags)
   162  			}
   163  			dst.Flags.Join(srcFlags)
   164  			if srcFlags.Has(jsonflags.NonBooleanFlags) {
   165  				if srcFlags.Has(jsonflags.Indent) {
   166  					dst.Indent = src.Indent
   167  				}
   168  				if srcFlags.Has(jsonflags.IndentPrefix) {
   169  					dst.IndentPrefix = src.IndentPrefix
   170  				}
   171  				if srcFlags.Has(jsonflags.ByteLimit) {
   172  					dst.ByteLimit = src.ByteLimit
   173  				}
   174  				if srcFlags.Has(jsonflags.DepthLimit) {
   175  					dst.DepthLimit = src.DepthLimit
   176  				}
   177  				if srcFlags.Has(jsonflags.Marshalers) {
   178  					dst.Marshalers = src.Marshalers
   179  				}
   180  				if srcFlags.Has(jsonflags.Unmarshalers) {
   181  					dst.Unmarshalers = src.Unmarshalers
   182  				}
   183  			}
   184  		default:
   185  			JoinUnknownOption(dst, src)
   186  		}
   187  	}
   188  }
   189  
   190  type (
   191  	Indent       string // jsontext.WithIndent
   192  	IndentPrefix string // jsontext.WithIndentPrefix
   193  	ByteLimit    int64  // jsontext.WithByteLimit
   194  	DepthLimit   int    // jsontext.WithDepthLimit
   195  	// type for jsonflags.Marshalers declared in "json" package
   196  	// type for jsonflags.Unmarshalers declared in "json" package
   197  )
   198  
   199  func (Indent) JSONOptions(internal.NotForPublicUse)       {}
   200  func (IndentPrefix) JSONOptions(internal.NotForPublicUse) {}
   201  func (ByteLimit) JSONOptions(internal.NotForPublicUse)    {}
   202  func (DepthLimit) JSONOptions(internal.NotForPublicUse)   {}
   203  

View as plain text