1package command 2 3import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "io" 8 "os" 9 "strings" 10 11 "github.com/hashicorp/errwrap" 12 "github.com/hashicorp/go-sockaddr/template" 13 "github.com/mitchellh/cli" 14) 15 16type EvalCommand struct { 17 Ui cli.Ui 18 19 // debugOutput emits framed output vs raw output. 20 debugOutput bool 21 22 // flags is a list of options belonging to this command 23 flags *flag.FlagSet 24 25 // rawInput disables wrapping the string in the text/template {{ }} 26 // handlebars. 27 rawInput bool 28 29 // suppressNewline changes whether or not there's a newline between each 30 // arg passed to the eval subcommand. 31 suppressNewline bool 32} 33 34// Description is the long-form command help. 35func (c *EvalCommand) Description() string { 36 return `Parse the sockaddr template and evaluates the output. 37 38` + "The `sockaddr` library has the potential to be very complex, which is why the " + 39 "`sockaddr` command supports an `eval` subcommand in order to test configurations " + 40 "from the command line. The `eval` subcommand automatically wraps its input with " + 41 "the `{{` and `}}` template delimiters unless the `-r` command is specified, in " + 42 "which case `eval` parses the raw input. If the `template` argument passed to " + 43 "`eval` is a dash (`-`), then `sockaddr eval` will read from stdin and " + 44 "automatically sets the `-r` flag." 45 46} 47 48// Help returns the full help output expected by `sockaddr -h cmd` 49func (c *EvalCommand) Help() string { 50 return MakeHelp(c) 51} 52 53// InitOpts is responsible for setup of this command's configuration via the 54// command line. InitOpts() does not parse the arguments (see parseOpts()). 55func (c *EvalCommand) InitOpts() { 56 c.flags = flag.NewFlagSet("eval", flag.ContinueOnError) 57 c.flags.Usage = func() { c.Ui.Output(c.Help()) } 58 c.flags.BoolVar(&c.debugOutput, "d", false, "Debug output") 59 c.flags.BoolVar(&c.suppressNewline, "n", false, "Suppress newlines between args") 60 c.flags.BoolVar(&c.rawInput, "r", false, "Suppress wrapping the input with {{ }} delimiters") 61} 62 63// Run executes this command. 64func (c *EvalCommand) Run(args []string) int { 65 if len(args) == 0 { 66 c.Ui.Error(c.Help()) 67 return 1 68 } 69 70 c.InitOpts() 71 tmpls, err := c.parseOpts(args) 72 if err != nil { 73 if errwrap.Contains(err, "flag: help requested") { 74 return 0 75 } 76 return 1 77 } 78 inputs, outputs := make([]string, len(tmpls)), make([]string, len(tmpls)) 79 var rawInput, readStdin bool 80 for i, in := range tmpls { 81 if readStdin { 82 break 83 } 84 85 rawInput = c.rawInput 86 if in == "-" { 87 rawInput = true 88 var f io.Reader = os.Stdin 89 var buf bytes.Buffer 90 if _, err := io.Copy(&buf, f); err != nil { 91 c.Ui.Error(fmt.Sprintf("[ERROR]: Error reading from stdin: %v", err)) 92 return 1 93 } 94 in = buf.String() 95 if len(in) == 0 { 96 return 0 97 } 98 readStdin = true 99 } 100 inputs[i] = in 101 102 if !rawInput { 103 in = `{{` + in + `}}` 104 inputs[i] = in 105 } 106 107 out, err := template.Parse(in) 108 if err != nil { 109 c.Ui.Error(fmt.Sprintf("ERROR[%d] in: %q\n[%d] msg: %v\n", i, in, i, err)) 110 return 1 111 } 112 outputs[i] = out 113 } 114 115 if c.debugOutput { 116 for i, out := range outputs { 117 c.Ui.Output(fmt.Sprintf("[%d] in: %q\n[%d] out: %q\n", i, inputs[i], i, out)) 118 if i != len(outputs)-1 { 119 if c.debugOutput { 120 c.Ui.Output(fmt.Sprintf("---\n")) 121 } 122 } 123 } 124 } else { 125 sep := "\n" 126 if c.suppressNewline { 127 sep = "" 128 } 129 c.Ui.Output(strings.Join(outputs, sep)) 130 } 131 132 return 0 133} 134 135// Synopsis returns a terse description used when listing sub-commands. 136func (c *EvalCommand) Synopsis() string { 137 return `Evaluates a sockaddr template` 138} 139 140// Usage is the one-line usage description 141func (c *EvalCommand) Usage() string { 142 return `sockaddr eval [options] [template ...]` 143} 144 145// VisitAllFlags forwards the visitor function to the FlagSet 146func (c *EvalCommand) VisitAllFlags(fn func(*flag.Flag)) { 147 c.flags.VisitAll(fn) 148} 149 150// parseOpts is responsible for parsing the options set in InitOpts(). Returns 151// a list of non-parsed flags. 152func (c *EvalCommand) parseOpts(args []string) ([]string, error) { 153 if err := c.flags.Parse(args); err != nil { 154 return nil, err 155 } 156 157 return c.flags.Args(), nil 158} 159