package engine import ( "fmt" "slices" "strconv" "sync" "time" ) type VariableState struct { Scope string // 零值时表示全局的,否则表示指定节点的 Key string Value any ValueType string } func (s VariableState) ValueString() string { switch s.ValueType { case "string": return fmt.Sprintf("%s", s.Value) case "number": return fmt.Sprintf("%d", s.Value) case "boolean": return strconv.FormatBool(s.Value.(bool)) case "datetime": valueAsTime := s.Value.(time.Time) if valueAsTime.IsZero() { return "-" } return valueAsTime.Format(time.RFC3339) default: return fmt.Sprintf("[%s]%v", s.ValueType, s.Value) } } type VariableManager interface { All() []VariableState Erase() Add(entry VariableState) Set(name string, value any, valueType string) SetScoped(scope string, name string, value any, valueType string) Get(name string) (*VariableState, bool) GetScoped(scope string, key string) (*VariableState, bool) Take(key string) (*VariableState, bool) TakeScoped(scope string, key string) (*VariableState, bool) Remove(key string) bool RemoveScoped(scope string, key string) bool } type variableManager struct { statesMtx sync.RWMutex states []VariableState } var _ VariableManager = (*variableManager)(nil) func (m *variableManager) All() []VariableState { m.statesMtx.RLock() defer m.statesMtx.RUnlock() if m.states == nil { return make([]VariableState, 0) } return slices.Clone(m.states) } func (m *variableManager) Erase() { m.statesMtx.Lock() defer m.statesMtx.Unlock() m.states = make([]VariableState, 0) } func (m *variableManager) Add(state VariableState) { m.statesMtx.Lock() defer m.statesMtx.Unlock() if m.states == nil { m.states = make([]VariableState, 0) } for i, item := range m.states { if item.Scope == state.Scope && item.Key == state.Key { m.states[i] = state return } } m.states = append(m.states, state) } func (m *variableManager) Set(key string, value any, valueType string) { m.SetScoped("", key, value, valueType) } func (m *variableManager) SetScoped(scope string, key string, value any, valueType string) { m.Add(VariableState{Scope: scope, Key: key, Value: value, ValueType: valueType}) } func (m *variableManager) Get(key string) (*VariableState, bool) { return m.GetScoped("", key) } func (m *variableManager) GetScoped(scope string, key string) (*VariableState, bool) { m.statesMtx.RLock() defer m.statesMtx.RUnlock() if m.states == nil { return nil, false } for _, item := range m.states { if item.Scope == scope && item.Key == key { return &item, true } } return nil, false } func (m *variableManager) Take(key string) (*VariableState, bool) { return m.TakeScoped("", key) } func (m *variableManager) TakeScoped(scope string, key string) (*VariableState, bool) { m.statesMtx.Lock() defer m.statesMtx.Unlock() if m.states == nil { return nil, false } for i, item := range m.states { if item.Scope == scope && item.Key == key { m.states = slices.Delete(m.states, i, i+1) return &item, true } } return nil, false } func (m *variableManager) Remove(key string) bool { return m.RemoveScoped("", key) } func (m *variableManager) RemoveScoped(scope string, key string) bool { _, ok := m.TakeScoped(scope, key) return ok } func newVariableManager() VariableManager { return &variableManager{ states: make([]VariableState, 0), } } type InOutState struct { NodeId string Type string Name string Value any ValueType string Persistent bool } func (s InOutState) ValueString() string { switch s.ValueType { case "string": return s.Value.(string) case "number": return fmt.Sprintf("%d", s.Value) case "boolean": return strconv.FormatBool(s.Value.(bool)) default: return fmt.Sprintf("%v", s.Value) } } type InOutManager interface { All() []InOutState Erase() Add(state InOutState) Set(nodeId string, stype string, name string, value any, valueType string, persistent bool) Get(nodeId string, name string) (*InOutState, bool) Take(nodeId string, name string) (*InOutState, bool) Remove(nodeId string, name string) bool } type inoutManager struct { statesMtx sync.RWMutex states []InOutState } var _ InOutManager = (*inoutManager)(nil) func (m *inoutManager) All() []InOutState { m.statesMtx.RLock() defer m.statesMtx.RUnlock() if m.states == nil { return make([]InOutState, 0) } return slices.Clone(m.states) } func (m *inoutManager) Erase() { m.statesMtx.Lock() defer m.statesMtx.Unlock() m.states = make([]InOutState, 0) } func (m *inoutManager) Add(state InOutState) { m.statesMtx.Lock() defer m.statesMtx.Unlock() if m.states == nil { m.states = make([]InOutState, 0) } for i, item := range m.states { if item.NodeId == state.NodeId && item.Name == state.Name { m.states[i] = state return } } m.states = append(m.states, state) } func (m *inoutManager) Set(nodeId string, stype string, name string, value any, valueType string, persistent bool) { m.Add(InOutState{NodeId: nodeId, Type: stype, Name: name, Value: value, ValueType: valueType, Persistent: persistent}) } func (m *inoutManager) Get(nodeId string, name string) (*InOutState, bool) { m.statesMtx.RLock() defer m.statesMtx.RUnlock() if m.states == nil { return nil, false } for _, item := range m.states { if item.NodeId == nodeId && item.Name == name { return &item, true } } return nil, false } func (m *inoutManager) Take(nodeId string, name string) (*InOutState, bool) { m.statesMtx.Lock() defer m.statesMtx.Unlock() if m.states == nil { return nil, false } for i, item := range m.states { if item.NodeId == nodeId && item.Name == name { m.states = slices.Delete(m.states, i, i+1) return &item, true } } return nil, false } func (m *inoutManager) Remove(nodeId string, name string) bool { _, ok := m.Take(nodeId, name) return ok } func newInOutManager() InOutManager { return &inoutManager{ states: make([]InOutState, 0), } } const ( stateIOTypeRef = "ref" ) const ( stateVarKeyWorkflowId = "workflow.id" // ValueType: "string" stateVarKeyWorkflowName = "workflow.name" // ValueType: "string" stateVarKeyRunId = "run.id" // ValueType: "string" stateVarKeyRunTrigger = "run.trigger" // ValueType: "string" stateVarKeyNodeId = "node.id" // ValueType: "string" stateVarKeyNodeName = "node.name" // ValueType: "string" stateVarKeyNodeSkipped = "node.skipped" // ValueType: "boolean" stateVarKeyErrorNodeId = "error.nodeId" // ValueType: "string" stateVarKeyErrorNodeName = "error.nodeName" // ValueType: "string" stateVarKeyErrorMessage = "error.message" // ValueType: "string" stateVarKeyCertificateDomain = "certificate.domain" // ValueType: "string" stateVarKeyCertificateDomains = "certificate.domains" // ValueType: "string" stateVarKeyCertificateNotBefore = "certificate.notBefore" // ValueType: "datetime" stateVarKeyCertificateNotAfter = "certificate.notAfter" // ValueType: "datetime" stateVarKeyCertificateHoursLeft = "certificate.hoursLeft" // ValueType: "number" stateVarKeyCertificateDaysLeft = "certificate.daysLeft" // ValueType: "number" stateVarKeyCertificateValidity = "certificate.validity" // ValueType: "boolean" )