cmd/viewer, types/views: implement support for json/v2 (#16852)

This adds support for having every viewer type implement
jsonv2.MarshalerTo and jsonv2.UnmarshalerFrom.

This provides a significant boost in performance
as the json package no longer needs to validate
the entirety of the JSON value outputted by MarshalJSON,
nor does it need to identify the boundaries of a JSON value
in order to call UnmarshalJSON.

For deeply nested and recursive MarshalJSON or UnmarshalJSON calls,
this can improve runtime from O(N²) to O(N).

This still references "github.com/go-json-experiment/json"
instead of the experimental "encoding/json/v2" package
now available in Go 1.25 under goexperiment.jsonv2
so that code still builds without the experiment tag.
Of note, the "github.com/go-json-experiment/json" package
aliases the standard library under the right build conditions.

Updates tailscale/corp#791

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
pull/16780/head
Joe Tsai 4 months ago committed by GitHub
parent c083a9b053
commit fbb91758ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -136,13 +136,13 @@ func gen(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named) {
writef("if src.%s[i] == nil { dst.%s[i] = nil } else {", fname, fname) writef("if src.%s[i] == nil { dst.%s[i] = nil } else {", fname, fname)
if codegen.ContainsPointers(ptr.Elem()) { if codegen.ContainsPointers(ptr.Elem()) {
if _, isIface := ptr.Elem().Underlying().(*types.Interface); isIface { if _, isIface := ptr.Elem().Underlying().(*types.Interface); isIface {
it.Import("tailscale.com/types/ptr") it.Import("", "tailscale.com/types/ptr")
writef("\tdst.%s[i] = ptr.To((*src.%s[i]).Clone())", fname, fname) writef("\tdst.%s[i] = ptr.To((*src.%s[i]).Clone())", fname, fname)
} else { } else {
writef("\tdst.%s[i] = src.%s[i].Clone()", fname, fname) writef("\tdst.%s[i] = src.%s[i].Clone()", fname, fname)
} }
} else { } else {
it.Import("tailscale.com/types/ptr") it.Import("", "tailscale.com/types/ptr")
writef("\tdst.%s[i] = ptr.To(*src.%s[i])", fname, fname) writef("\tdst.%s[i] = ptr.To(*src.%s[i])", fname, fname)
} }
writef("}") writef("}")
@ -165,7 +165,7 @@ func gen(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named) {
writef("dst.%s = src.%s.Clone()", fname, fname) writef("dst.%s = src.%s.Clone()", fname, fname)
continue continue
} }
it.Import("tailscale.com/types/ptr") it.Import("", "tailscale.com/types/ptr")
writef("if dst.%s != nil {", fname) writef("if dst.%s != nil {", fname)
if _, isIface := base.Underlying().(*types.Interface); isIface && hasPtrs { if _, isIface := base.Underlying().(*types.Interface); isIface && hasPtrs {
writef("\tdst.%s = ptr.To((*src.%s).Clone())", fname, fname) writef("\tdst.%s = ptr.To((*src.%s).Clone())", fname, fname)
@ -197,13 +197,13 @@ func gen(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named) {
writef("\t\tif v == nil { dst.%s[k] = nil } else {", fname) writef("\t\tif v == nil { dst.%s[k] = nil } else {", fname)
if base := elem.Elem().Underlying(); codegen.ContainsPointers(base) { if base := elem.Elem().Underlying(); codegen.ContainsPointers(base) {
if _, isIface := base.(*types.Interface); isIface { if _, isIface := base.(*types.Interface); isIface {
it.Import("tailscale.com/types/ptr") it.Import("", "tailscale.com/types/ptr")
writef("\t\t\tdst.%s[k] = ptr.To((*v).Clone())", fname) writef("\t\t\tdst.%s[k] = ptr.To((*v).Clone())", fname)
} else { } else {
writef("\t\t\tdst.%s[k] = v.Clone()", fname) writef("\t\t\tdst.%s[k] = v.Clone()", fname)
} }
} else { } else {
it.Import("tailscale.com/types/ptr") it.Import("", "tailscale.com/types/ptr")
writef("\t\t\tdst.%s[k] = ptr.To(*v)", fname) writef("\t\t\tdst.%s[k] = ptr.To(*v)", fname)
} }
writef("}") writef("}")
@ -224,7 +224,7 @@ func gen(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named) {
writef("\t}") writef("\t}")
writef("}") writef("}")
} else { } else {
it.Import("maps") it.Import("", "maps")
writef("\tdst.%s = maps.Clone(src.%s)", fname, fname) writef("\tdst.%s = maps.Clone(src.%s)", fname, fname)
} }
case *types.Interface: case *types.Interface:

@ -2,7 +2,7 @@ tailscale.com/cmd/stund dependencies: (generated by github.com/tailscale/depawar
github.com/beorn7/perks/quantile from github.com/prometheus/client_golang/prometheus github.com/beorn7/perks/quantile from github.com/prometheus/client_golang/prometheus
💣 github.com/cespare/xxhash/v2 from github.com/prometheus/client_golang/prometheus 💣 github.com/cespare/xxhash/v2 from github.com/prometheus/client_golang/prometheus
github.com/go-json-experiment/json from tailscale.com/types/opt github.com/go-json-experiment/json from tailscale.com/types/opt+
github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+ github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+
github.com/go-json-experiment/json/internal/jsonflags from github.com/go-json-experiment/json+ github.com/go-json-experiment/json/internal/jsonflags from github.com/go-json-experiment/json+
github.com/go-json-experiment/json/internal/jsonopts from github.com/go-json-experiment/json+ github.com/go-json-experiment/json/internal/jsonopts from github.com/go-json-experiment/json+

@ -6,10 +6,12 @@
package tests package tests
import ( import (
"encoding/json" jsonv1 "encoding/json"
"errors" "errors"
"net/netip" "net/netip"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"golang.org/x/exp/constraints" "golang.org/x/exp/constraints"
"tailscale.com/types/views" "tailscale.com/types/views"
) )
@ -44,8 +46,17 @@ func (v StructWithPtrsView) AsStruct() *StructWithPtrs {
return v.ж.Clone() return v.ж.Clone()
} }
func (v StructWithPtrsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v StructWithPtrsView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v StructWithPtrsView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *StructWithPtrsView) UnmarshalJSON(b []byte) error { func (v *StructWithPtrsView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -54,7 +65,20 @@ func (v *StructWithPtrsView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x StructWithPtrs var x StructWithPtrs
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *StructWithPtrsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x StructWithPtrs
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -108,8 +132,17 @@ func (v StructWithoutPtrsView) AsStruct() *StructWithoutPtrs {
return v.ж.Clone() return v.ж.Clone()
} }
func (v StructWithoutPtrsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v StructWithoutPtrsView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v StructWithoutPtrsView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *StructWithoutPtrsView) UnmarshalJSON(b []byte) error { func (v *StructWithoutPtrsView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -118,7 +151,20 @@ func (v *StructWithoutPtrsView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x StructWithoutPtrs var x StructWithoutPtrs
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *StructWithoutPtrsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x StructWithoutPtrs
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -162,8 +208,17 @@ func (v MapView) AsStruct() *Map {
return v.ж.Clone() return v.ж.Clone()
} }
func (v MapView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v MapView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v MapView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *MapView) UnmarshalJSON(b []byte) error { func (v *MapView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -172,7 +227,20 @@ func (v *MapView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Map var x Map
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *MapView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Map
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -268,8 +336,17 @@ func (v StructWithSlicesView) AsStruct() *StructWithSlices {
return v.ж.Clone() return v.ж.Clone()
} }
func (v StructWithSlicesView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v StructWithSlicesView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v StructWithSlicesView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *StructWithSlicesView) UnmarshalJSON(b []byte) error { func (v *StructWithSlicesView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -278,7 +355,20 @@ func (v *StructWithSlicesView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x StructWithSlices var x StructWithSlices
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *StructWithSlicesView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x StructWithSlices
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -342,8 +432,17 @@ func (v StructWithEmbeddedView) AsStruct() *StructWithEmbedded {
return v.ж.Clone() return v.ж.Clone()
} }
func (v StructWithEmbeddedView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v StructWithEmbeddedView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v StructWithEmbeddedView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *StructWithEmbeddedView) UnmarshalJSON(b []byte) error { func (v *StructWithEmbeddedView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -352,7 +451,20 @@ func (v *StructWithEmbeddedView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x StructWithEmbedded var x StructWithEmbedded
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *StructWithEmbeddedView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x StructWithEmbedded
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -398,8 +510,17 @@ func (v GenericIntStructView[T]) AsStruct() *GenericIntStruct[T] {
return v.ж.Clone() return v.ж.Clone()
} }
func (v GenericIntStructView[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v GenericIntStructView[T]) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v GenericIntStructView[T]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *GenericIntStructView[T]) UnmarshalJSON(b []byte) error { func (v *GenericIntStructView[T]) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -408,7 +529,20 @@ func (v *GenericIntStructView[T]) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x GenericIntStruct[T] var x GenericIntStruct[T]
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *GenericIntStructView[T]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x GenericIntStruct[T]
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -470,8 +604,17 @@ func (v GenericNoPtrsStructView[T]) AsStruct() *GenericNoPtrsStruct[T] {
return v.ж.Clone() return v.ж.Clone()
} }
func (v GenericNoPtrsStructView[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v GenericNoPtrsStructView[T]) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v GenericNoPtrsStructView[T]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *GenericNoPtrsStructView[T]) UnmarshalJSON(b []byte) error { func (v *GenericNoPtrsStructView[T]) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -480,7 +623,20 @@ func (v *GenericNoPtrsStructView[T]) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x GenericNoPtrsStruct[T] var x GenericNoPtrsStruct[T]
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *GenericNoPtrsStructView[T]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x GenericNoPtrsStruct[T]
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -542,8 +698,17 @@ func (v GenericCloneableStructView[T, V]) AsStruct() *GenericCloneableStruct[T,
return v.ж.Clone() return v.ж.Clone()
} }
func (v GenericCloneableStructView[T, V]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v GenericCloneableStructView[T, V]) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v GenericCloneableStructView[T, V]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *GenericCloneableStructView[T, V]) UnmarshalJSON(b []byte) error { func (v *GenericCloneableStructView[T, V]) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -552,7 +717,20 @@ func (v *GenericCloneableStructView[T, V]) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x GenericCloneableStruct[T, V] var x GenericCloneableStruct[T, V]
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *GenericCloneableStructView[T, V]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x GenericCloneableStruct[T, V]
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -617,8 +795,17 @@ func (v StructWithContainersView) AsStruct() *StructWithContainers {
return v.ж.Clone() return v.ж.Clone()
} }
func (v StructWithContainersView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v StructWithContainersView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v StructWithContainersView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *StructWithContainersView) UnmarshalJSON(b []byte) error { func (v *StructWithContainersView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -627,7 +814,20 @@ func (v *StructWithContainersView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x StructWithContainers var x StructWithContainers
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *StructWithContainersView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x StructWithContainers
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -689,8 +889,17 @@ func (v StructWithTypeAliasFieldsView) AsStruct() *StructWithTypeAliasFields {
return v.ж.Clone() return v.ж.Clone()
} }
func (v StructWithTypeAliasFieldsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v StructWithTypeAliasFieldsView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v StructWithTypeAliasFieldsView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *StructWithTypeAliasFieldsView) UnmarshalJSON(b []byte) error { func (v *StructWithTypeAliasFieldsView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -699,7 +908,20 @@ func (v *StructWithTypeAliasFieldsView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x StructWithTypeAliasFields var x StructWithTypeAliasFields
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *StructWithTypeAliasFieldsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x StructWithTypeAliasFields
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -787,10 +1009,17 @@ func (v GenericTypeAliasStructView[T, T2, V2]) AsStruct() *GenericTypeAliasStruc
return v.ж.Clone() return v.ж.Clone()
} }
// MarshalJSON implements [jsonv1.Marshaler].
func (v GenericTypeAliasStructView[T, T2, V2]) MarshalJSON() ([]byte, error) { func (v GenericTypeAliasStructView[T, T2, V2]) MarshalJSON() ([]byte, error) {
return json.Marshal(v.ж) return jsonv1.Marshal(v.ж)
} }
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v GenericTypeAliasStructView[T, T2, V2]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *GenericTypeAliasStructView[T, T2, V2]) UnmarshalJSON(b []byte) error { func (v *GenericTypeAliasStructView[T, T2, V2]) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -799,7 +1028,20 @@ func (v *GenericTypeAliasStructView[T, T2, V2]) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x GenericTypeAliasStruct[T, T2, V2] var x GenericTypeAliasStruct[T, T2, V2]
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *GenericTypeAliasStructView[T, T2, V2]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x GenericTypeAliasStruct[T, T2, V2]
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x

@ -49,8 +49,17 @@ func (v {{.ViewName}}{{.TypeParamNames}}) AsStruct() *{{.StructName}}{{.TypePara
return v.ж.Clone() return v.ж.Clone()
} }
func (v {{.ViewName}}{{.TypeParamNames}}) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v {{.ViewName}}{{.TypeParamNames}}) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v {{.ViewName}}{{.TypeParamNames}}) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *{{.ViewName}}{{.TypeParamNames}}) UnmarshalJSON(b []byte) error { func (v *{{.ViewName}}{{.TypeParamNames}}) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -59,10 +68,23 @@ func (v *{{.ViewName}}{{.TypeParamNames}}) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x {{.StructName}}{{.TypeParamNames}} var x {{.StructName}}{{.TypeParamNames}}
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *{{.ViewName}}{{.TypeParamNames}}) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x {{.StructName}}{{.TypeParamNames}}
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж=&x v.ж = &x
return nil return nil
} }
@ -125,8 +147,10 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
if !ok || codegen.IsViewType(t) { if !ok || codegen.IsViewType(t) {
return return
} }
it.Import("encoding/json") it.Import("jsonv1", "encoding/json")
it.Import("errors") it.Import("jsonv2", "github.com/go-json-experiment/json")
it.Import("", "github.com/go-json-experiment/json/jsontext")
it.Import("", "errors")
args := struct { args := struct {
StructName string StructName string
@ -182,11 +206,11 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
switch elem.String() { switch elem.String() {
case "byte": case "byte":
args.FieldType = it.QualifiedName(fieldType) args.FieldType = it.QualifiedName(fieldType)
it.Import("tailscale.com/types/views") it.Import("", "tailscale.com/types/views")
writeTemplate("byteSliceField") writeTemplate("byteSliceField")
default: default:
args.FieldType = it.QualifiedName(elem) args.FieldType = it.QualifiedName(elem)
it.Import("tailscale.com/types/views") it.Import("", "tailscale.com/types/views")
shallow, deep, base := requiresCloning(elem) shallow, deep, base := requiresCloning(elem)
if deep { if deep {
switch elem.Underlying().(type) { switch elem.Underlying().(type) {
@ -252,7 +276,7 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
writeTemplate("unsupportedField") writeTemplate("unsupportedField")
continue continue
} }
it.Import("tailscale.com/types/views") it.Import("", "tailscale.com/types/views")
args.MapKeyType = it.QualifiedName(key) args.MapKeyType = it.QualifiedName(key)
mElem := m.Elem() mElem := m.Elem()
var template string var template string

@ -20,19 +20,19 @@ func TestViewerImports(t *testing.T) {
name string name string
content string content string
typeNames []string typeNames []string
wantImports []string wantImports [][2]string
}{ }{
{ {
name: "Map", name: "Map",
content: `type Test struct { Map map[string]int }`, content: `type Test struct { Map map[string]int }`,
typeNames: []string{"Test"}, typeNames: []string{"Test"},
wantImports: []string{"tailscale.com/types/views"}, wantImports: [][2]string{{"", "tailscale.com/types/views"}},
}, },
{ {
name: "Slice", name: "Slice",
content: `type Test struct { Slice []int }`, content: `type Test struct { Slice []int }`,
typeNames: []string{"Test"}, typeNames: []string{"Test"},
wantImports: []string{"tailscale.com/types/views"}, wantImports: [][2]string{{"", "tailscale.com/types/views"}},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -68,9 +68,9 @@ func TestViewerImports(t *testing.T) {
genView(&output, tracker, namedType, pkg) genView(&output, tracker, namedType, pkg)
} }
for _, pkgName := range tt.wantImports { for _, pkg := range tt.wantImports {
if !tracker.Has(pkgName) { if !tracker.Has(pkg[0], pkg[1]) {
t.Errorf("missing import %q", pkgName) t.Errorf("missing import %q", pkg)
} }
} }
}) })

@ -6,9 +6,11 @@
package drive package drive
import ( import (
"encoding/json" jsonv1 "encoding/json"
"errors" "errors"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"tailscale.com/types/views" "tailscale.com/types/views"
) )
@ -42,8 +44,17 @@ func (v ShareView) AsStruct() *Share {
return v.ж.Clone() return v.ж.Clone()
} }
func (v ShareView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v ShareView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v ShareView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *ShareView) UnmarshalJSON(b []byte) error { func (v *ShareView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -52,7 +63,20 @@ func (v *ShareView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Share var x Share
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *ShareView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Share
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x

@ -33,7 +33,7 @@ require (
github.com/frankban/quicktest v1.14.6 github.com/frankban/quicktest v1.14.6
github.com/fxamacker/cbor/v2 v2.7.0 github.com/fxamacker/cbor/v2 v2.7.0
github.com/gaissmai/bart v0.18.0 github.com/gaissmai/bart v0.18.0
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced
github.com/go-logr/zapr v1.3.0 github.com/go-logr/zapr v1.3.0
github.com/go-ole/go-ole v1.3.0 github.com/go-ole/go-ole v1.3.0
github.com/go4org/plan9netshell v0.0.0-20250324183649-788daa080737 github.com/go4org/plan9netshell v0.0.0-20250324183649-788daa080737

@ -345,8 +345,8 @@ github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0q
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 h1:F8d1AJ6M9UQCavhwmO6ZsrYLfG8zVFWfEfMS2MXPkSY= github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced h1:Q311OHjMh/u5E2TITc++WlTP5We0xNseRMkHDyvhW7I=
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=

@ -6,10 +6,12 @@
package ipn package ipn
import ( import (
"encoding/json" jsonv1 "encoding/json"
"errors" "errors"
"net/netip" "net/netip"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"tailscale.com/drive" "tailscale.com/drive"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/opt" "tailscale.com/types/opt"
@ -48,8 +50,17 @@ func (v LoginProfileView) AsStruct() *LoginProfile {
return v.ж.Clone() return v.ж.Clone()
} }
func (v LoginProfileView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v LoginProfileView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v LoginProfileView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *LoginProfileView) UnmarshalJSON(b []byte) error { func (v *LoginProfileView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -58,7 +69,20 @@ func (v *LoginProfileView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x LoginProfile var x LoginProfile
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *LoginProfileView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x LoginProfile
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -114,8 +138,17 @@ func (v PrefsView) AsStruct() *Prefs {
return v.ж.Clone() return v.ж.Clone()
} }
func (v PrefsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v PrefsView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v PrefsView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *PrefsView) UnmarshalJSON(b []byte) error { func (v *PrefsView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -124,7 +157,20 @@ func (v *PrefsView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Prefs var x Prefs
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *PrefsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Prefs
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -239,8 +285,17 @@ func (v ServeConfigView) AsStruct() *ServeConfig {
return v.ж.Clone() return v.ж.Clone()
} }
func (v ServeConfigView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v ServeConfigView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v ServeConfigView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *ServeConfigView) UnmarshalJSON(b []byte) error { func (v *ServeConfigView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -249,7 +304,20 @@ func (v *ServeConfigView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x ServeConfig var x ServeConfig
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *ServeConfigView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x ServeConfig
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -323,8 +391,17 @@ func (v ServiceConfigView) AsStruct() *ServiceConfig {
return v.ж.Clone() return v.ж.Clone()
} }
func (v ServiceConfigView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v ServiceConfigView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v ServiceConfigView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *ServiceConfigView) UnmarshalJSON(b []byte) error { func (v *ServiceConfigView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -333,7 +410,20 @@ func (v *ServiceConfigView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x ServiceConfig var x ServiceConfig
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *ServiceConfigView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x ServiceConfig
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -388,8 +478,17 @@ func (v TCPPortHandlerView) AsStruct() *TCPPortHandler {
return v.ж.Clone() return v.ж.Clone()
} }
func (v TCPPortHandlerView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v TCPPortHandlerView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v TCPPortHandlerView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *TCPPortHandlerView) UnmarshalJSON(b []byte) error { func (v *TCPPortHandlerView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -398,7 +497,20 @@ func (v *TCPPortHandlerView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x TCPPortHandler var x TCPPortHandler
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *TCPPortHandlerView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x TCPPortHandler
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -446,8 +558,17 @@ func (v HTTPHandlerView) AsStruct() *HTTPHandler {
return v.ж.Clone() return v.ж.Clone()
} }
func (v HTTPHandlerView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v HTTPHandlerView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v HTTPHandlerView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *HTTPHandlerView) UnmarshalJSON(b []byte) error { func (v *HTTPHandlerView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -456,7 +577,20 @@ func (v *HTTPHandlerView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x HTTPHandler var x HTTPHandler
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *HTTPHandlerView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x HTTPHandler
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -502,8 +636,17 @@ func (v WebServerConfigView) AsStruct() *WebServerConfig {
return v.ж.Clone() return v.ж.Clone()
} }
func (v WebServerConfigView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v WebServerConfigView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v WebServerConfigView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *WebServerConfigView) UnmarshalJSON(b []byte) error { func (v *WebServerConfigView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -512,7 +655,20 @@ func (v *WebServerConfigView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x WebServerConfig var x WebServerConfig
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *WebServerConfigView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x WebServerConfig
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x

@ -6,11 +6,13 @@
package tailcfg package tailcfg
import ( import (
"encoding/json" jsonv1 "encoding/json"
"errors" "errors"
"net/netip" "net/netip"
"time" "time"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"tailscale.com/types/dnstype" "tailscale.com/types/dnstype"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/opt" "tailscale.com/types/opt"
@ -49,8 +51,17 @@ func (v UserView) AsStruct() *User {
return v.ж.Clone() return v.ж.Clone()
} }
func (v UserView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v UserView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v UserView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *UserView) UnmarshalJSON(b []byte) error { func (v *UserView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -59,7 +70,20 @@ func (v *UserView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x User var x User
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *UserView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x User
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -107,8 +131,17 @@ func (v NodeView) AsStruct() *Node {
return v.ж.Clone() return v.ж.Clone()
} }
func (v NodeView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v NodeView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v NodeView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *NodeView) UnmarshalJSON(b []byte) error { func (v *NodeView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -117,7 +150,20 @@ func (v *NodeView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Node var x Node
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *NodeView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Node
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -246,8 +292,17 @@ func (v HostinfoView) AsStruct() *Hostinfo {
return v.ж.Clone() return v.ж.Clone()
} }
func (v HostinfoView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v HostinfoView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v HostinfoView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *HostinfoView) UnmarshalJSON(b []byte) error { func (v *HostinfoView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -256,7 +311,20 @@ func (v *HostinfoView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Hostinfo var x Hostinfo
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *HostinfoView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Hostinfo
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -380,8 +448,17 @@ func (v NetInfoView) AsStruct() *NetInfo {
return v.ж.Clone() return v.ж.Clone()
} }
func (v NetInfoView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v NetInfoView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v NetInfoView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *NetInfoView) UnmarshalJSON(b []byte) error { func (v *NetInfoView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -390,7 +467,20 @@ func (v *NetInfoView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x NetInfo var x NetInfo
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *NetInfoView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x NetInfo
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -460,8 +550,17 @@ func (v LoginView) AsStruct() *Login {
return v.ж.Clone() return v.ж.Clone()
} }
func (v LoginView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v LoginView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v LoginView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *LoginView) UnmarshalJSON(b []byte) error { func (v *LoginView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -470,7 +569,20 @@ func (v *LoginView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Login var x Login
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *LoginView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Login
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -521,8 +633,17 @@ func (v DNSConfigView) AsStruct() *DNSConfig {
return v.ж.Clone() return v.ж.Clone()
} }
func (v DNSConfigView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v DNSConfigView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v DNSConfigView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *DNSConfigView) UnmarshalJSON(b []byte) error { func (v *DNSConfigView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -531,7 +652,20 @@ func (v *DNSConfigView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x DNSConfig var x DNSConfig
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *DNSConfigView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x DNSConfig
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -602,8 +736,17 @@ func (v RegisterResponseView) AsStruct() *RegisterResponse {
return v.ж.Clone() return v.ж.Clone()
} }
func (v RegisterResponseView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v RegisterResponseView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v RegisterResponseView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *RegisterResponseView) UnmarshalJSON(b []byte) error { func (v *RegisterResponseView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -612,7 +755,20 @@ func (v *RegisterResponseView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x RegisterResponse var x RegisterResponse
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *RegisterResponseView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x RegisterResponse
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -668,8 +824,17 @@ func (v RegisterResponseAuthView) AsStruct() *RegisterResponseAuth {
return v.ж.Clone() return v.ж.Clone()
} }
func (v RegisterResponseAuthView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v RegisterResponseAuthView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v RegisterResponseAuthView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *RegisterResponseAuthView) UnmarshalJSON(b []byte) error { func (v *RegisterResponseAuthView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -678,7 +843,20 @@ func (v *RegisterResponseAuthView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x RegisterResponseAuth var x RegisterResponseAuth
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *RegisterResponseAuthView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x RegisterResponseAuth
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -726,8 +904,17 @@ func (v RegisterRequestView) AsStruct() *RegisterRequest {
return v.ж.Clone() return v.ж.Clone()
} }
func (v RegisterRequestView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v RegisterRequestView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v RegisterRequestView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *RegisterRequestView) UnmarshalJSON(b []byte) error { func (v *RegisterRequestView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -736,7 +923,20 @@ func (v *RegisterRequestView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x RegisterRequest var x RegisterRequest
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *RegisterRequestView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x RegisterRequest
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -816,8 +1016,17 @@ func (v DERPHomeParamsView) AsStruct() *DERPHomeParams {
return v.ж.Clone() return v.ж.Clone()
} }
func (v DERPHomeParamsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v DERPHomeParamsView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v DERPHomeParamsView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *DERPHomeParamsView) UnmarshalJSON(b []byte) error { func (v *DERPHomeParamsView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -826,7 +1035,20 @@ func (v *DERPHomeParamsView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x DERPHomeParams var x DERPHomeParams
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *DERPHomeParamsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x DERPHomeParams
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -870,8 +1092,17 @@ func (v DERPRegionView) AsStruct() *DERPRegion {
return v.ж.Clone() return v.ж.Clone()
} }
func (v DERPRegionView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v DERPRegionView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v DERPRegionView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *DERPRegionView) UnmarshalJSON(b []byte) error { func (v *DERPRegionView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -880,7 +1111,20 @@ func (v *DERPRegionView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x DERPRegion var x DERPRegion
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *DERPRegionView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x DERPRegion
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -938,8 +1182,17 @@ func (v DERPMapView) AsStruct() *DERPMap {
return v.ж.Clone() return v.ж.Clone()
} }
func (v DERPMapView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v DERPMapView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v DERPMapView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *DERPMapView) UnmarshalJSON(b []byte) error { func (v *DERPMapView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -948,7 +1201,20 @@ func (v *DERPMapView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x DERPMap var x DERPMap
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *DERPMapView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x DERPMap
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -999,8 +1265,17 @@ func (v DERPNodeView) AsStruct() *DERPNode {
return v.ж.Clone() return v.ж.Clone()
} }
func (v DERPNodeView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v DERPNodeView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v DERPNodeView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *DERPNodeView) UnmarshalJSON(b []byte) error { func (v *DERPNodeView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -1009,7 +1284,20 @@ func (v *DERPNodeView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x DERPNode var x DERPNode
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *DERPNodeView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x DERPNode
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -1073,8 +1361,17 @@ func (v SSHRuleView) AsStruct() *SSHRule {
return v.ж.Clone() return v.ж.Clone()
} }
func (v SSHRuleView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v SSHRuleView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v SSHRuleView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *SSHRuleView) UnmarshalJSON(b []byte) error { func (v *SSHRuleView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -1083,7 +1380,20 @@ func (v *SSHRuleView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x SSHRule var x SSHRule
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *SSHRuleView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x SSHRule
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -1139,8 +1449,17 @@ func (v SSHActionView) AsStruct() *SSHAction {
return v.ж.Clone() return v.ж.Clone()
} }
func (v SSHActionView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v SSHActionView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v SSHActionView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *SSHActionView) UnmarshalJSON(b []byte) error { func (v *SSHActionView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -1149,7 +1468,20 @@ func (v *SSHActionView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x SSHAction var x SSHAction
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *SSHActionView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x SSHAction
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -1211,8 +1543,17 @@ func (v SSHPrincipalView) AsStruct() *SSHPrincipal {
return v.ж.Clone() return v.ж.Clone()
} }
func (v SSHPrincipalView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v SSHPrincipalView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v SSHPrincipalView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *SSHPrincipalView) UnmarshalJSON(b []byte) error { func (v *SSHPrincipalView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -1221,7 +1562,20 @@ func (v *SSHPrincipalView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x SSHPrincipal var x SSHPrincipal
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *SSHPrincipalView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x SSHPrincipal
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -1273,8 +1627,17 @@ func (v ControlDialPlanView) AsStruct() *ControlDialPlan {
return v.ж.Clone() return v.ж.Clone()
} }
func (v ControlDialPlanView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v ControlDialPlanView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v ControlDialPlanView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *ControlDialPlanView) UnmarshalJSON(b []byte) error { func (v *ControlDialPlanView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -1283,7 +1646,20 @@ func (v *ControlDialPlanView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x ControlDialPlan var x ControlDialPlan
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *ControlDialPlanView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x ControlDialPlan
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -1327,8 +1703,17 @@ func (v LocationView) AsStruct() *Location {
return v.ж.Clone() return v.ж.Clone()
} }
func (v LocationView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v LocationView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v LocationView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *LocationView) UnmarshalJSON(b []byte) error { func (v *LocationView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -1337,7 +1722,20 @@ func (v *LocationView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Location var x Location
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *LocationView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Location
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -1391,8 +1789,17 @@ func (v UserProfileView) AsStruct() *UserProfile {
return v.ж.Clone() return v.ж.Clone()
} }
func (v UserProfileView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v UserProfileView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v UserProfileView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *UserProfileView) UnmarshalJSON(b []byte) error { func (v *UserProfileView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -1401,7 +1808,20 @@ func (v *UserProfileView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x UserProfile var x UserProfile
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *UserProfileView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x UserProfile
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -1450,8 +1870,17 @@ func (v VIPServiceView) AsStruct() *VIPService {
return v.ж.Clone() return v.ж.Clone()
} }
func (v VIPServiceView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v VIPServiceView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v VIPServiceView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *VIPServiceView) UnmarshalJSON(b []byte) error { func (v *VIPServiceView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -1460,7 +1889,20 @@ func (v *VIPServiceView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x VIPService var x VIPService
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *VIPServiceView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x VIPService
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x

@ -6,10 +6,12 @@
package dnstype package dnstype
import ( import (
"encoding/json" jsonv1 "encoding/json"
"errors" "errors"
"net/netip" "net/netip"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"tailscale.com/types/views" "tailscale.com/types/views"
) )
@ -43,8 +45,17 @@ func (v ResolverView) AsStruct() *Resolver {
return v.ж.Clone() return v.ж.Clone()
} }
func (v ResolverView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v ResolverView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v ResolverView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *ResolverView) UnmarshalJSON(b []byte) error { func (v *ResolverView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -53,7 +64,20 @@ func (v *ResolverView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Resolver var x Resolver
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *ResolverView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Resolver
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x

@ -6,9 +6,11 @@
package persist package persist
import ( import (
"encoding/json" jsonv1 "encoding/json"
"errors" "errors"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/structs" "tailscale.com/types/structs"
@ -45,8 +47,17 @@ func (v PersistView) AsStruct() *Persist {
return v.ж.Clone() return v.ж.Clone()
} }
func (v PersistView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v PersistView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v PersistView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *PersistView) UnmarshalJSON(b []byte) error { func (v *PersistView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -55,7 +66,20 @@ func (v *PersistView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Persist var x Persist
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *PersistView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Persist
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x

@ -6,10 +6,12 @@
package prefs_example package prefs_example
import ( import (
"encoding/json" jsonv1 "encoding/json"
"errors" "errors"
"net/netip" "net/netip"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"tailscale.com/drive" "tailscale.com/drive"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/opt" "tailscale.com/types/opt"
@ -48,8 +50,17 @@ func (v PrefsView) AsStruct() *Prefs {
return v.ж.Clone() return v.ж.Clone()
} }
func (v PrefsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v PrefsView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v PrefsView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *PrefsView) UnmarshalJSON(b []byte) error { func (v *PrefsView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -58,7 +69,20 @@ func (v *PrefsView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x Prefs var x Prefs
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *PrefsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x Prefs
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -160,8 +184,17 @@ func (v AutoUpdatePrefsView) AsStruct() *AutoUpdatePrefs {
return v.ж.Clone() return v.ж.Clone()
} }
func (v AutoUpdatePrefsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v AutoUpdatePrefsView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v AutoUpdatePrefsView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *AutoUpdatePrefsView) UnmarshalJSON(b []byte) error { func (v *AutoUpdatePrefsView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -170,7 +203,20 @@ func (v *AutoUpdatePrefsView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x AutoUpdatePrefs var x AutoUpdatePrefs
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *AutoUpdatePrefsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x AutoUpdatePrefs
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -214,8 +260,17 @@ func (v AppConnectorPrefsView) AsStruct() *AppConnectorPrefs {
return v.ж.Clone() return v.ж.Clone()
} }
func (v AppConnectorPrefsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v AppConnectorPrefsView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v AppConnectorPrefsView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *AppConnectorPrefsView) UnmarshalJSON(b []byte) error { func (v *AppConnectorPrefsView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -224,7 +279,20 @@ func (v *AppConnectorPrefsView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x AppConnectorPrefs var x AppConnectorPrefs
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *AppConnectorPrefsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x AppConnectorPrefs
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x

@ -6,9 +6,12 @@
package prefs package prefs
import ( import (
"encoding/json" jsonv1 "encoding/json"
"errors" "errors"
"net/netip" "net/netip"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
) )
//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=TestPrefs,TestBundle,TestValueStruct,TestGenericStruct,TestPrefsGroup -tags=test //go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=TestPrefs,TestBundle,TestValueStruct,TestGenericStruct,TestPrefsGroup -tags=test
@ -41,8 +44,17 @@ func (v TestPrefsView) AsStruct() *TestPrefs {
return v.ж.Clone() return v.ж.Clone()
} }
func (v TestPrefsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v TestPrefsView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v TestPrefsView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *TestPrefsView) UnmarshalJSON(b []byte) error { func (v *TestPrefsView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -51,7 +63,20 @@ func (v *TestPrefsView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x TestPrefs var x TestPrefs
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *TestPrefsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x TestPrefs
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -145,8 +170,17 @@ func (v TestBundleView) AsStruct() *TestBundle {
return v.ж.Clone() return v.ж.Clone()
} }
func (v TestBundleView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v TestBundleView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v TestBundleView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *TestBundleView) UnmarshalJSON(b []byte) error { func (v *TestBundleView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -155,7 +189,20 @@ func (v *TestBundleView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x TestBundle var x TestBundle
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *TestBundleView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x TestBundle
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -200,8 +247,17 @@ func (v TestValueStructView) AsStruct() *TestValueStruct {
return v.ж.Clone() return v.ж.Clone()
} }
func (v TestValueStructView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v TestValueStructView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v TestValueStructView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *TestValueStructView) UnmarshalJSON(b []byte) error { func (v *TestValueStructView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -210,7 +266,20 @@ func (v *TestValueStructView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x TestValueStruct var x TestValueStruct
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *TestValueStructView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x TestValueStruct
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -253,8 +322,17 @@ func (v TestGenericStructView[T]) AsStruct() *TestGenericStruct[T] {
return v.ж.Clone() return v.ж.Clone()
} }
func (v TestGenericStructView[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v TestGenericStructView[T]) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v TestGenericStructView[T]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *TestGenericStructView[T]) UnmarshalJSON(b []byte) error { func (v *TestGenericStructView[T]) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -263,7 +341,20 @@ func (v *TestGenericStructView[T]) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x TestGenericStruct[T] var x TestGenericStruct[T]
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *TestGenericStructView[T]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x TestGenericStruct[T]
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x
@ -308,8 +399,17 @@ func (v TestPrefsGroupView) AsStruct() *TestPrefsGroup {
return v.ж.Clone() return v.ж.Clone()
} }
func (v TestPrefsGroupView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // MarshalJSON implements [jsonv1.Marshaler].
func (v TestPrefsGroupView) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v TestPrefsGroupView) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *TestPrefsGroupView) UnmarshalJSON(b []byte) error { func (v *TestPrefsGroupView) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
@ -318,7 +418,20 @@ func (v *TestPrefsGroupView) UnmarshalJSON(b []byte) error {
return nil return nil
} }
var x TestPrefsGroup var x TestPrefsGroup
if err := json.Unmarshal(b, &x); err != nil { if err := jsonv1.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (v *TestPrefsGroupView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
var x TestPrefsGroup
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
return err return err
} }
v.ж = &x v.ж = &x

@ -7,7 +7,7 @@ package views
import ( import (
"bytes" "bytes"
"encoding/json" jsonv1 "encoding/json"
"errors" "errors"
"fmt" "fmt"
"iter" "iter"
@ -15,20 +15,12 @@ import (
"reflect" "reflect"
"slices" "slices"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"go4.org/mem" "go4.org/mem"
"tailscale.com/types/ptr" "tailscale.com/types/ptr"
) )
func unmarshalSliceFromJSON[T any](b []byte, x *[]T) error {
if *x != nil {
return errors.New("already initialized")
}
if len(b) == 0 {
return nil
}
return json.Unmarshal(b, x)
}
// ByteSlice is a read-only accessor for types that are backed by a []byte. // ByteSlice is a read-only accessor for types that are backed by a []byte.
type ByteSlice[T ~[]byte] struct { type ByteSlice[T ~[]byte] struct {
// ж is the underlying mutable value, named with a hard-to-type // ж is the underlying mutable value, named with a hard-to-type
@ -93,15 +85,32 @@ func (v ByteSlice[T]) SliceTo(i int) ByteSlice[T] { return ByteSlice[T]{v.ж[:i]
// Slice returns v[i:j] // Slice returns v[i:j]
func (v ByteSlice[T]) Slice(i, j int) ByteSlice[T] { return ByteSlice[T]{v.ж[i:j]} } func (v ByteSlice[T]) Slice(i, j int) ByteSlice[T] { return ByteSlice[T]{v.ж[i:j]} }
// MarshalJSON implements json.Marshaler. // MarshalJSON implements [jsonv1.Marshaler].
func (v ByteSlice[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } func (v ByteSlice[T]) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// UnmarshalJSON implements json.Unmarshaler. // MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v ByteSlice[T]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
// It must only be called on an uninitialized ByteSlice.
func (v *ByteSlice[T]) UnmarshalJSON(b []byte) error { func (v *ByteSlice[T]) UnmarshalJSON(b []byte) error {
if v.ж != nil { if v.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
} }
return json.Unmarshal(b, &v.ж) return jsonv1.Unmarshal(b, &v.ж)
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
// It must only be called on an uninitialized ByteSlice.
func (v *ByteSlice[T]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
return jsonv2.UnmarshalDecode(dec, &v.ж)
} }
// StructView represents the corresponding StructView of a Viewable. The concrete types are // StructView represents the corresponding StructView of a Viewable. The concrete types are
@ -159,11 +168,35 @@ func (v SliceView[T, V]) All() iter.Seq2[int, V] {
} }
} }
// MarshalJSON implements json.Marshaler. // MarshalJSON implements [jsonv1.Marshaler].
func (v SliceView[T, V]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } func (v SliceView[T, V]) MarshalJSON() ([]byte, error) {
return jsonv1.Marshal(v.ж)
}
// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v SliceView[T, V]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements json.Unmarshaler. // UnmarshalJSON implements [jsonv1.Unmarshaler].
func (v *SliceView[T, V]) UnmarshalJSON(b []byte) error { return unmarshalSliceFromJSON(b, &v.ж) } // It must only be called on an uninitialized SliceView.
func (v *SliceView[T, V]) UnmarshalJSON(b []byte) error {
if v.ж != nil {
return errors.New("already initialized")
} else if len(b) == 0 {
return nil
}
return jsonv1.Unmarshal(b, &v.ж)
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
// It must only be called on an uninitialized SliceView.
func (v *SliceView[T, V]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
return jsonv2.UnmarshalDecode(dec, &v.ж)
}
// IsNil reports whether the underlying slice is nil. // IsNil reports whether the underlying slice is nil.
func (v SliceView[T, V]) IsNil() bool { return v.ж == nil } func (v SliceView[T, V]) IsNil() bool { return v.ж == nil }
@ -252,14 +285,34 @@ func SliceOf[T any](x []T) Slice[T] {
return Slice[T]{x} return Slice[T]{x}
} }
// MarshalJSON implements json.Marshaler. // MarshalJSON implements [jsonv1.Marshaler].
func (v Slice[T]) MarshalJSON() ([]byte, error) { func (v Slice[T]) MarshalJSON() ([]byte, error) {
return json.Marshal(v.ж) return jsonv1.Marshal(v.ж)
} }
// UnmarshalJSON implements json.Unmarshaler. // MarshalJSONTo implements [jsonv2.MarshalerTo].
func (v Slice[T]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, v.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
// It must only be called on an uninitialized Slice.
func (v *Slice[T]) UnmarshalJSON(b []byte) error { func (v *Slice[T]) UnmarshalJSON(b []byte) error {
return unmarshalSliceFromJSON(b, &v.ж) if v.ж != nil {
return errors.New("already initialized")
} else if len(b) == 0 {
return nil
}
return jsonv1.Unmarshal(b, &v.ж)
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
// It must only be called on an uninitialized Slice.
func (v *Slice[T]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if v.ж != nil {
return errors.New("already initialized")
}
return jsonv2.UnmarshalDecode(dec, &v.ж)
} }
// IsNil reports whether the underlying slice is nil. // IsNil reports whether the underlying slice is nil.
@ -512,18 +565,32 @@ func (m MapSlice[K, V]) GetOk(k K) (Slice[V], bool) {
return SliceOf(v), ok return SliceOf(v), ok
} }
// MarshalJSON implements json.Marshaler. // MarshalJSON implements [jsonv1.Marshaler].
func (m MapSlice[K, V]) MarshalJSON() ([]byte, error) { func (m MapSlice[K, V]) MarshalJSON() ([]byte, error) {
return json.Marshal(m.ж) return jsonv1.Marshal(m.ж)
} }
// UnmarshalJSON implements json.Unmarshaler. // MarshalJSONTo implements [jsonv2.MarshalerTo].
func (m MapSlice[K, V]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, m.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
// It should only be called on an uninitialized Map. // It should only be called on an uninitialized Map.
func (m *MapSlice[K, V]) UnmarshalJSON(b []byte) error { func (m *MapSlice[K, V]) UnmarshalJSON(b []byte) error {
if m.ж != nil { if m.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
} }
return json.Unmarshal(b, &m.ж) return jsonv1.Unmarshal(b, &m.ж)
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
// It should only be called on an uninitialized MapSlice.
func (m *MapSlice[K, V]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if m.ж != nil {
return errors.New("already initialized")
}
return jsonv2.UnmarshalDecode(dec, &m.ж)
} }
// AsMap returns a shallow-clone of the underlying map. // AsMap returns a shallow-clone of the underlying map.
@ -600,18 +667,32 @@ func (m Map[K, V]) GetOk(k K) (V, bool) {
return v, ok return v, ok
} }
// MarshalJSON implements json.Marshaler. // MarshalJSON implements [jsonv1.Marshaler].
func (m Map[K, V]) MarshalJSON() ([]byte, error) { func (m Map[K, V]) MarshalJSON() ([]byte, error) {
return json.Marshal(m.ж) return jsonv1.Marshal(m.ж)
} }
// UnmarshalJSON implements json.Unmarshaler. // MarshalJSONTo implements [jsonv2.MarshalerTo].
func (m Map[K, V]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, m.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
// It should only be called on an uninitialized Map. // It should only be called on an uninitialized Map.
func (m *Map[K, V]) UnmarshalJSON(b []byte) error { func (m *Map[K, V]) UnmarshalJSON(b []byte) error {
if m.ж != nil { if m.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
} }
return json.Unmarshal(b, &m.ж) return jsonv1.Unmarshal(b, &m.ж)
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
// It must only be called on an uninitialized Map.
func (m *Map[K, V]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if m.ж != nil {
return errors.New("already initialized")
}
return jsonv2.UnmarshalDecode(dec, &m.ж)
} }
// AsMap returns a shallow-clone of the underlying map. // AsMap returns a shallow-clone of the underlying map.
@ -809,17 +890,32 @@ func ValuePointerOf[T any](v *T) ValuePointer[T] {
return ValuePointer[T]{v} return ValuePointer[T]{v}
} }
// MarshalJSON implements [json.Marshaler]. // MarshalJSON implements [jsonv1.Marshaler].
func (p ValuePointer[T]) MarshalJSON() ([]byte, error) { func (p ValuePointer[T]) MarshalJSON() ([]byte, error) {
return json.Marshal(p.ж) return jsonv1.Marshal(p.ж)
} }
// UnmarshalJSON implements [json.Unmarshaler]. // MarshalJSONTo implements [jsonv2.MarshalerTo].
func (p ValuePointer[T]) MarshalJSONTo(enc *jsontext.Encoder) error {
return jsonv2.MarshalEncode(enc, p.ж)
}
// UnmarshalJSON implements [jsonv1.Unmarshaler].
// It must only be called on an uninitialized ValuePointer.
func (p *ValuePointer[T]) UnmarshalJSON(b []byte) error { func (p *ValuePointer[T]) UnmarshalJSON(b []byte) error {
if p.ж != nil { if p.ж != nil {
return errors.New("already initialized") return errors.New("already initialized")
} }
return json.Unmarshal(b, &p.ж) return jsonv1.Unmarshal(b, &p.ж)
}
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
// It must only be called on an uninitialized ValuePointer.
func (p *ValuePointer[T]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
if p.ж != nil {
return errors.New("already initialized")
}
return jsonv2.UnmarshalDecode(dec, &p.ж)
} }
// ContainsPointers reports whether T contains any pointers, // ContainsPointers reports whether T contains any pointers,

@ -4,8 +4,7 @@
package views package views
import ( import (
"bytes" jsonv1 "encoding/json"
"encoding/json"
"fmt" "fmt"
"net/netip" "net/netip"
"reflect" "reflect"
@ -15,9 +14,27 @@ import (
"unsafe" "unsafe"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
jsonv2 "github.com/go-json-experiment/json"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"tailscale.com/types/structs" "tailscale.com/types/structs"
) )
// Statically verify that each type implements the following interfaces.
var _ = []interface {
jsonv1.Marshaler
jsonv1.Unmarshaler
jsonv2.MarshalerTo
jsonv2.UnmarshalerFrom
}{
(*ByteSlice[[]byte])(nil),
(*SliceView[*testStruct, testStructView])(nil),
(*Slice[testStruct])(nil),
(*MapSlice[*testStruct, testStructView])(nil),
(*Map[*testStruct, testStructView])(nil),
(*ValuePointer[testStruct])(nil),
}
type viewStruct struct { type viewStruct struct {
Int int Int int
Addrs Slice[netip.Prefix] Addrs Slice[netip.Prefix]
@ -83,14 +100,16 @@ func TestViewsJSON(t *testing.T) {
ipp := SliceOf(mustCIDR("192.168.0.0/24")) ipp := SliceOf(mustCIDR("192.168.0.0/24"))
ss := SliceOf([]string{"bar"}) ss := SliceOf([]string{"bar"})
tests := []struct { tests := []struct {
name string name string
in viewStruct in viewStruct
wantJSON string wantJSONv1 string
wantJSONv2 string
}{ }{
{ {
name: "empty", name: "empty",
in: viewStruct{}, in: viewStruct{},
wantJSON: `{"Int":0,"Addrs":null,"Strings":null}`, wantJSONv1: `{"Int":0,"Addrs":null,"Strings":null}`,
wantJSONv2: `{"Int":0,"Addrs":[],"Strings":[]}`,
}, },
{ {
name: "everything", name: "everything",
@ -101,30 +120,49 @@ func TestViewsJSON(t *testing.T) {
StringsPtr: &ss, StringsPtr: &ss,
Strings: ss, Strings: ss,
}, },
wantJSON: `{"Int":1234,"Addrs":["192.168.0.0/24"],"Strings":["bar"],"AddrsPtr":["192.168.0.0/24"],"StringsPtr":["bar"]}`, wantJSONv1: `{"Int":1234,"Addrs":["192.168.0.0/24"],"Strings":["bar"],"AddrsPtr":["192.168.0.0/24"],"StringsPtr":["bar"]}`,
wantJSONv2: `{"Int":1234,"Addrs":["192.168.0.0/24"],"Strings":["bar"],"AddrsPtr":["192.168.0.0/24"],"StringsPtr":["bar"]}`,
}, },
} }
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
encoder.SetIndent("", "")
for _, tc := range tests { for _, tc := range tests {
buf.Reset() cmpOpts := cmp.Options{
if err := encoder.Encode(&tc.in); err != nil { cmp.AllowUnexported(Slice[string]{}),
t.Fatal(err) cmp.AllowUnexported(Slice[netip.Prefix]{}),
} cmpopts.EquateComparable(netip.Prefix{}),
b := buf.Bytes()
gotJSON := strings.TrimSpace(string(b))
if tc.wantJSON != gotJSON {
t.Fatalf("JSON: %v; want: %v", gotJSON, tc.wantJSON)
}
var got viewStruct
if err := json.Unmarshal(b, &got); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, tc.in) {
t.Fatalf("unmarshal resulted in different output: %+v; want %+v", got, tc.in)
} }
t.Run("JSONv1", func(t *testing.T) {
gotJSON, err := jsonv1.Marshal(tc.in)
if err != nil {
t.Fatal(err)
}
if string(gotJSON) != tc.wantJSONv1 {
t.Fatalf("JSON: %s; want: %s", gotJSON, tc.wantJSONv1)
}
var got viewStruct
if err := jsonv1.Unmarshal(gotJSON, &got); err != nil {
t.Fatal(err)
}
if d := cmp.Diff(got, tc.in, cmpOpts); d != "" {
t.Fatalf("unmarshal mismatch (-got +want):\n%s", d)
}
})
t.Run("JSONv2", func(t *testing.T) {
gotJSON, err := jsonv2.Marshal(tc.in)
if err != nil {
t.Fatal(err)
}
if string(gotJSON) != tc.wantJSONv2 {
t.Fatalf("JSON: %s; want: %s", gotJSON, tc.wantJSONv2)
}
var got viewStruct
if err := jsonv2.Unmarshal(gotJSON, &got); err != nil {
t.Fatal(err)
}
if d := cmp.Diff(got, tc.in, cmpOpts, cmpopts.EquateEmpty()); d != "" {
t.Fatalf("unmarshal mismatch (-got +want):\n%s", d)
}
})
} }
} }

@ -85,28 +85,35 @@ func NewImportTracker(thisPkg *types.Package) *ImportTracker {
} }
} }
type namePkgPath struct {
name string // optional import name
pkgPath string
}
// ImportTracker provides a mechanism to track and build import paths. // ImportTracker provides a mechanism to track and build import paths.
type ImportTracker struct { type ImportTracker struct {
thisPkg *types.Package thisPkg *types.Package
packages map[string]bool packages map[namePkgPath]bool
} }
func (it *ImportTracker) Import(pkg string) { // Import imports pkgPath under an optional import name.
if pkg != "" && !it.packages[pkg] { func (it *ImportTracker) Import(name, pkgPath string) {
mak.Set(&it.packages, pkg, true) if pkgPath != "" && !it.packages[namePkgPath{name, pkgPath}] {
mak.Set(&it.packages, namePkgPath{name, pkgPath}, true)
} }
} }
// Has reports whether the specified package has been imported. // Has reports whether the specified package path has been imported
func (it *ImportTracker) Has(pkg string) bool { // under the particular import name.
return it.packages[pkg] func (it *ImportTracker) Has(name, pkgPath string) bool {
return it.packages[namePkgPath{name, pkgPath}]
} }
func (it *ImportTracker) qualifier(pkg *types.Package) string { func (it *ImportTracker) qualifier(pkg *types.Package) string {
if it.thisPkg == pkg { if it.thisPkg == pkg {
return "" return ""
} }
it.Import(pkg.Path()) it.Import("", pkg.Path())
// TODO(maisem): handle conflicts? // TODO(maisem): handle conflicts?
return pkg.Name() return pkg.Name()
} }
@ -128,7 +135,11 @@ func (it *ImportTracker) PackagePrefix(pkg *types.Package) string {
func (it *ImportTracker) Write(w io.Writer) { func (it *ImportTracker) Write(w io.Writer) {
fmt.Fprintf(w, "import (\n") fmt.Fprintf(w, "import (\n")
for s := range it.packages { for s := range it.packages {
fmt.Fprintf(w, "\t%q\n", s) if s.name == "" {
fmt.Fprintf(w, "\t%q\n", s.pkgPath)
} else {
fmt.Fprintf(w, "\t%s %q\n", s.name, s.pkgPath)
}
} }
fmt.Fprintf(w, ")\n\n") fmt.Fprintf(w, ")\n\n")
} }

Loading…
Cancel
Save