// Copyright 2024 Matthew Rich . All rights reserved. package resource import ( _ "errors" "context" "encoding/json" "fmt" "io" "gopkg.in/yaml.v3" "log/slog" _ "gitea.rosskeen.house/rosskeen.house/machine" "gitea.rosskeen.house/pylon/luaruntime" "decl/internal/codec" "decl/internal/config" ) type ConfigName string type DeclarationType struct { Type TypeName `json:"type" yaml:"type"` Transition string `json:"transition,omitempty" yaml:"transition,omitempty"` Config ConfigName `json:"config,omitempty" yaml:"config,omitempty"` } type Declaration struct { Type TypeName `json:"type" yaml:"type"` Transition string `json:"transition,omitempty" yaml:"transition,omitempty"` Attributes Resource `json:"attributes" yaml:"attributes"` Config ConfigName `json:"config,omitempty" yaml:"config,omitempty"` runtime luaruntime.LuaRunner document *Document configBlock *config.Block } type ResourceLoader interface { LoadDecl(string) error } type StateTransformer interface { Apply() error } func NewDeclaration() *Declaration { return &Declaration{} } func NewDeclarationFromDocument(document *Document) *Declaration { return &Declaration{ document: document } } func (d *Declaration) SetDocument(newDocument *Document) { slog.Info("Declaration.SetDocument()") d.document = newDocument d.SetConfig(d.document.config) d.Attributes.SetResourceMapper(d.document.uris) } func (d *Declaration) ResolveId(ctx context.Context) string { defer func() { if r := recover(); r != nil { slog.Info("Declaration.ResolveId() - panic", "recover", r, "state", d.Attributes.StateMachine()) if triggerErr := d.Attributes.StateMachine().Trigger("notexists"); triggerErr != nil { panic(triggerErr) } } }() slog.Info("Declaration.ResolveId()") id := d.Attributes.ResolveId(ctx) return id } func (d *Declaration) Clone() *Declaration { return &Declaration { Type: d.Type, Transition: d.Transition, Attributes: d.Attributes.Clone(), runtime: luaruntime.New(), Config: d.Config, } } func (d *Declaration) Load(r io.Reader) error { return codec.NewYAMLDecoder(r).Decode(d) } func (d *Declaration) LoadDecl(yamlResourceDeclaration string) error { return codec.NewYAMLStringDecoder(yamlResourceDeclaration).Decode(d) } func (d *Declaration) NewResource() error { uri := fmt.Sprintf("%s://", d.Type) newResource, err := ResourceTypes.New(uri) d.Attributes = newResource return err } func (d *Declaration) Resource() Resource { return d.Attributes } func (d *Declaration) Apply() (result error) { defer func() { if r := recover(); r != nil { result = fmt.Errorf("%s", r) } }() stater := d.Attributes.StateMachine() slog.Info("Declaration.Apply()", "machine", stater, "machine.state", stater.CurrentState(), "uri", d.Attributes.URI()) switch d.Transition { case "read": result = stater.Trigger("read") case "delete", "absent": if stater.CurrentState() == "present" { result = stater.Trigger("delete") } case "update": if result = stater.Trigger("update"); result != nil { return result } result = stater.Trigger("read") default: fallthrough case "create", "present": if stater.CurrentState() == "absent" || stater.CurrentState() == "unknown" { if result = stater.Trigger("create"); result != nil { return result } } result = stater.Trigger("read") } return result } func (d *Declaration) SetConfig(configDoc *config.Document) { if configDoc != nil { if configDoc.Has(string(d.Config)) { d.configBlock = configDoc.Get(string(d.Config)) d.Attributes.UseConfig(d.configBlock) } } } func (d *Declaration) SetURI(uri string) error { slog.Info("Declaration.SetURI()", "uri", uri, "declaration", d) d.Attributes = NewResource(uri) if d.Attributes == nil { return ErrUnknownResourceType } d.Type = TypeName(d.Attributes.Type()) _,e := d.Attributes.Read(context.Background()) // fix context return e } func (d *Declaration) UnmarshalValue(value *DeclarationType) error { d.Type = value.Type d.Transition = value.Transition d.Config = value.Config newResource, resourceErr := ResourceTypes.New(fmt.Sprintf("%s://", value.Type)) if resourceErr != nil { slog.Info("Declaration.UnmarshalValue", "value", value, "error", resourceErr) return resourceErr } d.Attributes = newResource return nil } func (d *Declaration) UnmarshalYAML(value *yaml.Node) error { t := &DeclarationType{} if unmarshalResourceTypeErr := value.Decode(t); unmarshalResourceTypeErr != nil { return unmarshalResourceTypeErr } if err := d.UnmarshalValue(t); err != nil { return err } resourceAttrs := struct { Attributes yaml.Node `json:"attributes"` }{} if unmarshalAttributesErr := value.Decode(&resourceAttrs); unmarshalAttributesErr != nil { return unmarshalAttributesErr } if unmarshalResourceErr := resourceAttrs.Attributes.Decode(d.Attributes); unmarshalResourceErr != nil { return unmarshalResourceErr } return nil } func (d *Declaration) UnmarshalJSON(data []byte) error { t := &DeclarationType{} if unmarshalResourceTypeErr := json.Unmarshal(data, t); unmarshalResourceTypeErr != nil { return unmarshalResourceTypeErr } if err := d.UnmarshalValue(t); err != nil { return err } resourceAttrs := struct { Attributes Resource `json:"attributes"` }{Attributes: d.Attributes} if unmarshalAttributesErr := json.Unmarshal(data, &resourceAttrs); unmarshalAttributesErr != nil { return unmarshalAttributesErr } return nil } /* func (d *Declaration) MarshalJSON() ([]byte, error) { buf := new(bytes.Buffer) buf.WriteByte('"') buf.WriteString("value")) buf.WriteByte('"') return buf.Bytes(), nil } */ func (d *Declaration) MarshalYAML() (any, error) { return d, nil } /* func (l *LuaWorker) Receive(m message.Envelope) { s := m.Sender() switch b := m.Body().(type) { case *message.Error: // case *worker.Terminated: case *CodeExecute: stackSize := l.runtime.Api().GetTop() if e := l.runtime.LoadScriptFromString(b.Code); e != nil { s.Send(message.New(&message.Error{ E: e }, l)) } returnsCount := l.runtime.Api().GetTop() - stackSize if len(b.Entrypoint) == 0 { if ! l.runtime.Api().IsNil(-1) { if returnsCount == 0 { s.Send(message.New(&CodeResult{ Result: []interface{}{ 0 } }, l)) } else { lr,le := l.runtime.CopyReturnValuesFromCall(int(returnsCount)) if le != nil { s.Send(message.New(&message.Error{ E: le }, l)) } else { s.Send(message.New(&CodeResult{ Result: lr }, l)) } } } } else { r,ce := l.runtime.CallFunction(b.Entrypoint, b.Args) if ce != nil { s.Send(message.New(&message.Error{ E: ce }, l)) } s.Send(message.New(&CodeResult{ Result: r }, l)) } } } */