312 lines
8.3 KiB
Go
312 lines
8.3 KiB
Go
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||
|
|
||
|
package folio
|
||
|
|
||
|
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/data"
|
||
|
"decl/internal/schema"
|
||
|
)
|
||
|
|
||
|
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 data.Resource `json:"attributes" yaml:"attributes"`
|
||
|
Config ConfigName `json:"config,omitempty" yaml:"config,omitempty"`
|
||
|
// runtime luaruntime.LuaRunner
|
||
|
document *Document
|
||
|
configBlock data.Block
|
||
|
ResourceTypes data.TypesRegistry[data.Resource] `json:"-" yaml:"-"`
|
||
|
}
|
||
|
|
||
|
func NewDeclaration() *Declaration {
|
||
|
return &Declaration{ ResourceTypes: DocumentRegistry.ResourceTypes }
|
||
|
}
|
||
|
|
||
|
func NewDeclarationFromDocument(document *Document) *Declaration {
|
||
|
return &Declaration{ document: document, ResourceTypes: document.Types() }
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) SetDocument(newDocument *Document) {
|
||
|
slog.Info("Declaration.SetDocument()", "declaration", d)
|
||
|
d.document = newDocument
|
||
|
d.SetConfig(d.document.config)
|
||
|
d.ResourceTypes = d.document.Types()
|
||
|
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() data.Declaration {
|
||
|
return &Declaration {
|
||
|
Type: d.Type,
|
||
|
Transition: d.Transition,
|
||
|
Attributes: d.Attributes.Clone(),
|
||
|
//runtime: luaruntime.New(),
|
||
|
Config: d.Config,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) Load(docData []byte, f codec.Format) (err error) {
|
||
|
err = f.StringDecoder(string(docData)).Decode(d)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) LoadReader(r io.ReadCloser, f codec.Format) (err error) {
|
||
|
err = f.Decoder(r).Decode(d)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) LoadString(docData string, f codec.Format) (err error) {
|
||
|
err = f.StringDecoder(docData).Decode(d)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) ResourceType() data.TypeName {
|
||
|
return data.TypeName(d.Type)
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) URI() string {
|
||
|
return d.Attributes.URI()
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) JSON() ([]byte, error) {
|
||
|
return json.Marshal(d)
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) Validate() (err error) {
|
||
|
var declarationJson []byte
|
||
|
if declarationJson, err = d.JSON(); err == nil {
|
||
|
s := schema.New(fmt.Sprintf("%s-declaration", d.Type), schemaFiles)
|
||
|
err = s.Validate(string(declarationJson))
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) NewResource(uri *string) (err error) {
|
||
|
if d.ResourceTypes == nil {
|
||
|
panic(fmt.Errorf("Undefined type registry: unable to create new resource %s", *uri))
|
||
|
}
|
||
|
if uri == nil {
|
||
|
d.Attributes, err = d.ResourceTypes.New(fmt.Sprintf("%s://", d.Type))
|
||
|
} else {
|
||
|
d.Attributes, err = d.ResourceTypes.New(*uri)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) Resource() data.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 "construct":
|
||
|
if doc, ok := DocumentRegistry.DeclarationMap[d]; ok {
|
||
|
d.SetDocument(doc)
|
||
|
}
|
||
|
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 data.Document) {
|
||
|
if configDoc != nil {
|
||
|
if configDoc.Has(string(d.Config)) {
|
||
|
v, _ := configDoc.Get(string(d.Config))
|
||
|
d.configBlock = v.(data.Block)
|
||
|
d.Attributes.UseConfig(d.configBlock)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (d *Declaration) SetURI(uri string) (err error) {
|
||
|
slog.Info("Declaration.SetURI()", "uri", uri, "declaration", d)
|
||
|
if d.Attributes == nil {
|
||
|
if err = d.NewResource(&uri); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
if d.Attributes == nil {
|
||
|
return fmt.Errorf("%w: %s", ErrUnknownResourceType, uri)
|
||
|
}
|
||
|
d.Type = TypeName(d.Attributes.Type())
|
||
|
_, err = d.Attributes.Read(context.Background()) // fix context
|
||
|
slog.Info("Declaration.SetURI() - read", "error", err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
|
||
|
func (d *Declaration) UnmarshalValue(value *DeclarationType) error {
|
||
|
slog.Info("Declaration.UnmarshalValue", "declaration", d, "value", value, "addr", d)
|
||
|
if d.ResourceTypes == nil {
|
||
|
panic(fmt.Errorf("Undefined type registry: unable to create new resource %s", value.Type))
|
||
|
}
|
||
|
d.Type = value.Type
|
||
|
d.Transition = value.Transition
|
||
|
d.Config = value.Config
|
||
|
newResource, resourceErr := d.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 {
|
||
|
if d.ResourceTypes == nil {
|
||
|
d.ResourceTypes = DocumentRegistry.ResourceTypes
|
||
|
}
|
||
|
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(jsonData []byte) error {
|
||
|
if d.ResourceTypes == nil {
|
||
|
d.ResourceTypes = DocumentRegistry.ResourceTypes
|
||
|
}
|
||
|
t := &DeclarationType{}
|
||
|
if unmarshalResourceTypeErr := json.Unmarshal(jsonData, t); unmarshalResourceTypeErr != nil {
|
||
|
return unmarshalResourceTypeErr
|
||
|
}
|
||
|
|
||
|
if err := d.UnmarshalValue(t); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
resourceAttrs := struct {
|
||
|
Attributes data.Resource `json:"attributes"`
|
||
|
}{Attributes: d.Attributes}
|
||
|
if unmarshalAttributesErr := json.Unmarshal(jsonData, &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))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*/
|