struct/struct.go

172 lines
3.4 KiB
Go
Raw Permalink Normal View History

2019-09-24 02:13:29 +00:00
package main
import (
"bufio"
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"strings"
)
2019-10-27 19:41:52 +00:00
const (
defaultFieldSeparator = " "
defaultOutput = "string"
fieldsUsage = "comma-separated list of fields corresponding to the input data's fields\n\n" +
"Example\n\n" +
"$ echo \"foo1 bar1\" | struct -fields foo,bar\n" +
"$ foo:foo1 bar:bar1\n"
outputUsage = "the desired output format: json or string"
separatorUsage = "the character that separates fields in the input data"
2020-03-06 22:22:52 +00:00
skipHeadUsage = "the number of lines to skip from the beginning of the input"
2019-10-27 19:41:52 +00:00
)
2019-09-24 02:13:29 +00:00
2019-10-27 19:41:52 +00:00
var (
fieldsFlag fields // a comma-separated list of fields provided by the user
outFlag string // the format (either string or json) in which to structure data
sepFlag string // a string on which to split lines of input
2020-03-06 22:22:52 +00:00
skipHead int // the number of lines to skip from the head
2019-10-27 19:41:52 +00:00
)
2019-09-24 02:13:29 +00:00
func init() {
2019-10-27 19:41:52 +00:00
flagset := flag.CommandLine
flagset.Var(&fieldsFlag, "fields", fieldsUsage)
flagset.StringVar(&sepFlag, "separator", defaultFieldSeparator, separatorUsage)
flagset.StringVar(&outFlag, "output", defaultOutput, outputUsage)
2020-03-06 22:22:52 +00:00
flagset.IntVar(&skipHead, "skip-head", 0, skipHeadUsage)
2019-10-27 19:41:52 +00:00
flagset.Usage = func() {
2020-03-06 22:22:52 +00:00
fmt.Println("struct creates structured output from unstructured input")
2019-10-27 19:41:52 +00:00
flagset.PrintDefaults()
}
2019-09-24 02:13:29 +00:00
2019-10-27 19:41:52 +00:00
flagset.Parse(os.Args[1:])
2019-09-24 02:13:29 +00:00
2020-03-06 22:22:52 +00:00
if len(fieldsFlag) == 0 {
flagset.Usage()
os.Exit(0)
}
2019-09-24 02:13:29 +00:00
}
var sb strings.Builder
var jsonMap = make(map[string]string)
func main() {
2019-10-27 19:41:52 +00:00
fieldsProvided := len(fieldsFlag)
2019-09-24 02:13:29 +00:00
scanner := bufio.NewScanner(os.Stdin)
2020-03-06 22:22:52 +00:00
var i, line int
2019-10-27 19:41:52 +00:00
var field, in, v string
2019-09-24 02:13:29 +00:00
2019-10-27 19:41:52 +00:00
// perform a line-wise scan over stdin until EOF
2019-09-24 02:13:29 +00:00
for scanner.Scan() {
2020-03-06 22:22:52 +00:00
line++
if line <= skipHead {
continue
}
2019-09-24 02:13:29 +00:00
in = scanner.Text()
2019-10-27 19:41:52 +00:00
// if the separator string is overridden, split on it instead of the default of splitting on space
var inputFields []string
if sepFlag != defaultFieldSeparator {
inputFields = strings.Split(in, sepFlag)
} else {
inputFields = strings.Fields(in)
}
2019-09-24 02:13:29 +00:00
2019-10-27 19:41:52 +00:00
// for every field to be retained from the input, build a representation of it in the output
2019-09-24 02:13:29 +00:00
for i, v = range inputFields {
if i < fieldsProvided {
field = fieldsFlag[i]
} else {
field = ""
}
2019-10-27 19:41:52 +00:00
if err := buildOutput(field, v); err != nil {
fmt.Println(err.Error())
return
}
2019-09-24 02:13:29 +00:00
}
fmt.Println(out())
}
}
func out() (out string) {
2019-10-27 19:41:52 +00:00
switch outFlag {
case "string":
2019-09-24 02:13:29 +00:00
out = sb.String()
sb.Reset()
2019-10-27 19:41:52 +00:00
case "json":
2019-09-24 02:13:29 +00:00
o, _ := json.Marshal(jsonMap)
out = string(o)
for k := range jsonMap {
delete(jsonMap, k)
}
2019-10-27 19:41:52 +00:00
default:
2019-09-24 02:13:29 +00:00
out = ""
}
return
}
2019-10-27 19:41:52 +00:00
func buildOutput(field, value string) (err error) {
switch outFlag {
case "string":
2019-09-24 02:13:29 +00:00
buildString(field, value)
2019-10-27 19:41:52 +00:00
case "json":
2019-09-24 02:13:29 +00:00
buildJSON(field, value)
2019-10-27 19:41:52 +00:00
default:
err = fmt.Errorf("invalid -output option: %s", outFlag)
return
2019-09-24 02:13:29 +00:00
}
2019-10-27 19:41:52 +00:00
return
2019-09-24 02:13:29 +00:00
}
func buildString(field, value string) {
if field != "" {
sb.WriteString(field)
sb.WriteString(":")
sb.WriteString(value)
2019-10-27 19:41:52 +00:00
sb.WriteString(defaultFieldSeparator)
2019-09-24 02:13:29 +00:00
} else {
sb.WriteString(value)
2019-10-27 19:41:52 +00:00
sb.WriteString(defaultFieldSeparator)
2019-09-24 02:13:29 +00:00
}
}
func buildJSON(field, value string) {
if field == "" {
return
}
jsonMap[field] = value
}
2020-03-06 22:22:52 +00:00
type fields []string
func (f *fields) String() string {
return fmt.Sprint(*f)
}
func (f *fields) Set(value string) error {
if len(*f) > 0 {
return errors.New("fields switch already set")
}
for _, field := range strings.Split(value, ",") {
*f = append(*f, field)
}
return nil
}