Skip to content

Commit

Permalink
oss: cleanup scan code, references supported!
Browse files Browse the repository at this point in the history
Signed-off-by: Solomon Hykes <sh.github.6811@hykes.org>
  • Loading branch information
shykes committed May 16, 2020
1 parent 6762bf1 commit eebb934
Showing 1 changed file with 70 additions and 75 deletions.
145 changes: 70 additions & 75 deletions loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func main() {
panic(err)
}

conns := scanConnectors(i.Value(), nil)
conns := scanConnectors(i.Value())
for _, c := range(conns) {
tasks := c.Tasks()
ui.Info("Connector: %s (%d tasks)", c.String(), len(tasks))
Expand Down Expand Up @@ -78,7 +78,7 @@ func (c *Connector) String() (msg string) {
}

func (c *Connector) Tasks() []*Task {
return lookupTasks(c.Value)
return scanTasks(c.Value)
}

func (c *Connector) ID() (id string, set, exists bool) {
Expand Down Expand Up @@ -113,33 +113,34 @@ func NewConnector(v cue.Value, path ...string) *Connector {
}
}

func scanConnectors(v cue.Value, path []string) (conns []*Connector) {
func lookup(v cue.Value, path...string) (result cue.Value, err error) {
var field cue.FieldInfo
result = v
for _, name := range(path) {
field, err = result.LookupField(name)
if err != nil {
return
}
result = field.Value
}
return
}


func scan(v cue.Value, get func(cue.Value, []string) bool, path []string) {
debug("[scanning] %s", strings.Join(path, "."))
// Is `v` a struct with a definition #ID ?
c := NewConnector(v, path...)
if _, _, idExists := c.ID(); idExists {
debug("\tconnector detected")
conns = append(conns, c)
// FIXME: continue scanning. this allows connectors
// to dynamically link to other connectors
if !get(v, path) {
return
}

// Recursively check references
refI, refP := v.Reference()
if len(refP) > 0 {
info, err := refI.LookupField(refP...)
refTarget, err := lookup(refI.Value(), refP...)
if err != nil {
ui.Error("error looking up %v: %s", refP, err)
return
}
if info.IsDefinition {
// FIXME: LookupDef is tricky to use here
debug("FIXME: skipping reference to %s", strings.Join(refP, "."))
} else {
refTarget := refI.Lookup(refP...)
conns = append(conns, scanConnectors(refTarget, refP)...)
}
scan(refTarget, get, refP)
return
}

Expand All @@ -148,34 +149,71 @@ func scanConnectors(v cue.Value, path []string) (conns []*Connector) {
case cue.StructKind:
// Only iterate over "regular" fields (not hidden, eg. definitions)
for it, _ := v.Fields(); it.Next(); {
conns = append(conns, scanConnectors(it.Value(), append(path, it.Label()))...)
scan(it.Value(), get, append(path, it.Label()))
}
// Recursively check list elements
case cue.ListKind:
for it, _ := v.List(); it.Next(); {
conns = append(conns, scanConnectors(it.Value(), append(path, it.Label()))...)
scan(it.Value(), get, append(path, it.Label()))
}
}

// If `v` is an expression, break it down and recursively inspect each component
exprOp, exprArgs:= v.Expr()
if exprOp != cue.NoOp && exprOp != cue.SelectorOp {
for _, arg := range(exprArgs) {
// fakeLabel is used for human-friendly path display, only
conns = append(conns, scanConnectors(arg, path)...)
scan(arg, get, path)
}
}
return
}


func scanConnectors(v cue.Value) (conns []*Connector) {
scan(
v,
func(v cue.Value, path []string) bool {
// Is `v` a struct with a definition #ID ?
c := NewConnector(v, path...)
if _, _, idExists := c.ID(); idExists {
debug("\tconnector detected")
conns = append(conns, c)
return false
}
return true
},
nil,
)
return
}


func scanTasks(v cue.Value) (tasks []*Task) {
scan(
v,
func(v cue.Value, path []string) bool {
// Is `v` a struct with a definition #ID ?
t := NewTask(v, path...)
if _, exists := t.Verb(); exists {
debug("\ttask detected")
tasks = append(tasks, t)
return false
}
return true
},
nil,
)
return
}

type ExecTask struct {
Task
}

func (t *ExecTask) String() string {
cmd, cmdExists := t.Cmd()
if cmdExists {
return fmt.Sprintf("exec %v", cmd)
return fmt.Sprintf("%s [exec %v]", strings.Join(t.Path, "."), cmd)
}
return "exec <malformed>"
}
Expand All @@ -199,6 +237,14 @@ func (t *ExecTask) Cmd() (cmd []string, exists bool) {

type Task struct {
cue.Value
Path []string
}

func NewTask(v cue.Value, path ...string) *Task {
return &Task{
Value: v,
Path: path,
}
}

func (t *Task) Verb() (verb string, exists bool) {
Expand All @@ -216,54 +262,3 @@ func (t *Task) Verb() (verb string, exists bool) {
exists = true
return
}

// Current limitations of the task scanner:
// - Does not follow references to definitions. Tasks in definitions will not be found, even
// if a concrete value depends on part of the definition.
// - @task(exec) must be set after the struct value (embedded attributes are broken ATM)
func lookupTasks(v cue.Value) (tasks []*Task) {
// Does v have a @task attribute?
t := &Task{Value: v}
if _, exists := t.Verb(); exists {
tasks = append(tasks, t)
}
// Check for references
refI, refP := v.Reference()
if len(refP) > 0 {
info, err := refI.LookupField(refP...)
if err != nil {
ui.Error("error looking up %v: %s", refP, err)
return
}
if info.IsDefinition {
// FIXME: LookupDef is tricky to use here
debug("FIXME: skipping reference to %s", strings.Join(refP, "."))
} else {
tasks = append(tasks, lookupTasks(refI.Lookup(refP...))...)
}
return
}

switch v.Kind() {
// Recursively check struct fields
case cue.StructKind:
// Only iterate over "regular" fields (not hidden, eg. definitions)
for it, _ := v.Fields(); it.Next(); {
tasks = append(tasks, lookupTasks(it.Value())...)
}
// Recursively check list elements
case cue.ListKind:
for it, _ := v.List(); it.Next(); {
tasks = append(tasks, lookupTasks(it.Value())...)
}
}
// If v is an expression, recursively inspect its component parts
exprOp, exprArgs:= v.Expr()
if exprOp != cue.NoOp && exprOp != cue.SelectorOp {
for _, arg := range(exprArgs) {
// fakeLabel is used for human-friendly path display, only
tasks = append(tasks, lookupTasks(arg)...)
}
}
return
}

0 comments on commit eebb934

Please sign in to comment.