//nolint:lll
package integrations import ( am ) // Kind enum type Kind enum.Member[string] var ( KindReqGetter = Kind{"am_req_getter"} KindReqMutation = Kind{"am_req_mutation"} KindReqWaiting = Kind{"am_req_waiting"} KindRespGetter = Kind{"am_resp_getter"} KindRespMutation = Kind{"am_resp_mutation"} KindRespWaiting = Kind{"am_resp_waiting"} KindEnum = enum.New(KindReqGetter, KindReqMutation, KindReqWaiting, KindRespGetter, KindRespMutation, KindRespWaiting) ) // flatten to a string in JSON func ( *Kind) () ([]byte, error) { return json.Marshal(.Value) } func ( *Kind) ( []byte) error { var string := json.Unmarshal(, &) if != nil { return } // success .Value = return nil } // TODO validate JSON also for Req and Resp structs // MsgKindReq is a decoding helper. type MsgKindReq struct { // The kind of the request. Kind Kind `json:"kind" jsonschema:"required,enum=am_req_getter,enum=am_req_mutation,enum=am_req_waiting"` } // MsgKindResp is a decoding helper. type MsgKindResp struct { // The kind of the response. Kind Kind `json:"kind" jsonschema:"required,enum=am_resp_waiting,enum=am_resp_mutation,enum=am_resp_getter"` } // MUTATION type MutationReq struct { // The kind of the request. Kind Kind `json:"kind" jsonschema:"required,enum=am_req_mutation"` // The states to add to the state machine. Add am.S `json:"add,omitempty" jsonschema:"oneof_required=add"` // The states to remove from the state machine. Remove am.S `json:"remove,omitempty" jsonschema:"oneof_required=remove"` // Arguments passed to transition handlers. Args map[string]any `json:"args,omitempty"` // TODO machine filters to narrow down the request on group channels } type MutationResp struct { // The kind of the request. Kind Kind `json:"kind" jsonschema:"required,enum=am_req_mutation"` // The result of the mutation request. Result am.Result `json:"result"` } // WAITING type WaitingReq struct { // The kind of the request. Kind Kind `json:"kind" jsonschema:"required,enum=am_req_waiting"` // The states to wait for, the default is to all states being active simultaneously (if no time passed). States am.S `json:"states,omitempty" jsonschema:"oneof_required=states"` // The states names to wait for to be inactive. Ignores the Time field. StatesNot am.S `json:"states_not,omitempty" jsonschema:"oneof_required=statesNot"` // The specific (minimal) time to wait for. Time am.Time `json:"time,omitempty"` // TODO WhenArgs } type WaitingRespUnsafe struct { // The kind of the response. Kind Kind `json:"kind" jsonschema:"required,enum=am_resp_waiting"` // The ID of the state machine. MachId string `json:"mach_id"` // The active states waited for. If time is empty, all these states are active simultaneously. States am.S `json:"states,omitempty" jsonschema:"oneof_required=states"` // The inactive states waited for. StatesNot am.S `json:"states_not,omitempty" jsonschema:"oneof_required=states"` // The requested machine time (the current one may be higher). Time am.Time `json:"time,omitempty"` } type WaitingResp struct { WaitingRespUnsafe } func ( *WaitingResp) ( []byte) error { := WaitingRespUnsafe{} if := json.Unmarshal(, &); != nil { return } if .Kind != KindRespWaiting { return errors.New("wrong response kind") } .WaitingRespUnsafe = // TODO more validation return nil } // GETTER // GetterReq is a generic request, which results in GetterResp with // respective fields filled out. type GetterReq struct { // The kind of the request. Kind Kind `json:"kind" jsonschema:"required,enum=am_req_getter"` // Request ticks of the passed states Time am.S `json:"time,omitempty"` // Request the sum of ticks of the passed states TimeSum am.S `json:"time_sum,omitempty"` // Request named clocks of the passed states Clocks am.S `json:"clocks,omitempty"` // Request the tags of the state machine Tags bool `json:"tags,omitempty"` // Request an importable version of the state machine Export bool `json:"export,omitempty"` // Request the ID of the state machine Id bool `json:"id,omitempty"` // Request the ID of the parent state machine ParentId bool `json:"parent_id,omitempty"` } // GetterResp is a response to GetterReq. type GetterResp struct { // The kind of the response. Kind Kind `json:"kind" jsonschema:"required,enum=am_resp_getter"` // The ID of the state machine. MachId string `json:"mach_id,omitempty"` // The ticks of the passed states Time am.Time `json:"time,omitempty"` // The sum of ticks of the passed states TimeSum int `json:"time_sum,omitempty"` // The named clocks of the passed states Clocks am.Clock `json:"clocks,omitempty"` // The tags of the state machine Tags []string `json:"tags,omitempty"` // The importable version of the state machine Export *am.Serialized `json:"export,omitempty"` // The ID of the state machine Id string `json:"id,omitempty"` // The ID of the parent state machine ParentId string `json:"parent_id,omitempty"` } // UTILS & HANDLERS // NewGetterReq creates a new getter request. func () *GetterReq { return &GetterReq{ Kind: KindReqGetter, } } // NewMutationReq creates a new mutation request. // TODO sugar for NewAddReq and NewRemoveReq func () *MutationReq { return &MutationReq{ Kind: KindReqMutation, } } // NewWaitingReq creates a new waiting request. func () *WaitingReq { return &WaitingReq{ Kind: KindReqWaiting, } } func ( context.Context, am.Api, *WaitingReq, ) (*WaitingResp, error) { := &WaitingResp{WaitingRespUnsafe{Kind: KindRespWaiting}} // validate := len(.Time) := max(len(.States), len(.StatesNot)) if == 0 { return nil, errors.New("waiting states missing") } else if > 0 && != len(.States) { return nil, errors.New("waiting states and time length mismatch") } if !.Has(.States) { return nil, fmt.Errorf("%w: (%s) for %s", am.ErrStateMissing, .States, .Id()) } // subscribe var <-chan struct{} if len(.StatesNot) > 0 { = .WhenNot(.StatesNot, ) .StatesNot = .StatesNot } else if > 0 { = .WhenTime(.States, .Time, ) } else { = .When(.States, ) .States = .States } // wait select { case <-.Done(): return nil, .Err() // pass case <-: } // update the response and return .MachId = .Id() if > 0 { .Time = .Time(.States) } return , nil } func ( context.Context, am.Api, *MutationReq, ) (*MutationResp, error) { := &MutationResp{Kind: KindRespMutation} if len(.Add) > 0 && len(.Remove) == 0 { if !.Has(.Add) { return nil, fmt.Errorf("%w: (%s) for %s", am.ErrStateMissing, .Add, .Id()) } .Result = .Add(.Add, .Args) } else if len(.Remove) > 0 && len(.Add) == 0 { if !.Has(.Remove) { return nil, fmt.Errorf("%w: (%s) for %s", am.ErrStateMissing, .Remove, .Id()) } .Result = .Remove(.Remove, .Args) } else if len(.Add) > 0 && len(.Remove) > 0 { return nil, errors.New("mutation can be add or remove, not both") } else { return nil, errors.New("mutation state names missing") } return , nil } func ( context.Context, am.Api, *GetterReq, ) (*GetterResp, error) { := &GetterResp{Kind: KindRespGetter} if .Id { .Id = .Id() } if .ParentId { .ParentId = .ParentId() } if .Tags { .Tags = .Tags() } if .Export { .Export, _, _ = .Export() } if len(.Time) > 0 { .Time = .Time(.Time) } if len(.Clocks) > 0 { .Clocks = .Clock(.Clocks) } if len(.TimeSum) > 0 { .TimeSum = int(.Time(.TimeSum).Sum(nil)) } return , nil }