-
Notifications
You must be signed in to change notification settings - Fork 0
/
piper.go
150 lines (138 loc) · 3.39 KB
/
piper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// piper is a CLI framework that does just what I want.
// It Manages a CLI for pipeline processes, where any number of defined tasks
// can be specified in sequence.
package piper
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
)
type Task func(interface{}, map[string]Flag, []string) (interface{}, error)
type Flag struct {
Name string
Symbol string
Description string
}
type Command struct {
Name string
Description string
Args []string
Task Task
}
type CLIApp struct {
Name string
Description string
Flags []Flag
Commands []Command
}
func (c *CLIApp) PrintHelp() {
fmt.Print("\n* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n")
fmt.Print("\n" + c.Name + " - " + c.Description + "\n\n")
fmt.Println("Usage:")
fmt.Println(" " + c.Name + " [global options] [command [arguments...] ...]")
fmt.Print("\n")
if len(c.Flags) > 0 {
fmt.Println("Global options:")
for _, f := range c.Flags {
if len(f.Symbol) > 0 {
fmt.Print(" -" + f.Symbol)
}
if len(f.Description) > 0 {
fmt.Print(" " + f.Description + "\n")
}
}
}
fmt.Print("\n")
if len(c.Commands) > 0 {
if len(c.Commands) > 0 {
fmt.Println("Commands:")
for _, s := range c.Commands {
fmt.Print(" " + s.Name + " - " + s.Description + "\n")
if len(s.Args) > 0 {
fmt.Print(" args: " + s.Args[0])
for _, a := range s.Args[1:] {
fmt.Print(", " + a)
}
fmt.Print("\n")
}
fmt.Print("\n")
}
}
}
fmt.Print("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n")
}
func (c *CLIApp) RegisterCommand(cmd Command) {
c.Commands = append(c.Commands, cmd)
}
func (c *CLIApp) RegisterFlag(flag Flag) {
c.Flags = append(c.Flags, flag)
}
// Parses command line arguments, constructing a pipeline of tasks from the
// subcommands along the way, returning an error if any issues are
// encountered.
// Once the arguments have been interpreted it executes the pipline.
func (c *CLIApp) Run() (err error) {
flags := make(map[string]Flag)
pipeline := make([]func(interface{}) (interface{}, error), 0)
i := 1
for i < len(os.Args) {
// skip whitespace
if strings.TrimSpace(os.Args[i]) == "" {
i++
continue
}
var read int
read, err = func(args []string) (read int, err error) {
arg := args[0]
if arg[:1] == "-" {
// it's a flag
for _, f := range c.Flags {
if f.Symbol == arg[1:] {
flags[f.Name] = f
read = 1
return
}
}
err = errors.New("Unknown flag: " + arg)
} else {
// it's a task
for _, t := range c.Commands {
if t.Name == arg {
if len(args) < len(t.Args)+1 {
err = errors.New("Insufficient arguments provided for task " +
arg + ", expected " + strconv.Itoa(len(t.Args)))
return
}
task_args := args[1 : len(t.Args)+1]
pipeline = append(pipeline, func(data interface{}) (interface{}, error) {
data, err = t.Task(data, flags, task_args)
return data, err
})
read = len(t.Args) + 1
return
}
}
err = errors.New("Unknown task: " + arg)
}
return
}(os.Args[i:])
if err != nil {
return
}
i += read
}
if len(pipeline) == 0 {
c.PrintHelp()
}
var data interface{}
for i, stage := range pipeline {
data, err = stage(data)
if err != nil {
fmt.Println("Error returned from pipeline stage "+strconv.Itoa(i)+" : ",
err.Error())
}
}
return
}