92 lines
2.6 KiB
Go
92 lines
2.6 KiB
Go
|
|
package contract
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Identifier prefixes from FluidHypothesisRevisionSchema.md section 16.
|
||
|
|
//
|
||
|
|
// Implementations may use UUIDs internally; these human-readable prefixes exist
|
||
|
|
// so that operational tooling and audit trails stay legible to people.
|
||
|
|
const (
|
||
|
|
PrefixHypothesis = "H-"
|
||
|
|
PrefixRevision = "R-"
|
||
|
|
PrefixExperiment = "E-"
|
||
|
|
PrefixPressure = "P-"
|
||
|
|
PrefixBackendRequirement = "BR-"
|
||
|
|
PrefixDecision = "D-"
|
||
|
|
PrefixEvent = "EV-"
|
||
|
|
PrefixFeedback = "F-"
|
||
|
|
PrefixCohort = "C-"
|
||
|
|
)
|
||
|
|
|
||
|
|
// EntityKind names the FLUID artifact an identifier refers to.
|
||
|
|
type EntityKind string
|
||
|
|
|
||
|
|
const (
|
||
|
|
KindHypothesis EntityKind = "hypothesis"
|
||
|
|
KindRevision EntityKind = "revision"
|
||
|
|
KindExperiment EntityKind = "experiment"
|
||
|
|
KindPressure EntityKind = "pressure"
|
||
|
|
KindBackendRequirement EntityKind = "backend_requirement"
|
||
|
|
KindDecision EntityKind = "decision"
|
||
|
|
KindEvent EntityKind = "event"
|
||
|
|
KindFeedback EntityKind = "feedback"
|
||
|
|
KindCohort EntityKind = "cohort"
|
||
|
|
)
|
||
|
|
|
||
|
|
// prefixOrder matters: "BR-" must be tested before "B"-less single letters
|
||
|
|
// would otherwise mis-claim it, and "EV-" before "E-".
|
||
|
|
var prefixOrder = []struct {
|
||
|
|
prefix string
|
||
|
|
kind EntityKind
|
||
|
|
}{
|
||
|
|
{PrefixBackendRequirement, KindBackendRequirement},
|
||
|
|
{PrefixEvent, KindEvent},
|
||
|
|
{PrefixHypothesis, KindHypothesis},
|
||
|
|
{PrefixRevision, KindRevision},
|
||
|
|
{PrefixExperiment, KindExperiment},
|
||
|
|
{PrefixPressure, KindPressure},
|
||
|
|
{PrefixDecision, KindDecision},
|
||
|
|
{PrefixFeedback, KindFeedback},
|
||
|
|
{PrefixCohort, KindCohort},
|
||
|
|
}
|
||
|
|
|
||
|
|
// KindOf reports which FLUID artifact an identifier names.
|
||
|
|
//
|
||
|
|
// Audit trails carry bare identifiers across record boundaries, so being able to
|
||
|
|
// classify one without knowing where it came from keeps trace reconstruction
|
||
|
|
// from needing a lookup table at every hop.
|
||
|
|
func KindOf(id string) (EntityKind, bool) {
|
||
|
|
for _, p := range prefixOrder {
|
||
|
|
if strings.HasPrefix(id, p.prefix) {
|
||
|
|
return p.kind, true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return "", false
|
||
|
|
}
|
||
|
|
|
||
|
|
// ErrWrongKind reports an identifier used in the wrong position.
|
||
|
|
type ErrWrongKind struct {
|
||
|
|
ID string
|
||
|
|
Want EntityKind
|
||
|
|
Got EntityKind
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *ErrWrongKind) Error() string {
|
||
|
|
if e.Got == "" {
|
||
|
|
return fmt.Sprintf("identifier %q has no recognized FLUID prefix, wanted a %s id", e.ID, e.Want)
|
||
|
|
}
|
||
|
|
return fmt.Sprintf("identifier %q is a %s id, wanted a %s id", e.ID, e.Got, e.Want)
|
||
|
|
}
|
||
|
|
|
||
|
|
// RequireKind checks that id names the expected artifact.
|
||
|
|
func RequireKind(id string, want EntityKind) error {
|
||
|
|
got, ok := KindOf(id)
|
||
|
|
if !ok || got != want {
|
||
|
|
return &ErrWrongKind{ID: id, Want: want, Got: got}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|