Skip to content

Commit f29e701

Browse files
committed
gen: allow use of pre-generated Partials
Add support for using pre-Partialized structs in either local or remote packages by simple name matching. This permits embedding of the `Values` struct of a "render" package into its `chart` subpackage while retaining the ability to generate a fully partialized struct. Additionally, this technically allows packages to provide their own partialized variants but that functionality is unused.
1 parent 6f4a476 commit f29e701

File tree

3 files changed

+69
-13
lines changed

3 files changed

+69
-13
lines changed

gen/partial/partial.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func (g *Generator) Generate(t types.Type) []ast.Node {
146146
Tok: token.TYPE,
147147
Specs: []ast.Spec{
148148
&ast.TypeSpec{
149-
Name: &ast.Ident{Name: "Partial" + named.Obj().Name()},
149+
Name: &ast.Ident{Name: g.partialName(named.Obj().Name())},
150150
TypeParams: params,
151151
Type: g.typeToNode(partialized).(ast.Expr),
152152
},
@@ -216,8 +216,16 @@ func (g *Generator) partializeStruct(t *types.Struct) *types.Struct {
216216

217217
partialized := g.partialize(field.Type())
218218
switch partialized.Underlying().(type) {
219-
case *types.Basic, *types.Struct:
219+
case *types.Basic:
220220
partialized = types.NewPointer(partialized)
221+
case *types.Struct:
222+
// Embedding of pointer values is kinda weird, so we don't do
223+
// it. TODO: this should technically only be done if no JSON tag
224+
// is explicitly specified (e.g. ObjectMeta) but we don't currently have any such
225+
// cases.
226+
if !field.Embedded() {
227+
partialized = types.NewPointer(partialized)
228+
}
221229
}
222230

223231
// TODO Docs injection would be nice but given that we're crawling the
@@ -233,12 +241,30 @@ func (g *Generator) partializeStruct(t *types.Struct) *types.Struct {
233241
}
234242

235243
func (g *Generator) partializeNamed(t *types.Named) types.Type {
244+
// If there exists a Partial___ variant of the type, we'll use this. This
245+
// allows Partial structs to references partial structs from other packages
246+
// that contain Partialized structs and/or allows end users to provide
247+
// "manual" implementations of certain types.
248+
inPkg := t.Obj().Pkg() == g.pkg.Types
249+
partialName := g.partialName(t.Obj().Name())
250+
if obj := t.Obj().Pkg().Scope().Lookup(partialName); obj != nil {
251+
// Normally, we'd rely on build flags here but redpanda's values rely
252+
// on console's PartialValues. Instead, we check if the file looks like
253+
// a generated files as this will always resolve to the results of the
254+
// previous generation for redpanda.
255+
// To be a bit more accurate, we could alternatively get the full
256+
// source file and manually check for the go:build constraint.
257+
srcFile := g.pkg.Fset.Position(obj.Pos()).Filename
258+
if !inPkg || !strings.HasSuffix(srcFile, ".gen.go") {
259+
return obj.Type()
260+
}
261+
}
262+
236263
// This check isn't going to be correct in the long run but it's intention
237264
// boils down to "Have we generated a Partialized version of this named
238265
// type?"
239266
// NB: This check MUST match the check in FindAllNames.
240-
isPartialized := t.Obj().Pkg() == g.pkg.Types && !IsType[*types.Basic](t.Underlying())
241-
267+
isPartialized := inPkg && !IsType[*types.Basic](t.Underlying())
242268
if !isPartialized {
243269
// If we haven't partialized this type, there's nothing we can do. Noop.
244270
return t
@@ -261,7 +287,7 @@ func (g *Generator) partializeNamed(t *types.Named) types.Type {
261287
params[i] = types.NewTypeParam(param.Obj(), param.Constraint())
262288
}
263289

264-
named := types.NewNamed(types.NewTypeName(0, g.pkg.Types, "Partial"+t.Obj().Name(), t.Underlying()), t.Underlying(), nil)
290+
named := types.NewNamed(types.NewTypeName(0, g.pkg.Types, partialName, t.Underlying()), t.Underlying(), nil)
265291
if len(args) < 1 {
266292
return named
267293
}
@@ -273,6 +299,10 @@ func (g *Generator) partializeNamed(t *types.Named) types.Type {
273299
return result
274300
}
275301

302+
func (g *Generator) partialName(name string) string {
303+
return "Partial" + name
304+
}
305+
276306
func GeneratePartial(pkg *packages.Package, structName string, outPackage string, out io.Writer) error {
277307
root := pkg.Types.Scope().Lookup(structName)
278308

gen/partial/partial_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ type ExampleStruct struct {
8383
H3 map[string]GenericStruct[int]
8484
H4 map[string]GenericStruct[NestedStruct]
8585
H5 MapAlias
86+
87+
I1 CustomPartial
88+
89+
J1 HasEmbedded
8690
}
8791

8892
type NestedStruct struct {
@@ -93,6 +97,18 @@ type GenericStruct[T any] struct {
9397
Foo T
9498
}
9599

100+
type (
101+
CustomPartial struct{}
102+
PartialCustomPartial struct{}
103+
)
104+
105+
type (
106+
Embedded struct{}
107+
HasEmbedded struct {
108+
Embedded
109+
}
110+
)
111+
96112
func TestGenerateParital(t *testing.T) {
97113
pkgs, err := packages.Load(&packages.Config{
98114
Mode: mode,

gen/partial/testdata/partial.go

Lines changed: 18 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)