diff --git a/README.md b/README.md index 9b9c8626..8c569c8f 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,8 @@ Example usage assuming there's a valid schema in `in.avsc`: avrogen -pkg avro -o bla.go -tags json:snake,yaml:upper-camel in.avsc ``` +**Tip:** Omit `-o FILE` to dump the generated Go structs to stdout instead of a file. + Check the options and usage with `-h`: ```shell diff --git a/cmd/avrogen/main.go b/cmd/avrogen/main.go index ef28186c..7ba35a71 100644 --- a/cmd/avrogen/main.go +++ b/cmd/avrogen/main.go @@ -23,15 +23,15 @@ type config struct { } func main() { - os.Exit(realMain(os.Args, os.Stderr)) + os.Exit(realMain(os.Args, os.Stderr, os.Stdout)) } -func realMain(args []string, out io.Writer) int { +func realMain(args []string, out, dumpout io.Writer) int { var cfg config flgs := flag.NewFlagSet("avrogen", flag.ExitOnError) flgs.SetOutput(out) flgs.StringVar(&cfg.Pkg, "pkg", "", "The package name of the output file.") - flgs.StringVar(&cfg.Out, "o", "", "The output file path.") + flgs.StringVar(&cfg.Out, "o", "", "The output file path to write to instead of stdout.") flgs.StringVar(&cfg.Tags, "tags", "", "The additional field tags :{snake|camel|upper-camel|kebab}>[,...]") flgs.BoolVar(&cfg.FullName, "fullname", false, "Use the full name of the Record schema to create the struct name.") flgs.BoolVar(&cfg.Encoders, "encoders", false, "Generate encoders for the structs.") @@ -75,13 +75,27 @@ func realMain(args []string, out io.Writer) int { } formatted, err := format.Source(buf.Bytes()) if err != nil { - _, _ = fmt.Fprintf(out, "Error: could format code: %v\n", err) + _, _ = fmt.Fprintf(out, "Error: could not format code: %v\n", err) return 3 } - if err = os.WriteFile(cfg.Out, formatted, 0o600); err != nil { - _, _ = fmt.Fprintf(out, "Error: could write file: %v\n", err) + + writer := dumpout + if cfg.Out != "" { + file, err := os.Create(cfg.Out) + if err != nil { + _, _ = fmt.Fprintf(out, "Error: could not create output file: %v\n", err) + return 4 + } + defer func() { _ = file.Close() }() + + writer = file + } + + if _, err := writer.Write(formatted); err != nil { + _, _ = fmt.Fprintf(out, "Error: could not write code: %v\n", err) return 4 } + return 0 } @@ -94,10 +108,6 @@ func validateOpts(nargs int, cfg config) error { return fmt.Errorf("a package is required") } - if cfg.Out == "" { - return fmt.Errorf("an output file is reqired") - } - return nil } diff --git a/cmd/avrogen/main_test.go b/cmd/avrogen/main_test.go index 677277dc..42b73011 100644 --- a/cmd/avrogen/main_test.go +++ b/cmd/avrogen/main_test.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "flag" "io" "os" @@ -34,11 +35,6 @@ func TestAvroGen_RequiredFlags(t *testing.T) { args: []string{"avrogen", "-o", "some/file", "schema.avsc"}, wantErr: true, }, - { - name: "validates output file is set", - args: []string{"avrogen", "-pkg", "test", "schema.avsc"}, - wantErr: true, - }, { name: "validates tag format are valid", args: []string{"avrogen", "-o", "some/file", "-pkg", "test", "-tags", "snake", "schema.avsc"}, @@ -59,7 +55,7 @@ func TestAvroGen_RequiredFlags(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - got := realMain(test.args, io.Discard) + got := realMain(test.args, io.Discard, io.Discard) if !test.wantErr { assert.Equal(t, 0, got) @@ -71,6 +67,18 @@ func TestAvroGen_RequiredFlags(t *testing.T) { } } +func TestAvroGen_GeneratesSchemaStdout(t *testing.T) { + var buf bytes.Buffer + + args := []string{"avrogen", "-pkg", "testpkg", "testdata/schema.avsc"} + gotCode := realMain(args, io.Discard, &buf) + require.Equal(t, 0, gotCode) + + want, err := os.ReadFile("testdata/golden.go") + require.NoError(t, err) + assert.Equal(t, want, buf.Bytes()) +} + func TestAvroGen_GeneratesSchema(t *testing.T) { path, err := os.MkdirTemp("./", "avrogen") require.NoError(t, err) @@ -78,7 +86,7 @@ func TestAvroGen_GeneratesSchema(t *testing.T) { file := filepath.Join(path, "test.go") args := []string{"avrogen", "-pkg", "testpkg", "-o", file, "testdata/schema.avsc"} - gotCode := realMain(args, io.Discard) + gotCode := realMain(args, io.Discard, io.Discard) require.Equal(t, 0, gotCode) got, err := os.ReadFile(file) @@ -101,7 +109,7 @@ func TestAvroGen_GeneratesSchemaWithFullname(t *testing.T) { file := filepath.Join(path, "test.go") args := []string{"avrogen", "-pkg", "testpkg", "-o", file, "-fullname", "testdata/schema.avsc"} - gotCode := realMain(args, io.Discard) + gotCode := realMain(args, io.Discard, io.Discard) require.Equal(t, 0, gotCode) got, err := os.ReadFile(file) @@ -124,7 +132,7 @@ func TestAvroGen_GeneratesSchemaWithEncoders(t *testing.T) { file := filepath.Join(path, "test.go") args := []string{"avrogen", "-pkg", "testpkg", "-o", file, "-encoders", "testdata/schema.avsc"} - gotCode := realMain(args, io.Discard) + gotCode := realMain(args, io.Discard, io.Discard) require.Equal(t, 0, gotCode) got, err := os.ReadFile(file)