@@ -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
235243func (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+
276306func GeneratePartial (pkg * packages.Package , structName string , outPackage string , out io.Writer ) error {
277307 root := pkg .Types .Scope ().Lookup (structName )
278308
0 commit comments