Add support for binding with a custom marshal/unmarshal function (#104)
## Summary: This is useful if you want to bind to a type you don't control (or use for other things) but need different serialization than its default. This is a feature gqlgen has and we've found it very useful. For example, in webapp we want to bind `DateTime` to `time.Time`, but its default serialization is not compatible with Python, so currently we have to bind to a wrapper type and cast all over the place, which is exactly the sort of boilerplate genqlient is supposed to avoid. For unmarshaling, the implementation basically just follows the existing support for abstract types; instead of calling our own generated helper, we now call your specified function. This required some refactoring to abstract the handling of custom unmarshalers generally from abstract types specifically, and to wire in not only the unmarshaler-name but also the `generator` (in order to compute the right import alias). For marshaling, I had to implement all that stuff over again; it's mostly parallel to unmarshaling (and I made a few minor changes to unmarshaling to make the two more parallel). Luckily, after #103 I at least only had to do it once, rather than implementing the same functionality for arguments and for input-type fields. It was still quite a bit of code; I didn't try to be quite as completionist about the tests as with unmarshal but still had to add a few. Issue: https://github.com/Khan/genqlient/issues/38 ## Test plan: make check Author: benjaminjkraft Reviewers: StevenACoffman, dnerdy, benjaminjkraft, aberkan, jvoll, mahtabsabet, MiguelCastillo Required Reviewers: Approved By: StevenACoffman, dnerdy Checks: ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint Pull Request URL: https://github.com/Khan/genqlient/pull/104
This commit is contained in:
@@ -59,15 +59,18 @@ type ComplexityRoot struct {
|
||||
}
|
||||
|
||||
Query struct {
|
||||
Being func(childComplexity int, id string) int
|
||||
Beings func(childComplexity int, ids []string) int
|
||||
Fail func(childComplexity int) int
|
||||
LotteryWinner func(childComplexity int, number int) int
|
||||
Me func(childComplexity int) int
|
||||
User func(childComplexity int, id *string) int
|
||||
Being func(childComplexity int, id string) int
|
||||
Beings func(childComplexity int, ids []string) int
|
||||
Fail func(childComplexity int) int
|
||||
LotteryWinner func(childComplexity int, number int) int
|
||||
Me func(childComplexity int) int
|
||||
User func(childComplexity int, id *string) int
|
||||
UsersBornOn func(childComplexity int, date string) int
|
||||
UsersBornOnDates func(childComplexity int, dates []string) int
|
||||
}
|
||||
|
||||
User struct {
|
||||
Birthdate func(childComplexity int) int
|
||||
Hair func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
LuckyNumber func(childComplexity int) int
|
||||
@@ -81,6 +84,8 @@ type QueryResolver interface {
|
||||
Being(ctx context.Context, id string) (Being, error)
|
||||
Beings(ctx context.Context, ids []string) ([]Being, error)
|
||||
LotteryWinner(ctx context.Context, number int) (Lucky, error)
|
||||
UsersBornOn(ctx context.Context, date string) ([]*User, error)
|
||||
UsersBornOnDates(ctx context.Context, dates []string) ([]*User, error)
|
||||
Fail(ctx context.Context) (*bool, error)
|
||||
}
|
||||
|
||||
@@ -210,6 +215,37 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.Query.User(childComplexity, args["id"].(*string)), true
|
||||
|
||||
case "Query.usersBornOn":
|
||||
if e.complexity.Query.UsersBornOn == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Query_usersBornOn_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Query.UsersBornOn(childComplexity, args["date"].(string)), true
|
||||
|
||||
case "Query.usersBornOnDates":
|
||||
if e.complexity.Query.UsersBornOnDates == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Query_usersBornOnDates_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Query.UsersBornOnDates(childComplexity, args["dates"].([]string)), true
|
||||
|
||||
case "User.birthdate":
|
||||
if e.complexity.User.Birthdate == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.User.Birthdate(childComplexity), true
|
||||
|
||||
case "User.hair":
|
||||
if e.complexity.User.Hair == nil {
|
||||
break
|
||||
@@ -288,12 +324,16 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er
|
||||
}
|
||||
|
||||
var sources = []*ast.Source{
|
||||
{Name: "../schema.graphql", Input: `type Query {
|
||||
{Name: "../schema.graphql", Input: `scalar Date
|
||||
|
||||
type Query {
|
||||
me: User
|
||||
user(id: ID): User
|
||||
being(id: ID!): Being
|
||||
beings(ids: [ID!]!): [Being]!
|
||||
lotteryWinner(number: Int!): Lucky
|
||||
usersBornOn(date: Date!): [User!]!
|
||||
usersBornOnDates(dates: [Date!]!): [User!]!
|
||||
fail: Boolean
|
||||
}
|
||||
|
||||
@@ -302,6 +342,7 @@ type User implements Being & Lucky {
|
||||
name: String!
|
||||
luckyNumber: Int
|
||||
hair: Hair
|
||||
birthdate: Date
|
||||
}
|
||||
|
||||
type Hair { color: String } # silly name to confuse the name-generator
|
||||
@@ -412,6 +453,36 @@ func (ec *executionContext) field_Query_user_args(ctx context.Context, rawArgs m
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query_usersBornOnDates_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 []string
|
||||
if tmp, ok := rawArgs["dates"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("dates"))
|
||||
arg0, err = ec.unmarshalNDate2ᚕstringᚄ(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["dates"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query_usersBornOn_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 string
|
||||
if tmp, ok := rawArgs["date"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("date"))
|
||||
arg0, err = ec.unmarshalNDate2string(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["date"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
@@ -877,6 +948,90 @@ func (ec *executionContext) _Query_lotteryWinner(ctx context.Context, field grap
|
||||
return ec.marshalOLucky2githubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐLucky(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_usersBornOn(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Query",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
rawArgs := field.ArgumentMap(ec.Variables)
|
||||
args, err := ec.field_Query_usersBornOn_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
fc.Args = args
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Query().UsersBornOn(rctx, args["date"].(string))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.([]*User)
|
||||
fc.Result = res
|
||||
return ec.marshalNUser2ᚕᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐUserᚄ(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_usersBornOnDates(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Query",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
rawArgs := field.ArgumentMap(ec.Variables)
|
||||
args, err := ec.field_Query_usersBornOnDates_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
fc.Args = args
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Query().UsersBornOnDates(rctx, args["dates"].([]string))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.([]*User)
|
||||
fc.Result = res
|
||||
return ec.marshalNUser2ᚕᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐUserᚄ(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_fail(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@@ -1114,6 +1269,38 @@ func (ec *executionContext) _User_hair(ctx context.Context, field graphql.Collec
|
||||
return ec.marshalOHair2ᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐHair(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _User_birthdate(ctx context.Context, field graphql.CollectedField, obj *User) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "User",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Birthdate, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*string)
|
||||
fc.Result = res
|
||||
return ec.marshalODate2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@@ -2413,6 +2600,34 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
||||
res = ec._Query_lotteryWinner(ctx, field)
|
||||
return res
|
||||
})
|
||||
case "usersBornOn":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Query_usersBornOn(ctx, field)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "usersBornOnDates":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Query_usersBornOnDates(ctx, field)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "fail":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
@@ -2464,6 +2679,8 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
|
||||
out.Values[i] = ec._User_luckyNumber(ctx, field, obj)
|
||||
case "hair":
|
||||
out.Values[i] = ec._User_hair(ctx, field, obj)
|
||||
case "birthdate":
|
||||
out.Values[i] = ec._User_birthdate(ctx, field, obj)
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
@@ -2772,6 +2989,51 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNDate2string(ctx context.Context, v interface{}) (string, error) {
|
||||
res, err := graphql.UnmarshalString(v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNDate2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler {
|
||||
res := graphql.MarshalString(v)
|
||||
if res == graphql.Null {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNDate2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) {
|
||||
var vSlice []interface{}
|
||||
if v != nil {
|
||||
if tmp1, ok := v.([]interface{}); ok {
|
||||
vSlice = tmp1
|
||||
} else {
|
||||
vSlice = []interface{}{v}
|
||||
}
|
||||
}
|
||||
var err error
|
||||
res := make([]string, len(vSlice))
|
||||
for i := range vSlice {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
|
||||
res[i], err = ec.unmarshalNDate2string(ctx, vSlice[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNDate2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
for i := range v {
|
||||
ret[i] = ec.marshalNDate2string(ctx, sel, v[i])
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) {
|
||||
res, err := graphql.UnmarshalID(v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
@@ -2857,6 +3119,53 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNUser2ᚕᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐUserᚄ(ctx context.Context, sel ast.SelectionSet, v []*User) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
isLen1 := len(v) == 1
|
||||
if !isLen1 {
|
||||
wg.Add(len(v))
|
||||
}
|
||||
for i := range v {
|
||||
i := i
|
||||
fc := &graphql.FieldContext{
|
||||
Index: &i,
|
||||
Result: &v[i],
|
||||
}
|
||||
ctx := graphql.WithFieldContext(ctx, fc)
|
||||
f := func(i int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
if !isLen1 {
|
||||
defer wg.Done()
|
||||
}
|
||||
ret[i] = ec.marshalNUser2ᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐUser(ctx, sel, v[i])
|
||||
}
|
||||
if isLen1 {
|
||||
f(i)
|
||||
} else {
|
||||
go f(i)
|
||||
}
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNUser2ᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐUser(ctx context.Context, sel ast.SelectionSet, v *User) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._User(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler {
|
||||
return ec.___Directive(ctx, sel, &v)
|
||||
}
|
||||
@@ -3124,6 +3433,21 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast
|
||||
return graphql.MarshalBoolean(*v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalODate2ᚖstring(ctx context.Context, v interface{}) (*string, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
res, err := graphql.UnmarshalString(v)
|
||||
return &res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalODate2ᚖstring(ctx context.Context, sel ast.SelectionSet, v *string) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
return graphql.MarshalString(*v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOHair2ᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐHair(ctx context.Context, sel ast.SelectionSet, v *Hair) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
|
||||
@@ -35,10 +35,11 @@ type Hair struct {
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LuckyNumber *int `json:"luckyNumber"`
|
||||
Hair *Hair `json:"hair"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LuckyNumber *int `json:"luckyNumber"`
|
||||
Hair *Hair `json:"hair"`
|
||||
Birthdate *string `json:"birthdate"`
|
||||
}
|
||||
|
||||
func (User) IsBeing() {}
|
||||
|
||||
@@ -15,7 +15,8 @@ func intptr(v int) *int { return &v }
|
||||
var users = []*User{
|
||||
{
|
||||
ID: "1", Name: "Yours Truly", LuckyNumber: intptr(17),
|
||||
Hair: &Hair{Color: strptr("Black")},
|
||||
Birthdate: strptr("2025-01-01"),
|
||||
Hair: &Hair{Color: strptr("Black")},
|
||||
},
|
||||
{ID: "2", Name: "Raven", LuckyNumber: intptr(-1), Hair: nil},
|
||||
}
|
||||
@@ -40,6 +41,18 @@ func userByID(id string) *User {
|
||||
return nil
|
||||
}
|
||||
|
||||
func usersByBirthdates(dates []string) []*User {
|
||||
var retval []*User
|
||||
for _, date := range dates {
|
||||
for _, user := range users {
|
||||
if user.Birthdate != nil && *user.Birthdate == date {
|
||||
retval = append(retval, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval
|
||||
}
|
||||
|
||||
func beingByID(id string) Being {
|
||||
for _, user := range users {
|
||||
if id == user.ID {
|
||||
@@ -86,6 +99,14 @@ func (r *queryResolver) LotteryWinner(ctx context.Context, number int) (Lucky, e
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) UsersBornOn(ctx context.Context, date string) ([]*User, error) {
|
||||
return usersByBirthdates([]string{date}), nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) UsersBornOnDates(ctx context.Context, dates []string) ([]*User, error) {
|
||||
return usersByBirthdates(dates), nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Fail(ctx context.Context) (*bool, error) {
|
||||
f := true
|
||||
return &f, fmt.Errorf("oh no")
|
||||
|
||||
Reference in New Issue
Block a user