mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-30 12:42:31 +00:00

Now that 25c4dc5fd70 removed unregistering hooks and made them into slices, just expose the slices and remove the setter funcs. This removes boilerplate ceremony around adding new hooks. This does export the hooks and make them mutable at runtime in theory, but that'd be a data race. If we really wanted to lock it down in the future we could make the feature.Hooks slice type be an opaque struct with an All() iterator and a "frozen" bool and we could freeze all the hooks after init. But that doesn't seem worth it. This means that hook registration is also now all in one place, rather than being mixed into ProfilesService vs ipnext.Host vs FooService vs BarService. I view that as a feature. When we have a ton of hooks and the list is long, then we can rearrange the fields in the Hooks struct as needed, or make sub-structs, or big comments. Updates #12614 Change-Id: I05ce5baa45a61e79c04591c2043c05f3288d8587 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
73 lines
1.6 KiB
Go
73 lines
1.6 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package feature tracks which features are linked into the binary.
|
|
package feature
|
|
|
|
import "reflect"
|
|
|
|
var in = map[string]bool{}
|
|
|
|
// Register notes that the named feature is linked into the binary.
|
|
func Register(name string) {
|
|
if _, ok := in[name]; ok {
|
|
panic("duplicate feature registration for " + name)
|
|
}
|
|
in[name] = true
|
|
}
|
|
|
|
// Hook is a func that can only be set once.
|
|
//
|
|
// It is not safe for concurrent use.
|
|
type Hook[Func any] struct {
|
|
f Func
|
|
ok bool
|
|
}
|
|
|
|
// IsSet reports whether the hook has been set.
|
|
func (h *Hook[Func]) IsSet() bool {
|
|
return h.ok
|
|
}
|
|
|
|
// Set sets the hook function, panicking if it's already been set
|
|
// or f is the zero value.
|
|
//
|
|
// It's meant to be called in init.
|
|
func (h *Hook[Func]) Set(f Func) {
|
|
if h.ok {
|
|
panic("Set on already-set feature hook")
|
|
}
|
|
if reflect.ValueOf(f).IsZero() {
|
|
panic("Set with zero value")
|
|
}
|
|
h.f = f
|
|
h.ok = true
|
|
}
|
|
|
|
// Get returns the hook function, or panics if it hasn't been set.
|
|
// Use IsSet to check if it's been set.
|
|
func (h *Hook[Func]) Get() Func {
|
|
if !h.ok {
|
|
panic("Get on unset feature hook, without IsSet")
|
|
}
|
|
return h.f
|
|
}
|
|
|
|
// Hooks is a slice of funcs.
|
|
//
|
|
// As opposed to a single Hook, this is meant to be used when
|
|
// multiple parties are able to install the same hook.
|
|
type Hooks[Func any] []Func
|
|
|
|
// Add adds a hook to the list of hooks.
|
|
//
|
|
// Add should only be called during early program
|
|
// startup before Tailscale has started.
|
|
// It is not safe for concurrent use.
|
|
func (h *Hooks[Func]) Add(f Func) {
|
|
if reflect.ValueOf(f).IsZero() {
|
|
panic("Add with zero value")
|
|
}
|
|
*h = append(*h, f)
|
|
}
|