Skip to content

Latest commit

 

History

History
71 lines (56 loc) · 2.6 KB

nodes_processors_and_edges.md

File metadata and controls

71 lines (56 loc) · 2.6 KB

Nodes, Processors, and Edges

At a high level, the command package simply traverses a graph of command.Node objects. A Node contains two fields:

  1. Node.Processor contains the logic that should be executed when a Node is reached.

  2. Node.Edge is the logic executed to determine which Node should be visited next.

The graph is traversed until there aren't any more nodes to traverse (i.e. Node.Edge.Next() returns nil) or until the Node.Processor logic or Node.Edge logic returns an error.

Usage

While the graph logic may seem convoluted, this strucutre allows for features like command execution caching and command shortcuts. Additionally, simple graphs can still be easily constructed with the SerialNodes and BranchNode helper functions.

Examples

Serial Graph

This example constructs a graph that simply works its way through a set of linear nodes:

func SerialGraph() command.Node {
  firstNameArg := commander.Arg[string]("FIRST_NAME", "First name")
  lastNameArg := commander.Arg[string]("LAST_NAME", "Last name")
  excArg := commander.OptionalArg[int]("EXCITEMENT", "How excited you are", commander.Default(1))
  return commander.SerialNodes(
    commander.Description("A friendly CLI"),
    firstNameArg,
    lastNameArg,
    excArg,
    &commander.ExecutorProcessor{func(o command.Output, d *command.Data) {
      o.Stdoutf("Hello, %s %s%s\n", firstNameArg.Get(d), lastNameArg.Get(d), strings.Repeat("!", excArg.Get(d)))
    }}
  )
}

Branch Node Graph

This graph does different things depending on the first argument.

func BranchingGraph() command.Node {
  defaultNode := commander.SerialNodes(
    command.ExecutorNode(func(o command.Output, d *command.Data) {
      o.Stdoutln("Why didn't you pick a door?")
    })
  )
  return commander.BranchNode(map[string]command.Node{
    "one": commander.SerialNodes(
      command.ExecutorNode(func(o command.Output, d *command.Data) {
        o.Stdoutln("Not quite!")
      }),
    ),
    "two": commander.SerialNodes(
      command.ExecutorNode(func(o command.Output, d *command.Data) {
        o.Stdoutln("You won a new car!")
      }),
    ),
    "three": commander.SerialNodes(
      command.ExecutorNode(func(o command.Output, d *command.Data) {
        o.Stdoutln("Try again!")
      }),
    ),
  }, defaultNode)
}

And Beyond

As you can see, these two functions alone can be combined to cover a broad range of CLI use cases. By also exposing the Node, Processor, and Edge types, users are empowered to implement even more advanced CLI-specific graph structures.