From 3d28aa19cbf70a0b0e72d2ce37e83bd7e73a346c Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Thu, 27 Feb 2025 12:33:31 -0800 Subject: [PATCH] all: statically enforce json/v2 interface satisfaction (#15154) The json/v2 prototype is still in flux and the API can/will change. Statically enforce that types implementing the v2 methods satisfy the correct interface so that changes to the signature can be statically detected by the compiler. Updates tailscale/corp#791 Signed-off-by: Joe Tsai --- types/opt/value_test.go | 5 +++++ types/prefs/list.go | 5 +++++ types/prefs/prefs.go | 5 +++++ types/prefs/prefs_example/prefs_types.go | 5 +++++ types/prefs/prefs_test.go | 19 +++++++++++++++++++ util/syspolicy/setting/origin.go | 5 +++++ util/syspolicy/setting/raw_item.go | 10 ++++++++++ util/syspolicy/setting/snapshot.go | 5 +++++ util/syspolicy/setting/summary.go | 5 +++++ 9 files changed, 64 insertions(+) diff --git a/types/opt/value_test.go b/types/opt/value_test.go index dbd8b255f..890f9a579 100644 --- a/types/opt/value_test.go +++ b/types/opt/value_test.go @@ -13,6 +13,11 @@ import ( "tailscale.com/util/must" ) +var ( + _ jsonv2.MarshalerTo = (*Value[bool])(nil) + _ jsonv2.UnmarshalerFrom = (*Value[bool])(nil) +) + type testStruct struct { Int int `json:",omitempty,omitzero"` Str string `json:",omitempty"` diff --git a/types/prefs/list.go b/types/prefs/list.go index e9c1a1f33..7db473887 100644 --- a/types/prefs/list.go +++ b/types/prefs/list.go @@ -157,6 +157,11 @@ func (lv ListView[T]) Equal(lv2 ListView[T]) bool { return lv.ж.Equal(*lv2.ж) } +var ( + _ jsonv2.MarshalerTo = (*ListView[bool])(nil) + _ jsonv2.UnmarshalerFrom = (*ListView[bool])(nil) +) + // MarshalJSONTo implements [jsonv2.MarshalerTo]. func (lv ListView[T]) MarshalJSONTo(out *jsontext.Encoder) error { return lv.ж.MarshalJSONTo(out) diff --git a/types/prefs/prefs.go b/types/prefs/prefs.go index 52cb464b6..a6caf1283 100644 --- a/types/prefs/prefs.go +++ b/types/prefs/prefs.go @@ -158,6 +158,11 @@ func (p *preference[T]) SetReadOnly(readonly bool) { p.s.Metadata.ReadOnly = readonly } +var ( + _ jsonv2.MarshalerTo = (*preference[struct{}])(nil) + _ jsonv2.UnmarshalerFrom = (*preference[struct{}])(nil) +) + // MarshalJSONTo implements [jsonv2.MarshalerTo]. func (p preference[T]) MarshalJSONTo(out *jsontext.Encoder) error { return jsonv2.MarshalEncode(out, &p.s) diff --git a/types/prefs/prefs_example/prefs_types.go b/types/prefs/prefs_example/prefs_types.go index f88c29f94..c35f1f62f 100644 --- a/types/prefs/prefs_example/prefs_types.go +++ b/types/prefs/prefs_example/prefs_types.go @@ -128,6 +128,11 @@ type AppConnectorPrefs struct { Advertise prefs.Item[bool] `json:",omitzero"` } +var ( + _ jsonv2.MarshalerTo = (*Prefs)(nil) + _ jsonv2.UnmarshalerFrom = (*Prefs)(nil) +) + // MarshalJSONTo implements [jsonv2.MarshalerTo]. // It is implemented as a performance improvement and to enable omission of // unconfigured preferences from the JSON output. See the [Prefs] doc for details. diff --git a/types/prefs/prefs_test.go b/types/prefs/prefs_test.go index 1201054d0..d6af745bf 100644 --- a/types/prefs/prefs_test.go +++ b/types/prefs/prefs_test.go @@ -19,6 +19,20 @@ import ( //go:generate go run tailscale.com/cmd/viewer --tags=test --type=TestPrefs,TestBundle,TestValueStruct,TestGenericStruct,TestPrefsGroup +var ( + _ jsonv2.MarshalerTo = (*ItemView[*TestBundle, TestBundleView])(nil) + _ jsonv2.UnmarshalerFrom = (*ItemView[*TestBundle, TestBundleView])(nil) + + _ jsonv2.MarshalerTo = (*MapView[string, string])(nil) + _ jsonv2.UnmarshalerFrom = (*MapView[string, string])(nil) + + _ jsonv2.MarshalerTo = (*StructListView[*TestBundle, TestBundleView])(nil) + _ jsonv2.UnmarshalerFrom = (*StructListView[*TestBundle, TestBundleView])(nil) + + _ jsonv2.MarshalerTo = (*StructMapView[string, *TestBundle, TestBundleView])(nil) + _ jsonv2.UnmarshalerFrom = (*StructMapView[string, *TestBundle, TestBundleView])(nil) +) + type TestPrefs struct { Int32Item Item[int32] `json:",omitzero"` UInt64Item Item[uint64] `json:",omitzero"` @@ -53,6 +67,11 @@ type TestPrefs struct { Group TestPrefsGroup `json:",omitzero"` } +var ( + _ jsonv2.MarshalerTo = (*TestPrefs)(nil) + _ jsonv2.UnmarshalerFrom = (*TestPrefs)(nil) +) + // MarshalJSONTo implements [jsonv2.MarshalerTo]. func (p TestPrefs) MarshalJSONTo(out *jsontext.Encoder) error { // The testPrefs type shadows the TestPrefs's method set, diff --git a/util/syspolicy/setting/origin.go b/util/syspolicy/setting/origin.go index b5b28edf6..4c7cc7025 100644 --- a/util/syspolicy/setting/origin.go +++ b/util/syspolicy/setting/origin.go @@ -50,6 +50,11 @@ func (s Origin) String() string { return s.Scope().String() } +var ( + _ jsonv2.MarshalerTo = (*Origin)(nil) + _ jsonv2.UnmarshalerFrom = (*Origin)(nil) +) + // MarshalJSONTo implements [jsonv2.MarshalerTo]. func (s Origin) MarshalJSONTo(out *jsontext.Encoder) error { return jsonv2.MarshalEncode(out, &s.data) diff --git a/util/syspolicy/setting/raw_item.go b/util/syspolicy/setting/raw_item.go index 82e5f634a..9a96073b0 100644 --- a/util/syspolicy/setting/raw_item.go +++ b/util/syspolicy/setting/raw_item.go @@ -75,6 +75,11 @@ func (i RawItem) String() string { return fmt.Sprintf("%v%s", i.data.Value.Value, suffix) } +var ( + _ jsonv2.MarshalerTo = (*RawItem)(nil) + _ jsonv2.UnmarshalerFrom = (*RawItem)(nil) +) + // MarshalJSONTo implements [jsonv2.MarshalerTo]. func (i RawItem) MarshalJSONTo(out *jsontext.Encoder) error { return jsonv2.MarshalEncode(out, &i.data) @@ -114,6 +119,11 @@ func RawValueOf[T RawValueType](v T) RawValue { return RawValue{opt.ValueOf[any](v)} } +var ( + _ jsonv2.MarshalerTo = (*RawValue)(nil) + _ jsonv2.UnmarshalerFrom = (*RawValue)(nil) +) + // MarshalJSONTo implements [jsonv2.MarshalerTo]. func (v RawValue) MarshalJSONTo(out *jsontext.Encoder) error { return jsonv2.MarshalEncode(out, v.Value) diff --git a/util/syspolicy/setting/snapshot.go b/util/syspolicy/setting/snapshot.go index 38642f7cc..087325a04 100644 --- a/util/syspolicy/setting/snapshot.go +++ b/util/syspolicy/setting/snapshot.go @@ -147,6 +147,11 @@ type snapshotJSON struct { Settings map[Key]RawItem `json:",omitempty"` } +var ( + _ jsonv2.MarshalerTo = (*Snapshot)(nil) + _ jsonv2.UnmarshalerFrom = (*Snapshot)(nil) +) + // MarshalJSONTo implements [jsonv2.MarshalerTo]. func (s *Snapshot) MarshalJSONTo(out *jsontext.Encoder) error { data := &snapshotJSON{} diff --git a/util/syspolicy/setting/summary.go b/util/syspolicy/setting/summary.go index d7c139a87..9864822f7 100644 --- a/util/syspolicy/setting/summary.go +++ b/util/syspolicy/setting/summary.go @@ -54,6 +54,11 @@ func (s Summary) String() string { return s.data.Scope.String() } +var ( + _ jsonv2.MarshalerTo = (*Summary)(nil) + _ jsonv2.UnmarshalerFrom = (*Summary)(nil) +) + // MarshalJSONTo implements [jsonv2.MarshalerTo]. func (s Summary) MarshalJSONTo(out *jsontext.Encoder) error { return jsonv2.MarshalEncode(out, &s.data)