package machineimport ()// ///// ///// /////// ///// STATES & SCHEMA// ///// ///// /////typeStatesBasestruct {// Exception is the only built-in state and mean a global error. All errors // have to [State.Require] the Exception state. If [Machine.PanicToErr] is // true, Exception will receive it. Exception string names S groups map[string][]int groupsOrder []string}var _ States = &StatesBase{}func ( *StatesBase) () S {return .names}func ( *StatesBase) () (map[string][]int, []string) {return .groups, .groupsOrder}func ( *StatesBase) ( S) { .names = slicesUniq()}func ( *StatesBase) ( map[string][]int, []string) { .groups = .groupsOrder = }// States is the vase interface for schema states.typeStatesinterface {// Names returns the state names of the state machine.Names() S// TODOStateGroups() (map[string][]int, []string)SetNames(S)SetStateGroups(map[string][]int, []string)}func [ States]( ) {// read and assign names of all the embedded structs := S{} := map[string][]int{} := reflect.ValueOf(&).Elem() := []string{}parseStateNames(, &, "self", , &) .SetNames() .SetStateGroups(, )return}func parseStateNames(reflect.Value, *S, string, map[string][]int, *[]string,) {if != "StatesBase" { [] = []int{} * = append(*, ) } := .Type()for := 0; < .NumField(); ++ { := .Field() := .Field() := .Type.Kind()if .Anonymous && == reflect.Ptr &&// embedded struct (inherit states) .Type.Elem().Kind() == reflect.Struct {if .IsNil() { := reflect.New(.Type.Elem()) .Set() } (.Elem(), , .Name, , ) } elseif .CanSet() && == reflect.String {// local state name .SetString(.Name)if !slices.Contains(*, .Name) {if != "StatesBase" { [] = append([], len(*)) } * = append(*, .Name) } } }}// NewStateGroups accepts the target group with values (FooGroupsDef) and// inherited GroupsDefs (optionally).func [ any]( , ...any) {// init nil embeds := reflect.ValueOf(&).Elem()initNilEmbeds()// assign values from parent mixins into the local instancefor := range {copyFields([], &) }return}func initNilEmbeds( reflect.Value) { := .Type()for := 0; < .NumField(); ++ { := .Field() := .Field() := .Type.Kind()if .Anonymous && == reflect.Ptr && .Type.Elem().Kind() == reflect.Struct {if .IsNil() { := reflect.New(.Type.Elem()) .Set() } (.Elem()) } }}func copyFields(, interface{}) {if == nil {return } := reflect.ValueOf() := reflect.ValueOf()if .Kind() == reflect.Ptr { = .Elem() }if .Kind() == reflect.Ptr { = .Elem() }for := 0; < .NumField(); ++ { := .Type().Field().Name := .Field() := .FieldByName()if .Kind() == reflect.Struct { (.Addr().Interface(), .Addr().Interface()) } else {if .CanSet() { .Set() } } }}// TODO refac to Schema.StatesByTagfunc ( Schema, string) S { := S{}for , := range {for , := range .Tags {if == || strings.HasPrefix(, +":") { = append(, )break } } }return}// ///// ///// /////// ///// ARGS// ///// ///// /////constAPrefix = "_am"// ArgsApi is the base interface for typed arguments.typeArgsApiinterface {// ArgsPrefix returns the argument prefix inside the [A] map. Should be // provided by the implementation.ArgsPrefix() string// ArgsState represents the state this argument struct belongs to. // Defaults to [StateAny].ArgsState() string// TODO deep clone interface, optional // ArgsClone() G}// ArgsBase is the base implementation of [ArgsApi] interface.typeArgsBasestruct {ArgsApi}// ArgsPrefix is a fallback to a stack trace hash of the implementation// symbol. Each// pkg should overwrite this method.func ( ArgsBase) () string { := make([]byte, 4024) := runtime.Stack(, false) := string([:]) := strings.Split(, "\n") := Hash([3], 10)return}// State is the default Any state.func ( ArgsBase) () string {returnStateAny}// Clone for deep cloning.func ( ArgsBase) () ArgsApi {// shallow clonereturn}// pkg args// Args for this pkg. Do not reuse.typeArgsstruct {ArgsBase}func (Args) () string {returnAPrefix}// -----// ExceptionArgsPanic is an optional argument ["panic"] for the StateException// state which describes a panic within a Transition handler.typeExceptionArgsPanicstruct { CalledStates S StatesBefore S Transition *Transition LastStep *Step StackTrace string}typeAExceptionstruct {Args Err error`log:"err"` ErrTrace string Panic *ExceptionArgsPanic// TODO handle Fn string`log:"fn"`// deadline TargetStates S CalledStates S TimeBefore Time TimeAfter Time Event *Event}func (AException) () string {returnStateException}// ACheck with a CheckDone chan which gets closed by the machine once it's// processed. Can cause chan leaks when misused. Only for Can* checks.typeACheckstruct {Args// TODO close these on dispose and deadline CheckDone chanstruct{}// Was the mutation canceled? Canceled bool}// -----// PassMerge merged one or more [A] into [A]. Technically a `map[string]any`// merge.func ( ...A) A { := A{}for , := range {for , := range { [] = } }return}// Pass accepts pointers of [ArgsBase] to pass to the machine as [A].func ( ...ArgsApi) A { := A{}for , := range { [ArgIndex()] = }return}// ParseArgs is [ParseArgsCheck] but without the check.func [ ArgsApi]( A) * { , := ParseArgsCheck[]()return}// ParseArgsCheck parses [A] into typed arguments described by the// passed generic// type. Returnsfunc [ ArgsApi]( A) (*, bool) {var := ArgIndex() , := [].(*)if ! {// RPC support , := [].()if ! {// fallbackreturn &, false } = & }return , true}// ArgIndex return an index of [arg] inside [A].func ( ArgsApi) string { := .ArgsPrefix() := .ArgsState()return + "__" + }
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.