diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index dc4df564a..ab254a57e 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -4,7 +4,7 @@ package tailcfg -//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal --clonefunc +//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal,ControlDialPlan --clonefunc import ( "bytes" diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index 4c24485d3..daec87fc9 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -398,9 +398,26 @@ var _SSHPrincipalCloneNeedsRegeneration = SSHPrincipal(struct { PubKeys []string }{}) +// Clone makes a deep copy of ControlDialPlan. +// The result aliases no memory with the original. +func (src *ControlDialPlan) Clone() *ControlDialPlan { + if src == nil { + return nil + } + dst := new(ControlDialPlan) + *dst = *src + dst.Candidates = append(src.Candidates[:0:0], src.Candidates...) + return dst +} + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _ControlDialPlanCloneNeedsRegeneration = ControlDialPlan(struct { + Candidates []ControlIPCandidate +}{}) + // Clone duplicates src into dst and reports whether it succeeded. // To succeed, must be of types <*T, *T> or <*T, **T>, -// where T is one of User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal. +// where T is one of User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal,ControlDialPlan. func Clone(dst, src any) bool { switch src := src.(type) { case *User: @@ -511,6 +528,15 @@ func Clone(dst, src any) bool { *dst = src.Clone() return true } + case *ControlDialPlan: + switch dst := dst.(type) { + case *ControlDialPlan: + *dst = *src.Clone() + return true + case **ControlDialPlan: + *dst = src.Clone() + return true + } } return false } diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index ac1b58139..30da1d02b 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -21,7 +21,7 @@ import ( "tailscale.com/types/views" ) -//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal +//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal,ControlDialPlan // View returns a readonly view of User. func (p *User) View() UserView { @@ -923,3 +923,57 @@ var _SSHPrincipalViewNeedsRegeneration = SSHPrincipal(struct { Any bool PubKeys []string }{}) + +// View returns a readonly view of ControlDialPlan. +func (p *ControlDialPlan) View() ControlDialPlanView { + return ControlDialPlanView{ж: p} +} + +// ControlDialPlanView provides a read-only view over ControlDialPlan. +// +// Its methods should only be called if `Valid()` returns true. +type ControlDialPlanView struct { + // ж is the underlying mutable value, named with a hard-to-type + // character that looks pointy like a pointer. + // It is named distinctively to make you think of how dangerous it is to escape + // to callers. You must not let callers be able to mutate it. + ж *ControlDialPlan +} + +// Valid reports whether underlying value is non-nil. +func (v ControlDialPlanView) Valid() bool { return v.ж != nil } + +// AsStruct returns a clone of the underlying value which aliases no memory with +// the original. +func (v ControlDialPlanView) AsStruct() *ControlDialPlan { + if v.ж == nil { + return nil + } + return v.ж.Clone() +} + +func (v ControlDialPlanView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } + +func (v *ControlDialPlanView) UnmarshalJSON(b []byte) error { + if v.ж != nil { + return errors.New("already initialized") + } + if len(b) == 0 { + return nil + } + var x ControlDialPlan + if err := json.Unmarshal(b, &x); err != nil { + return err + } + v.ж = &x + return nil +} + +func (v ControlDialPlanView) Candidates() views.Slice[ControlIPCandidate] { + return views.SliceOf(v.ж.Candidates) +} + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _ControlDialPlanViewNeedsRegeneration = ControlDialPlan(struct { + Candidates []ControlIPCandidate +}{})