Skip to content

Commit

Permalink
Fixes #3083: properly generate attributes with meta struct:pkg:path (
Browse files Browse the repository at this point in the history
…#3084)

* Fixes #3083: properly generate attributes with meta `struct:pkg:path`

What was the issue?

What was meant to be a check on the parentPkg to avoid recursive
definitions wasn't actually checking the parent package, but the package
of the field itself.

It was a bug that got introduced in #3022 and never worked as expected.

Solution:

Injecting the parent package for the initial definition seems to address
this.

* Update codegen/scope.go

* failing server encoding test

* Fix marshalling function argument type packages

When using "struct:pkg:path"

Co-authored-by: Raphael Simon <simon.raphael@gmail.com>
  • Loading branch information
Ernesto Jiménez and raphael committed Jul 12, 2022
1 parent 4bceaee commit ff403eb
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 108 deletions.
42 changes: 21 additions & 21 deletions codegen/go_transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func transformPrimitive(source, target *expr.AttributeExpr, sourceVar, targetVar
assign = ":="
}
if source.Type.Name() != target.Type.Name() {
cast := ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg)
cast := ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg(target))
return fmt.Sprintf("%s %s %s(%s)\n", targetVar, assign, cast, sourceVar), nil
}
return fmt.Sprintf("%s %s %s\n", targetVar, assign, sourceVar), nil
Expand Down Expand Up @@ -153,7 +153,7 @@ func transformObject(source, target *expr.AttributeExpr, sourceVar, targetVar st
if srcPtr {
deref = "*"
}
exp = fmt.Sprintf("%s(%s%s)", ta.TargetCtx.Scope.Ref(tgtc, ta.TargetCtx.Pkg), deref, srcField)
exp = fmt.Sprintf("%s(%s%s)", ta.TargetCtx.Scope.Ref(tgtc, ta.TargetCtx.Pkg(tgtc)), deref, srcField)
if srcPtr && !srcMatt.IsRequired(n) {
postInitCode += fmt.Sprintf("if %s != nil {\n", srcField)
if tgtPtr {
Expand Down Expand Up @@ -197,7 +197,7 @@ func transformObject(source, target *expr.AttributeExpr, sourceVar, targetVar st
if newVar {
assign = ":="
}
name := ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg, ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault)
name := ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg(target), ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault)
buffer.WriteString(fmt.Sprintf("%s %s %s%s{%s}\n", targetVar, assign, deref, name, initCode))
buffer.WriteString(postInitCode)

Expand Down Expand Up @@ -274,7 +274,7 @@ func transformObject(source, target *expr.AttributeExpr, sourceVar, targetVar st
code += fmt.Sprintf("var zero %s\n\t", typeName)
} else if _, ok := tgtc.Type.(expr.UserType); ok {
// aliased primitive
code += fmt.Sprintf("var zero %s\n\t", ta.TargetCtx.Scope.Ref(tgtc, ta.TargetCtx.Pkg))
code += fmt.Sprintf("var zero %s\n\t", ta.TargetCtx.Scope.Ref(tgtc, ta.TargetCtx.Pkg(tgtc)))
} else {
code += fmt.Sprintf("var zero %s\n\t", GoNativeTypeName(tgtc.Type))
}
Expand All @@ -297,7 +297,7 @@ func transformArray(source, target *expr.Array, sourceVar, targetVar string, new
return "", err
}
data := map[string]interface{}{
"ElemTypeRef": ta.TargetCtx.Scope.Ref(target.ElemType, ta.TargetCtx.Pkg),
"ElemTypeRef": ta.TargetCtx.Scope.Ref(target.ElemType, ta.TargetCtx.Pkg(target.ElemType)),
"SourceElem": source.ElemType,
"TargetElem": target.ElemType,
"SourceVar": sourceVar,
Expand All @@ -323,8 +323,8 @@ func transformMap(source, target *expr.Map, sourceVar, targetVar string, newVar
return "", err
}
data := map[string]interface{}{
"KeyTypeRef": ta.TargetCtx.Scope.Ref(target.KeyType, ta.TargetCtx.Pkg),
"ElemTypeRef": ta.TargetCtx.Scope.Ref(target.ElemType, ta.TargetCtx.Pkg),
"KeyTypeRef": ta.TargetCtx.Scope.Ref(target.KeyType, ta.TargetCtx.Pkg(target.KeyType)),
"ElemTypeRef": ta.TargetCtx.Scope.Ref(target.ElemType, ta.TargetCtx.Pkg(target.ElemType)),
"SourceKey": source.KeyType,
"TargetKey": target.KeyType,
"SourceElem": source.ElemType,
Expand Down Expand Up @@ -365,11 +365,11 @@ func transformUnion(source, target *expr.AttributeExpr, sourceVar, targetVar str
}
sourceTypeRefs := make([]string, len(srcUnion.Values))
for i, st := range srcUnion.Values {
sourceTypeRefs[i] = ta.TargetCtx.Scope.Ref(st.Attribute, ta.TargetCtx.Pkg)
sourceTypeRefs[i] = ta.TargetCtx.Scope.Ref(st.Attribute, ta.TargetCtx.Pkg(st.Attribute))
}
targetTypeNames := make([]string, len(tgtUnion.Values))
for i, tt := range tgtUnion.Values {
targetTypeNames[i] = ta.TargetCtx.Scope.Name(tt.Attribute, ta.TargetCtx.Pkg, ta.TargetCtx.Pointer, ta.TargetCtx.Pointer)
targetTypeNames[i] = ta.TargetCtx.Scope.Name(tt.Attribute, ta.TargetCtx.Pkg(tt.Attribute), ta.TargetCtx.Pointer, ta.TargetCtx.Pointer)
}
data := map[string]interface{}{
"SourceTypeRefs": sourceTypeRefs,
Expand All @@ -378,8 +378,8 @@ func transformUnion(source, target *expr.AttributeExpr, sourceVar, targetVar str
"SourceVar": sourceVar,
"TargetVar": targetVar,
"NewVar": newVar,
"TypeRef": ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg),
"TargetTypeName": ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg, ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault),
"TypeRef": ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg(target)),
"TargetTypeName": ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg(target), ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault),
"TransformAttrs": ta,
}
var buf bytes.Buffer
Expand All @@ -401,17 +401,17 @@ func transformUnionToObject(source, target *expr.AttributeExpr, sourceVar, targe
sourceTypeRefs := make([]string, len(srcUnion.Values))
sourceTypeNames := make([]string, len(srcUnion.Values))
for i, st := range srcUnion.Values {
sourceTypeRefs[i] = ta.SourceCtx.Scope.Ref(st.Attribute, ta.SourceCtx.Pkg)
sourceTypeRefs[i] = ta.SourceCtx.Scope.Ref(st.Attribute, ta.SourceCtx.Pkg(st.Attribute))
sourceTypeNames[i] = st.Name
}
data := map[string]interface{}{
"NewVar": newVar,
"TargetVar": targetVar,
"TypeRef": ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg),
"TypeRef": ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg(target)),
"SourceVar": sourceVar,
"SourceTypeRefs": sourceTypeRefs,
"SourceTypeNames": sourceTypeNames,
"TargetTypeName": ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg, ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault),
"TargetTypeName": ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg(target), ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault),
}
var buf bytes.Buffer
if err := transformGoUnionToObjectT.Execute(&buf, data); err != nil {
Expand All @@ -438,17 +438,17 @@ func transformObjectToUnion(source, target *expr.AttributeExpr, sourceVar, targe
targetTypeRefs := make([]string, len(tgtUnion.Values))
for i, tt := range tgtUnion.Values {
unionTypes[i] = tt.Name
targetTypeRefs[i] = ta.TargetCtx.Scope.Ref(tt.Attribute, ta.TargetCtx.Pkg)
targetTypeRefs[i] = ta.TargetCtx.Scope.Ref(tt.Attribute, ta.TargetCtx.Pkg(tt.Attribute))
}
data := map[string]interface{}{
"NewVar": newVar,
"TargetVar": targetVar,
"TypeRef": ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg),
"TypeRef": ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg(target)),
"SourceVar": sourceVar,
"SourceVarDeref": sourceVarDeref,
"UnionTypes": unionTypes,
"TargetTypeRefs": targetTypeRefs,
"TargetTypeName": ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg, ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault),
"TargetTypeName": ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg(target), ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault),
"Pointer": ta.SourceCtx.Pointer,
}
var buf bytes.Buffer
Expand Down Expand Up @@ -587,8 +587,8 @@ func generateHelper(source, target *expr.AttributeExpr, req bool, ta *TransformA
}
tfd := &TransformFunctionData{
Name: name,
ParamTypeRef: ta.SourceCtx.Scope.Ref(source, ta.SourceCtx.Pkg),
ResultTypeRef: ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg),
ParamTypeRef: ta.SourceCtx.Scope.Ref(source, ta.SourceCtx.Pkg(source)),
ResultTypeRef: ta.TargetCtx.Scope.Ref(target, ta.TargetCtx.Pkg(target)),
Code: code,
}
seen[name] = tfd
Expand Down Expand Up @@ -620,8 +620,8 @@ func transformHelperName(source, target *expr.AttributeExpr, ta *TransformAttrs)
prefix string
)
{
sname = Goify(ta.SourceCtx.Scope.Name(source, ta.SourceCtx.Pkg, ta.SourceCtx.Pointer, ta.SourceCtx.UseDefault), true)
tname = Goify(ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg, ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault), true)
sname = Goify(ta.SourceCtx.Scope.Name(source, ta.SourceCtx.Pkg(source), ta.SourceCtx.Pointer, ta.SourceCtx.UseDefault), true)
tname = Goify(ta.TargetCtx.Scope.Name(target, ta.TargetCtx.Pkg(target), ta.TargetCtx.Pointer, ta.TargetCtx.UseDefault), true)
prefix = ta.Prefix
if prefix == "" {
prefix = "transform"
Expand Down
16 changes: 8 additions & 8 deletions codegen/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,13 @@ func (s *NameScope) Name(name string) string {
// useDefault if true indicates that the attribute must not be a pointer
// if it has a default value.
func (s *NameScope) GoTypeDef(att *expr.AttributeExpr, ptr, useDefault bool) string {
return s.goTypeDef(att, ptr, useDefault, "")
pkg := ""
if loc := UserTypeLocation(att.Type); loc != nil {
pkg = loc.PackageName()
} else if p, ok := att.Meta.Last("struct:pkg:path"); ok && p != "" {
pkg = p
}
return s.goTypeDef(att, ptr, useDefault, pkg)
}

func (s *NameScope) goTypeDef(att *expr.AttributeExpr, ptr, useDefault bool, pkg string) string {
Expand Down Expand Up @@ -138,13 +144,7 @@ func (s *NameScope) goTypeDef(att *expr.AttributeExpr, ptr, useDefault bool, pkg
)
{
fn = GoifyAtt(at, name, true)
var parentPkg string
if ut, ok := at.Type.(expr.UserType); ok {
if UserTypeLocation(ut) != nil {
parentPkg = UserTypeLocation(ut).PackageName()
}
}
tdef = s.goTypeDef(at, ptr, useDefault, parentPkg)
tdef = s.goTypeDef(at, ptr, useDefault, pkg)
if expr.IsObject(at.Type) ||
att.IsPrimitivePointer(name, useDefault) ||
(ptr && expr.IsPrimitive(at.Type) && at.Type.Kind() != expr.AnyKind && at.Type.Kind() != expr.BytesKind) {
Expand Down
3 changes: 2 additions & 1 deletion codegen/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ func Files(genpkg string, service *expr.ServiceExpr, userTypePkgs map[string][]s
if len(secs) == 0 {
continue
}
h := codegen.Header("User types", codegen.Goify(filepath.Base(p), false), nil)
fn := filepath.Base(p)
h := codegen.Header("User types", codegen.Goify(fn[:len(fn)-len(filepath.Ext(fn))], false), nil)
sections := append([]*codegen.SectionTemplate{h}, secs...)
files = append(files, &codegen.File{Path: filepath.Join(codegen.Gendir, p), SectionTemplates: sections})
}
Expand Down
2 changes: 1 addition & 1 deletion codegen/service/service_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -1678,7 +1678,7 @@ func buildConstructorCode(src, tgt *expr.AttributeExpr, sourceVar, targetVar str
"ArgVar": sourceVar,
"ReturnVar": targetVar,
"IsCollection": arr != nil,
"TargetType": targetCtx.Scope.Name(tgt, targetCtx.Pkg, targetCtx.Pointer, targetCtx.UseDefault),
"TargetType": targetCtx.Scope.Name(tgt, targetCtx.Pkg(tgt), targetCtx.Pointer, targetCtx.UseDefault),
}

if arr != nil {
Expand Down
1 change: 1 addition & 0 deletions codegen/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func TestStructPkgPath(t *testing.T) {
{"multiple", testdata.PkgPathMultipleDSL, []string{testdata.PkgPathMultiple}, []string{barPath, bazPath}, []string{testdata.PkgPathBar, testdata.PkgPathBaz}},
{"nopkg", testdata.PkgPathNoDirDSL, []string{testdata.PkgPathNoDir}, nil, nil},
{"dupes", testdata.PkgPathDupeDSL, []string{testdata.PkgPathDupe1, testdata.PkgPathDupe2}, []string{fooPath}, []string{testdata.PkgPathFooDupe}},
{"payload_attribute", testdata.PkgPathPayloadAttributeDSL, []string{testdata.PkgPathPayloadAttribute}, []string{fooPath}, []string{testdata.PkgPathPayloadAttributeFoo}},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
Expand Down
33 changes: 31 additions & 2 deletions codegen/service/testdata/service_code.go
Original file line number Diff line number Diff line change
Expand Up @@ -2508,13 +2508,13 @@ var MethodNames = [3]string{"A", "B", "EnvelopedB"}
// EnvelopedBPayload is the payload type of the MultiplePkgPathMethod service
// EnvelopedB method.
type EnvelopedBPayload struct {
Baz *Baz
Baz *baz.Baz
}
// EnvelopedBResult is the result type of the MultiplePkgPathMethod service
// EnvelopedB method.
type EnvelopedBResult struct {
Baz *Baz
Baz *baz.Baz
}
`

Expand Down Expand Up @@ -2617,3 +2617,32 @@ const ServiceName = "PkgPathDupeMethod2"
// MethodKey key.
var MethodNames = [2]string{"A", "B"}
`

const PkgPathPayloadAttribute = `
// Service is the PkgPathPayloadAttributeDSL service interface.
type Service interface {
// Foo implements Foo.
FooEndpoint(context.Context, *Bar) (res *Bar, err error)
}
// ServiceName is the name of the service as defined in the design. This is the
// same value that is set in the endpoint request contexts under the ServiceKey
// key.
const ServiceName = "PkgPathPayloadAttributeDSL"
// MethodNames lists the service method names as defined in the design. These
// are the same values that are set in the endpoint request contexts under the
// MethodKey key.
var MethodNames = [1]string{"Foo"}
// Bar is the payload type of the PkgPathPayloadAttributeDSL service Foo method.
type Bar struct {
Foo *foo.Foo
}
`

const PkgPathPayloadAttributeFoo = `
type Foo struct {
IntField *int
}
`
17 changes: 17 additions & 0 deletions codegen/service/testdata/service_dsls.go
Original file line number Diff line number Diff line change
Expand Up @@ -899,3 +899,20 @@ var PkgPathDupeDSL = func() {
})
})
}

var PkgPathPayloadAttributeDSL = func() {
var Foo = Type("Foo", func() {
Attribute("IntField", Int)
Meta("struct:pkg:path", "foo")
})
var Bar = Type("Bar", func() {
Attribute("Foo", Foo)
})

Service("PkgPathPayloadAttributeDSL", func() {
Method("Foo", func() {
Payload(Bar)
Result(Bar)
})
})
}
17 changes: 13 additions & 4 deletions codegen/transformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ type (
// primitive types are non-pointers if they are required, otherwise they
// are pointers.
UseDefault bool
// Pkg is the package name where the attribute type is found.
Pkg string
// Scope is the attribute scope.
Scope Attributor
// defaultPkg is the default package name where the attribute
// type is found. it can be overridden via struct:pkg:path meta.
defaultPkg string
}

// AttributeScope contains the scope of an attribute. It implements the
Expand Down Expand Up @@ -89,8 +90,8 @@ func NewAttributeContext(pointer, reqIgnore, useDefault bool, pkg string, scope
Pointer: pointer,
IgnoreRequired: reqIgnore,
UseDefault: useDefault,
Pkg: pkg,
Scope: NewAttributeScope(scope),
defaultPkg: pkg,
}
}

Expand Down Expand Up @@ -222,14 +223,22 @@ func (a *AttributeContext) IsRequired(name string, att *expr.AttributeExpr) bool
return att.IsRequired(name)
}

// Pkg returns the package name of the given type.
func (a *AttributeContext) Pkg(att *expr.AttributeExpr) string {
if loc := UserTypeLocation(att.Type); loc != nil {
return loc.PackageName()
}
return a.defaultPkg
}

// Dup creates a shallow copy of the AttributeContext.
func (a *AttributeContext) Dup() *AttributeContext {
return &AttributeContext{
Pointer: a.Pointer,
IgnoreRequired: a.IgnoreRequired,
UseDefault: a.UseDefault,
Pkg: a.Pkg,
Scope: a.Scope,
defaultPkg: a.defaultPkg,
}
}

Expand Down
10 changes: 7 additions & 3 deletions codegen/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,18 @@ func TestGoTypeDef(t *testing.T) {
stringMetaType = expr.MetaExpr{"struct:field:type": []string{"string"}}
jsonWithImportMetaType = expr.MetaExpr{"struct:field:type": []string{"json.RawMessage", "encoding/json"}}
jsonWithRenameMetaType = expr.MetaExpr{"struct:field:type": []string{"jason.RawMessage", "encoding/json", "jason"}}
mixedObj = &expr.AttributeExpr{
structPkgPathMetaType = expr.MetaExpr{"struct:pkg:path": []string{"types"}}
utPkgPathMeta = &expr.UserTypeExpr{AttributeExpr: &expr.AttributeExpr{Type: expr.Boolean, Meta: structPkgPathMetaType}, TypeName: "UserType"}

mixedObj = &expr.AttributeExpr{
Type: &expr.Object{
{"IntField", &expr.AttributeExpr{Type: expr.Int}},
{"ArrayField", simpleArray},
{"MapField", simpleMap},
{"UserTypeField", userType},
{"MetaTypeField", &expr.AttributeExpr{Type: expr.Int, Meta: jsonWithImportMetaType}},
{"QualifiedMetaTypeField", &expr.AttributeExpr{Type: expr.Int, Meta: jsonWithRenameMetaType}},
{"StructPkgPath", &expr.AttributeExpr{Type: utPkgPathMeta}},
},
Validation: &expr.ValidationExpr{Required: []string{"IntField", "ArrayField", "MapField", "UserTypeField", "MetaTypeField", "QualifiedMetaTypeField"}}}
)
Expand Down Expand Up @@ -71,8 +75,8 @@ func TestGoTypeDef(t *testing.T) {
"Object": {requiredObj, false, true, "struct {\n\tIntField int\n\tStringField string\n}"},
"ObjDefault": {defaultObj, false, true, "struct {\n\tIntField int\n\tStringField string\n}"},
"ObjDefaultNoDef": {defaultObj, false, false, "struct {\n\tIntField *int\n\tStringField *string\n}"},
"ObjMixed": {mixedObj, false, true, "struct {\n\tIntField int\n\tArrayField []bool\n\tMapField map[int]string\n\tUserTypeField UserType\n\tMetaTypeField json.RawMessage\n\tQualifiedMetaTypeField jason.RawMessage\n}"},
"ObjMixedPointer": {mixedObj, true, true, "struct {\n\tIntField *int\n\tArrayField []bool\n\tMapField map[int]string\n\tUserTypeField *UserType\n\tMetaTypeField *json.RawMessage\n\tQualifiedMetaTypeField *jason.RawMessage\n}"},
"ObjMixed": {mixedObj, false, true, "struct {\n\tIntField int\n\tArrayField []bool\n\tMapField map[int]string\n\tUserTypeField UserType\n\tMetaTypeField json.RawMessage\n\tQualifiedMetaTypeField jason.RawMessage\n\tStructPkgPath *types.UserType\n}"},
"ObjMixedPointer": {mixedObj, true, true, "struct {\n\tIntField *int\n\tArrayField []bool\n\tMapField map[int]string\n\tUserTypeField *UserType\n\tMetaTypeField *json.RawMessage\n\tQualifiedMetaTypeField *jason.RawMessage\n\tStructPkgPath *types.UserType\n}"},

"MetaTypeSameAsDesign": {&expr.AttributeExpr{Type: expr.String, Meta: stringMetaType}, false, true, "string"},
"MetaTypeOverrideDesign": {&expr.AttributeExpr{Type: expr.String, Meta: jsonWithImportMetaType}, false, true, "json.RawMessage"},
Expand Down
4 changes: 2 additions & 2 deletions codegen/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ func recurseValidationCode(att *expr.AttributeExpr, attCtx *AttributeContext, re
return ""
}
var buf bytes.Buffer
name := attCtx.Scope.Name(att, ctx.Pkg, ctx.Pointer, ctx.UseDefault)
name := attCtx.Scope.Name(att, ctx.Pkg(att), ctx.Pointer, ctx.UseDefault)
data := map[string]interface{}{"name": Goify(name, true), "target": tgt}
if err := userValT.Execute(&buf, data); err != nil {
panic(err) // bug
Expand Down Expand Up @@ -313,7 +313,7 @@ func recurseAttribute(att *expr.AttributeExpr, attCtx *AttributeContext, nat *ex
if expr.IsPrimitive(nat.Attribute.Type) {
buf.Write(recurseValidationCode(ut.Attribute(), attCtx, att.IsRequired(nat.Name), true, tgt, context, seen).Bytes())
} else {
name := attCtx.Scope.Name(nat.Attribute, attCtx.Pkg, attCtx.Pointer, attCtx.UseDefault)
name := attCtx.Scope.Name(nat.Attribute, attCtx.Pkg(nat.Attribute), attCtx.Pointer, attCtx.UseDefault)
if err := userValT.Execute(&buf, map[string]interface{}{"name": Goify(name, true), "target": tgt}); err != nil {
panic(err) // bug
}
Expand Down

0 comments on commit ff403eb

Please sign in to comment.