Skip to content

Commit 559723e

Browse files
authored
feat(gen): implement struct generation from schema registry subjects and versions (#498)
1 parent 29f45e2 commit 559723e

File tree

2 files changed

+70
-14
lines changed

2 files changed

+70
-14
lines changed

cmd/avrogen/main.go

+58-14
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,35 @@ package main
22

33
import (
44
"bytes"
5+
"context"
56
"errors"
67
"flag"
78
"fmt"
89
"io"
910
"os"
1011
"path/filepath"
12+
"strconv"
1113
"strings"
1214

1315
"github.com/hamba/avro/v2"
1416
"github.com/hamba/avro/v2/gen"
17+
"github.com/hamba/avro/v2/registry"
1518
"golang.org/x/tools/imports"
1619
)
1720

1821
type config struct {
1922
TemplateFileName string
2023

21-
Pkg string
22-
PkgDoc string
23-
Out string
24-
Tags string
25-
FullName bool
26-
Encoders bool
27-
FullSchema bool
28-
StrictTypes bool
29-
Initialisms string
24+
Pkg string
25+
PkgDoc string
26+
Out string
27+
Tags string
28+
FullName bool
29+
Encoders bool
30+
FullSchema bool
31+
StrictTypes bool
32+
Initialisms string
33+
SchemaRegistry string
3034
}
3135

3236
func main() {
@@ -47,6 +51,7 @@ func realMain(args []string, stdout, stderr io.Writer) int {
4751
flgs.BoolVar(&cfg.StrictTypes, "strict-types", false, "Use strict type sizes (e.g. int32) during generation.")
4852
flgs.StringVar(&cfg.Initialisms, "initialisms", "", "Custom initialisms <VAL>[,...] for struct and field names.")
4953
flgs.StringVar(&cfg.TemplateFileName, "template-filename", "", "Override output template with one loaded from file.")
54+
flgs.StringVar(&cfg.SchemaRegistry, "schemaregistry", "", "The URL to schema registry, e.g.: http://localhost:8081.")
5055
flgs.Usage = func() {
5156
_, _ = fmt.Fprintln(stderr, "Usage: avrogen [options] schemas")
5257
_, _ = fmt.Fprintln(stderr, "Options:")
@@ -88,13 +93,38 @@ func realMain(args []string, stdout, stderr io.Writer) int {
8893
gen.WithStrictTypes(cfg.StrictTypes),
8994
gen.WithFullSchema(cfg.FullSchema),
9095
}
96+
9197
g := gen.NewGenerator(cfg.Pkg, tags, opts...)
92-
for _, file := range flgs.Args() {
93-
schema, err := avro.ParseFiles(filepath.Clean(file))
94-
if err != nil {
95-
_, _ = fmt.Fprintf(stderr, "Error: %v\n", err)
96-
return 2
98+
for _, entry := range flgs.Args() {
99+
var schema avro.Schema
100+
101+
switch cfg.SchemaRegistry {
102+
case "":
103+
schema, err = avro.ParseFiles(filepath.Clean(entry))
104+
if err != nil {
105+
_, _ = fmt.Fprintf(stderr, "Error: %v\n", err)
106+
return 2
107+
}
108+
default:
109+
client, err := registry.NewClient(cfg.SchemaRegistry)
110+
if err != nil {
111+
_, _ = fmt.Fprintf(stderr, "Error: %v\n", err)
112+
return 2
113+
}
114+
115+
subject, version, err := parseSubjectVersion(entry)
116+
if err != nil {
117+
_, _ = fmt.Fprintf(stderr, "Error: %v\n", err)
118+
return 2
119+
}
120+
121+
schema, err = client.GetSchemaByVersion(context.Background(), subject, version)
122+
if err != nil {
123+
_, _ = fmt.Fprintf(stderr, "Error: %v\n", err)
124+
return 2
125+
}
97126
}
127+
98128
g.Parse(schema)
99129
}
100130

@@ -205,3 +235,17 @@ func loadTemplate(templateFileName string) ([]byte, error) {
205235
}
206236
return os.ReadFile(filepath.Clean(templateFileName))
207237
}
238+
239+
func parseSubjectVersion(entry string) (string, int, error) {
240+
parts := strings.Split(entry, ":")
241+
if len(parts) != 2 {
242+
return "", -1, errors.New("entry must be of format subject:version")
243+
}
244+
245+
version, err := strconv.Atoi(parts[1])
246+
if err != nil {
247+
return "", -1, err
248+
}
249+
250+
return parts[0], version, nil
251+
}

gen/gen.go

+12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type Config struct {
2727
StrictTypes bool
2828
Initialisms []string
2929
LogicalTypes []LogicalType
30+
Metadata any
3031
}
3132

3233
// TagStyle defines the styling for a tag.
@@ -85,6 +86,7 @@ func StructFromSchema(schema avro.Schema, w io.Writer, cfg Config) error {
8586
WithInitialisms(cfg.Initialisms),
8687
WithStrictTypes(cfg.StrictTypes),
8788
WithFullSchema(cfg.FullSchema),
89+
WithMetadata(cfg.Metadata),
8890
}
8991
for _, opt := range cfg.LogicalTypes {
9092
opts = append(opts, WithLogicalType(opt))
@@ -168,6 +170,13 @@ func WithFullSchema(b bool) OptsFunc {
168170
}
169171
}
170172

173+
// WithMetadata configures the generator to store the metadata within the generation context.
174+
func WithMetadata(m any) OptsFunc {
175+
return func(g *Generator) {
176+
g.metadata = m
177+
}
178+
}
179+
171180
// LogicalType used when the name of the "LogicalType" field in the Avro schema matches the Name attribute.
172181
type LogicalType struct {
173182
// Name of the LogicalType
@@ -213,6 +222,7 @@ type Generator struct {
213222
strictTypes bool
214223
initialisms []string
215224
logicalTypes map[avro.LogicalType]LogicalType
225+
metadata any
216226

217227
imports []string
218228
thirdPartyImports []string
@@ -451,12 +461,14 @@ func (g *Generator) Write(w io.Writer) error {
451461
Imports []string
452462
ThirdPartyImports []string
453463
Typedefs []typedef
464+
Metadata any
454465
}{
455466
WithEncoders: g.encoders,
456467
PackageName: g.pkg,
457468
PackageDoc: g.pkgdoc,
458469
Imports: append(g.imports, g.thirdPartyImports...),
459470
Typedefs: g.typedefs,
471+
Metadata: g.metadata,
460472
}
461473
return parsed.Execute(w, data)
462474
}

0 commit comments

Comments
 (0)