@@ -5,7 +5,11 @@ package setting
55
66import (
77 "fmt"
8+ "reflect"
89
10+ jsonv2 "github.com/go-json-experiment/json"
11+ "github.com/go-json-experiment/json/jsontext"
12+ "tailscale.com/types/opt"
913 "tailscale.com/types/structs"
1014)
1115
@@ -17,10 +21,15 @@ import (
1721// or converted from strings, these setting types predate the typed policy
1822// hierarchies, and must be supported at this layer.
1923type RawItem struct {
20- _ structs.Incomparable
21- value any
22- err * ErrorText
23- origin * Origin // or nil
24+ _ structs.Incomparable
25+ data rawItemJSON
26+ }
27+
28+ // rawItemJSON holds JSON-marshallable data for [RawItem].
29+ type rawItemJSON struct {
30+ Value RawValue `json:",omitzero"`
31+ Error * ErrorText `json:",omitzero"` // or nil
32+ Origin * Origin `json:",omitzero"` // or nil
2433}
2534
2635// RawItemOf returns a [RawItem] with the specified value.
@@ -30,38 +39,124 @@ func RawItemOf(value any) RawItem {
3039
3140// RawItemWith returns a [RawItem] with the specified value, error and origin.
3241func RawItemWith (value any , err * ErrorText , origin * Origin ) RawItem {
33- return RawItem {value : value , err : err , origin : origin }
42+ return RawItem {data : rawItemJSON { Value : RawValue { opt . ValueOf ( value )}, Error : err , Origin : origin } }
3443}
3544
3645// Value returns the value of the policy setting, or nil if the policy setting
3746// is not configured, or an error occurred while reading it.
3847func (i RawItem ) Value () any {
39- return i .value
48+ return i .data . Value . Get ()
4049}
4150
4251// Error returns the error that occurred when reading the policy setting,
4352// or nil if no error occurred.
4453func (i RawItem ) Error () error {
45- if i .err != nil {
46- return i .err
54+ if i .data . Error != nil {
55+ return i .data . Error
4756 }
4857 return nil
4958}
5059
5160// Origin returns an optional [Origin] indicating where the policy setting is
5261// configured.
5362func (i RawItem ) Origin () * Origin {
54- return i .origin
63+ return i .data . Origin
5564}
5665
5766// String implements [fmt.Stringer].
5867func (i RawItem ) String () string {
5968 var suffix string
60- if i .origin != nil {
61- suffix = fmt .Sprintf (" - {%v}" , i .origin )
69+ if i .data .Origin != nil {
70+ suffix = fmt .Sprintf (" - {%v}" , i .data .Origin )
71+ }
72+ if i .data .Error != nil {
73+ return fmt .Sprintf ("Error{%q}%s" , i .data .Error .Error (), suffix )
74+ }
75+ return fmt .Sprintf ("%v%s" , i .data .Value .Value , suffix )
76+ }
77+
78+ // MarshalJSONV2 implements [jsonv2.MarshalerV2].
79+ func (i RawItem ) MarshalJSONV2 (out * jsontext.Encoder , opts jsonv2.Options ) error {
80+ return jsonv2 .MarshalEncode (out , & i .data , opts )
81+ }
82+
83+ // UnmarshalJSONV2 implements [jsonv2.UnmarshalerV2].
84+ func (i * RawItem ) UnmarshalJSONV2 (in * jsontext.Decoder , opts jsonv2.Options ) error {
85+ return jsonv2 .UnmarshalDecode (in , & i .data , opts )
86+ }
87+
88+ // MarshalJSON implements [json.Marshaler].
89+ func (i RawItem ) MarshalJSON () ([]byte , error ) {
90+ return jsonv2 .Marshal (i ) // uses MarshalJSONV2
91+ }
92+
93+ // UnmarshalJSON implements [json.Unmarshaler].
94+ func (i * RawItem ) UnmarshalJSON (b []byte ) error {
95+ return jsonv2 .Unmarshal (b , i ) // uses UnmarshalJSONV2
96+ }
97+
98+ // RawValue represents a raw policy setting value read from a policy store.
99+ // It is JSON-marshallable and facilitates unmarshalling of JSON values
100+ // into corresponding policy setting types, with special handling for JSON numbers
101+ // (unmarshalled as float64) and JSON string arrays (unmarshalled as []string).
102+ // See also [RawValue.UnmarshalJSONV2].
103+ type RawValue struct {
104+ opt.Value [any ]
105+ }
106+
107+ // RawValueType is a constraint that permits raw setting value types.
108+ type RawValueType interface {
109+ bool | uint64 | string | []string
110+ }
111+
112+ // RawValueOf returns a new [RawValue] holding the specified value.
113+ func RawValueOf [T RawValueType ](v T ) RawValue {
114+ return RawValue {opt.ValueOf [any ](v )}
115+ }
116+
117+ // MarshalJSONV2 implements [jsonv2.MarshalerV2].
118+ func (v RawValue ) MarshalJSONV2 (out * jsontext.Encoder , opts jsonv2.Options ) error {
119+ return jsonv2 .MarshalEncode (out , v .Value , opts )
120+ }
121+
122+ // UnmarshalJSONV2 implements [jsonv2.UnmarshalerV2] by attempting to unmarshal
123+ // a JSON value as one of the supported policy setting value types (bool, string, uint64, or []string),
124+ // based on the JSON value type. It fails if the JSON value is an object, if it's a JSON number that
125+ // cannot be represented as a uint64, or if a JSON array contains anything other than strings.
126+ func (v * RawValue ) UnmarshalJSONV2 (in * jsontext.Decoder , opts jsonv2.Options ) error {
127+ var valPtr any
128+ switch k := in .PeekKind (); k {
129+ case 't' , 'f' :
130+ valPtr = new (bool )
131+ case '"' :
132+ valPtr = new (string )
133+ case '0' :
134+ valPtr = new (uint64 ) // unmarshal JSON numbers as uint64
135+ case '[' , 'n' :
136+ valPtr = new ([]string ) // unmarshal arrays as string slices
137+ case '{' :
138+ return fmt .Errorf ("unexpected token: %v" , k )
139+ default :
140+ panic ("unreachable" )
62141 }
63- if i .err != nil {
64- return fmt .Sprintf ("Error{%q}%s" , i .err .Error (), suffix )
142+ if err := jsonv2 .UnmarshalDecode (in , valPtr , opts ); err != nil {
143+ v .Value .Clear ()
144+ return err
65145 }
66- return fmt .Sprintf ("%v%s" , i .value , suffix )
146+ value := reflect .ValueOf (valPtr ).Elem ().Interface ()
147+ v .Value = opt .ValueOf (value )
148+ return nil
149+ }
150+
151+ // MarshalJSON implements [json.Marshaler].
152+ func (v RawValue ) MarshalJSON () ([]byte , error ) {
153+ return jsonv2 .Marshal (v ) // uses MarshalJSONV2
67154}
155+
156+ // UnmarshalJSON implements [json.Unmarshaler].
157+ func (v * RawValue ) UnmarshalJSON (b []byte ) error {
158+ return jsonv2 .Unmarshal (b , v ) // uses UnmarshalJSONV2
159+ }
160+
161+ // RawValues is a map of keyed setting values that can be read from a JSON.
162+ type RawValues map [Key ]RawValue
0 commit comments