package main import ( "encoding/csv" "encoding/json" "errors" "flag" "fmt" "io" "os" "sync" ) const ( FormatCSV = "csv" FormatJSON = "json" FormatStream = "stream" ) type UnknownFormatError struct{ Format string } func (e UnknownFormatError) Error() string { return "unknown format: '" + e.Format + "'" } func main() { if err := run(); err != nil { _, _ = os.Stderr.WriteString("ERROR: " + err.Error()) os.Exit(1) } } func run() error { var in string var out string var format string var width int flag.StringVar(&in, "in", "", "input file") flag.StringVar(&out, "out", "", "output file") flag.StringVar(&format, "format", "csv", "output format (csv,json,stream)") flag.IntVar(&width, "width", DefaultTabWidth, "spaces per tab") flag.Parse() var input io.ReadCloser = os.Stdin var output io.WriteCloser = os.Stdout if in != "" { var err error if input, err = os.Open(in); err != nil { return fmt.Errorf("failed to get input %s: %w", in, err) } } if out != "" { var err error if output, err = os.Create(out); err != nil { return fmt.Errorf("failed to get output %s: %w", out, err) } } p := Parser{Width: width} var err error switch format { case FormatCSV: var once sync.Once writer := csv.NewWriter(output) err = p.IterSlice(input, func(headers []string, values []string) error { var err error once.Do(func() { err = writer.Write(headers) }) if err != nil { return err } return writer.Write(values) }) writer.Flush() case FormatStream: enc := json.NewEncoder(output) err = p.IterMap(input, func(values map[string]string) error { return enc.Encode(values) }) case FormatJSON: var doc Document doc, err = p.Parse(input) if err != nil { return fmt.Errorf("failed to parse input: %w", errors.Join(err, input.Close(), output.Close())) } err = json.NewEncoder(output).Encode(doc) default: err = UnknownFormatError{Format: format} } return errors.Join(err, input.Close(), output.Close()) }