Skip to content

Declare features and resolve a set

Declare at init

A package that owns a feature declares it on the default registry from init, so a blank import is all a consumer needs:

func init() {
    features.MustDeclare(features.Default(), myDescriptor{})
}

Registry.Declare returns an error for a nil or incomplete descriptor (no ID, no kind) or a duplicate ID; MustDeclare panics on it, which at init is the right thing. Any further rule (go-tool-base refuses a plugin that defaults on) is the consumer's, applied before Declare.

Contribute under a slot

const SlotCheck features.Slot = "check"

features.Default().Contribute("reports", SlotCheck, checkReportsStorage)
features.Default().Contribute(features.Global, SlotMiddleware, logging)

A contribution is a value under a feature ID and a slot name. features.Global is the ID for a contribution that applies to every feature. A contribution for an ID nobody has declared is kept, since declaration and contribution may come from different packages in either init order; a Set only hands out the contributions of enabled (hence declared) features.

Order

A snapshot's order is derived from data, never from init sequencing (Go orders init by dependency then filename, which moves with the import graph): descriptors that implement Ranked first, by rank, then the rest by kind and ID. Implement Rank() (int, bool) on your descriptor to pin an order for your own features and return false for the rest.

Resolve

set, err := features.Resolve(features.Default().Snapshot(), states)

The default Resolver applies every descriptor's DefaultOn, then the states in order. Enabling an ID the snapshot does not hold is ErrUnknownFeature; disabling one is ignored and listed by set.Ignored(). Supply your own Resolver for another precedence (an environment overlay, say): Resolve is DefaultResolver().Resolve.

Read

  • set.Enabled(id) for a gate.
  • set.Descriptors() and set.EnabledDescriptors() for an inventory, in snapshot order.
  • set.Contributions(id, slot) for what an enabled feature brings; features.ContributionsOf[T](set, id, slot) for the typed read, which returns the well-typed values and an error naming the feature and slot for any that are not.

A snapshot is the same surface without the enabled gate: snapshot.Contributions hands out everything declared, and snapshot.Contributed() lists every ID carrying a contribution.