v0.30.0 lw --tui, compose pipeline on screen

This commit is contained in:
lixen committed 2026-10-09 19:30:50 -04:00
1 parent 2d72772943
commit 883af0fcaa
20 files changed
+2442 -69

No files matched your search

+48 -2
View File
@@ -94,14 +94,18 @@ func FromCommandLine(line string, isDir config.IsDir) (*Composition, error) {
return c, c.settle()
}
// settle gives every flow stage its defaults and every plugin's options their
// kinds; the rules are Validate's
// settle gives every flow stage its defaults, every plugin's options their
// kinds and every source and sink an id; the rules are Validate's
func (c *Composition) settle() error {
for i := range c.Pipelines {
p := &c.Pipelines[i]
if p.Flow == nil {
p.Flow = &config.FlowConfig{}
}
name(len(p.PluginSources), func(j int) *string { return &p.PluginSources[j].ID },
func(j int) string { return p.PluginSources[j].Type })
name(len(p.PluginSinks), func(j int) *string { return &p.PluginSinks[j].ID },
func(j int) string { return p.PluginSinks[j].Type })
for _, stage := range stages(p.Flow) {
if !stage.IsNil() {
config.Fill(stage.Interface())
@@ -126,6 +130,23 @@ func (c *Composition) settle() error {
return nil
}
// name gives the parts a file left unnamed the ids their specs would get: the
// shell forms write no empty id
func name(n int, id func(int) *string, typ func(int) string) {
var taken []string
for j := range n {
if *id(j) != "" {
taken = append(taken, *id(j))
}
}
for j := range n {
if *id(j) == "" {
*id(j) = config.FreeID(typ(j), taken)
taken = append(taken, *id(j))
}
}
}
// stages are the flow table's fields that hold one stage each, by key: a
// pointer, nil while the stage is off. The filters are a list.
func stages(f *config.FlowConfig) map[string]reflect.Value {
@@ -358,6 +379,31 @@ func editStage(stage reflect.Value, change func(map[string]any) error) error {
return nil
}
// Options returns a copy of a node's options that are set, and whether it is
// on: a flow stage that is off has none
func (c *Composition) Options(pi int, n Node) (map[string]any, bool, error) {
p, err := c.pipeline(pi)
if err != nil {
return nil, false, err
}
switch i := index(p, n); {
case i < 0:
case n.Role == "source":
return config.Clone(p.PluginSources[i].Config), true, nil
case n.Role == "sink":
return config.Clone(p.PluginSinks[i].Config), true, nil
case n.Role == "filters":
return config.Values(&p.Flow.Filters[i]), true, nil
}
if stage, ok := stages(p.Flow)[n.Role]; ok {
if stage.IsNil() {
return nil, false, nil
}
return config.Values(stage.Interface()), true, nil
}
return nil, false, n.missing()
}
// Validate applies every rule lw applies at load, naming the refused key's path
func (c *Composition) Validate() error { return config.ValidatePipelines(c.Pipelines) }
+42
View File
@@ -7,6 +7,8 @@ import (
"slices"
"strings"
"testing"
"github.com/lixenwraith/logwisp/internal/config"
)
// The engine builds as WebAssembly for the website: it links no TLS, HTTP,
@@ -117,3 +119,43 @@ func TestSetKeepsAKeyItCannotTake(t *testing.T) {
t.Fatalf("options %v, heartbeat %+v", m, beat)
}
}
// Options are a copy: changing them changes no pipeline; a stage that is off
// has none
func TestOptionsAreACopy(t *testing.T) {
c := &Composition{}
if err := c.AddPipeline("p"); err != nil {
t.Fatal(err)
}
n, _ := c.Add(0, "sink", "tcp")
if err := c.Set(0, n, "port", "9000"); err != nil {
t.Fatal(err)
}
opts, on, err := c.Options(0, n)
opts["port"] = int64(1)
if _, rate, _ := c.Options(0, Node{Role: "rate_limit"}); err != nil || !on || rate || c.Pipelines[0].PluginSinks[0].Config["port"] != int64(9000) {
t.Fatalf("%v %v %v %v", opts, on, rate, err)
}
}
// A file may leave a source's or sink's id out: the engine names it as a
// spec would, around the ids the file gave, so the shell forms load back
func TestUnnamedPartsTakeFreeIDs(t *testing.T) {
c, err := FromConfig(&config.Config{Pipelines: []config.PipelineConfig{{Name: "p",
PluginSources: []config.PluginSourceConfig{{Type: "null"}, {ID: "null", Type: "null"}},
PluginSinks: []config.PluginSinkConfig{{Type: "null"}}}}})
if err != nil {
t.Fatal(err)
}
p := c.Pipelines[0]
if ids := []string{p.PluginSources[0].ID, p.PluginSources[1].ID, p.PluginSinks[0].ID}; !slices.Equal(ids, []string{"null_2", "null", "null"}) {
t.Fatalf("ids %q", ids)
}
line, err := c.CommandLine()
if err == nil {
_, err = FromCommandLine(line, nil)
}
if err != nil {
t.Fatalf("%v\n%s", err, line)
}
}