Skip to content

Commit

Permalink
Rename ConfigCommand to Command
Browse files Browse the repository at this point in the history
  • Loading branch information
evilmarty committed May 25, 2022
1 parent 439bbff commit 99951dc
Show file tree
Hide file tree
Showing 8 changed files with 325 additions and 312 deletions.
114 changes: 114 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package main

import (
"fmt"

"github.com/erikgeiser/promptkit/selection"
"gopkg.in/yaml.v3"
)

type Command struct {
Name string `yaml:"-"`
Description string
Run string
Env map[string]string
Inputs Inputs
Commands Commands
}

func (command *Command) HasSubCommands() bool {
return len(command.Commands) > 0
}

type Commands []Command

func (commands *Commands) Get(name string) *Command {
for _, command := range *commands {
if command.Name == name {
return &command
}
}
return nil
}

func (commands *Commands) Inputs() Inputs {
inputs := make(Inputs, 0)
for _, command := range *commands {
inputs = append(inputs, command.Inputs...)
}
return inputs
}

func (initCommands Commands) Select() (Commands, error) {
askedCommands := make(Commands, 0)
commands := initCommands

for numCommands := len(commands); numCommands > 0; numCommands = len(commands) {
choices := make([]*selection.Choice, numCommands)

for i, command := range commands {
choices[i] = &selection.Choice{String: command.Name, Value: command}
}

prompt := promptStyle.Render("Choose command")
sp := selection.New(prompt, choices)

if numCommands <= minChoiceFiltering {
sp.Filter = nil
}

choice, err := sp.RunPrompt()
if err != nil {
return askedCommands, err
}

command := choice.Value.(Command)
askedCommands = append(askedCommands, command)
commands = command.Commands
}

return askedCommands, nil
}

func (x *Commands) UnmarshalYAML(value *yaml.Node) error {
if value.Kind != yaml.MappingNode || len(value.Content)%2 != 0 {
return fmt.Errorf("line %d: cannot unmarshal commands into map", value.Line)
}

commands := Commands{}
content := value.Content

for len(content) > 0 {
keyNode := content[0]
valueNode := content[1]

if keyNode.Kind != yaml.ScalarNode || !(valueNode.Kind == yaml.MappingNode || valueNode.Kind == yaml.ScalarNode) {
return fmt.Errorf("line %d: unexpected node type", keyNode.Line)
}

var command Command
if valueNode.Kind == yaml.ScalarNode {
command = Command{
Name: keyNode.Value,
Run: valueNode.Value,
}
} else {
if err := valueNode.Decode(&command); err != nil {
return err
}
if numCommands := len(command.Commands); command.Run == "" && numCommands == 0 {
return fmt.Errorf("line %d: '%s' command missing run or commands attribute", keyNode.Line, keyNode.Value)
} else if command.Run != "" && numCommands > 0 {
return fmt.Errorf("line %d: '%s' command cannot have both run and commands attribute", keyNode.Line, keyNode.Value)
}
command.Name = keyNode.Value
}
commands = append(commands, command)

content = content[2:]
}

*x = commands

return nil
}
198 changes: 198 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package main

import (
"reflect"
"testing"

"gopkg.in/yaml.v3"
)

func TestCommandHasSubCommands(t *testing.T) {
command := Command{
Name: "decepticons",
Commands: Commands{
Command{Name: "punish"},
Command{Name: "enslave"},
},
}
if !command.HasSubCommands() {
t.Errorf("Expected to have sub commands")
}
}

func TestCommandsUnmarshalYAML_WithRunAndCommands(t *testing.T) {
var commands Commands
content := `
protect:
run: echo fail
commands:
foobar:
run: echo foobar
punish:
run: echo Punish and enslave
`
expected := "line 2: 'protect' command cannot have both run and commands attribute"
actual := yaml.Unmarshal([]byte(content), &commands)

if expected != actual.Error() {
t.Errorf("Expected error, but received: %s", actual)
}
}

func TestCommandsUnmarshalYAML_WithoutRunAndCommands(t *testing.T) {
var commands Commands
content := `
protect: {}
punish:
run: echo Punish and enslave
`
expected := "line 2: 'protect' command missing run or commands attribute"
actual := yaml.Unmarshal([]byte(content), &commands)

if expected != actual.Error() {
t.Errorf("Expected error, but received: %s", actual)
}
}

func TestCommandsUnmarshalYAML_WithCommands(t *testing.T) {
var actual Commands
content := `
protect:
commands:
foobar:
run: echo foobar
punish:
commands:
foobaz:
run: echo foobaz
`
expected := Commands{
Command{
Name: "protect",
Commands: Commands{
Command{
Name: "foobar",
Run: "echo foobar",
},
},
},
Command{
Name: "punish",
Commands: Commands{
Command{
Name: "foobaz",
Run: "echo foobaz",
},
},
},
}
err := yaml.Unmarshal([]byte(content), &actual)

if err != nil {
t.Errorf("Received error from parser: %s", err)
}

if !reflect.DeepEqual(actual, expected) {
fatalDiff(t, expected, actual)
}
}

func TestCommandsUnmarshalYAML_WithRun(t *testing.T) {
var actual Commands
content := `
protect:
run: echo Protect all sentient life forms
punish:
run: echo Punish and enslave
`
expected := Commands{
Command{
Name: "protect",
Run: "echo Protect all sentient life forms",
},
Command{
Name: "punish",
Run: "echo Punish and enslave",
},
}
err := yaml.Unmarshal([]byte(content), &actual)

if err != nil {
t.Errorf("Received error from parser: %s", err)
}

if !reflect.DeepEqual(actual, expected) {
fatalDiff(t, expected, actual)
}
}

func TestCommandsUnmarshalYAML_Shorthand(t *testing.T) {
var actual Commands
content := `
protect: echo Protect all sentient life forms
punish: echo Punish and enslave
`
expected := Commands{
Command{
Name: "protect",
Run: "echo Protect all sentient life forms",
},
Command{
Name: "punish",
Run: "echo Punish and enslave",
},
}
err := yaml.Unmarshal([]byte(content), &actual)

if err != nil {
t.Errorf("Received error from parser: %s", err)
}

if !reflect.DeepEqual(actual, expected) {
fatalDiff(t, expected, actual)
}
}

func TestCommandsGet(t *testing.T) {
commands := Commands{
Command{
Name: "a",
},
Command{
Name: "b",
},
}

expected := &commands[1]
actual := commands.Get("b")
if !reflect.DeepEqual(actual, expected) {
fatalDiff(t, expected, actual)
}

expected = nil
actual = commands.Get("c")
if !reflect.DeepEqual(actual, expected) {
fatalDiff(t, expected, actual)
}
}

func TestCommandsInputs(t *testing.T) {
input1 := Input{}
input2 := Input{}
commands := Commands{
Command{
Name: "a",
Inputs: Inputs{input1},
},
Command{
Name: "b",
Inputs: Inputs{input2},
},
}

expected := Inputs{input1, input2}
actual := commands.Inputs()
if !reflect.DeepEqual(actual, expected) {
fatalDiff(t, expected, actual)
}
}
Loading

0 comments on commit 99951dc

Please sign in to comment.