diff --git a/_example/web-server-gen/main.go b/_example/web-server-gen/main.go index 7338d8d..f85c4af 100644 --- a/_example/web-server-gen/main.go +++ b/_example/web-server-gen/main.go @@ -8,11 +8,15 @@ package main import ( "context" "fmt" + "time" + + "go.osspkg.com/logx" "go.osspkg.com/goppy/v3" "go.osspkg.com/goppy/v3/_example/web-server-gen/transport" "go.osspkg.com/goppy/v3/_example/web-server-gen/types" "go.osspkg.com/goppy/v3/web" + "go.osspkg.com/goppy/v3/web/jsonrpc" ) func main() { @@ -21,11 +25,22 @@ func main() { app := goppy.New("app_name", "v1.0.0", "app description") app.Plugins( web.WithServer(), + jsonrpc.WithTransport( + jsonrpc.Path("/rpc"), + jsonrpc.Timeout(5*time.Second), + jsonrpc.ErrHandler(func(method string, err error) error { + logx.Error("json-rpc call failed", "method", method, "err", err) + return fmt.Errorf("json-rpc call failed: %w", err) + }), + ), ) app.Plugins( NewController, - func(routes web.ServerPool, c *Controller) *transport.JSONRPCHandler { - return transport.NewJSONRPCHandler(routes, c, c, c) + func(t jsonrpc.Transport, c *Controller) error { + t.Inject(transport.NewJSONRPCApiTransport(c, []string{"main"})) + t.Inject(transport.NewJSONRPCUserTransport(c, []string{"main"})) + t.Inject(transport.NewJSONRPCPostTransport(c, []string{"main"})) + return nil }, ) app.Run() diff --git a/_example/web-server-gen/transport/jsonrpc_handler.go b/_example/web-server-gen/transport/jsonrpc_api_handler.go similarity index 56% rename from _example/web-server-gen/transport/jsonrpc_handler.go rename to _example/web-server-gen/transport/jsonrpc_api_handler.go index 42f9da7..4032d63 100644 --- a/_example/web-server-gen/transport/jsonrpc_handler.go +++ b/_example/web-server-gen/transport/jsonrpc_api_handler.go @@ -7,20 +7,46 @@ package transport import ( - nethttp "net/http" + context "context" + time "time" + + fmt "fmt" web "go.osspkg.com/goppy/v3/web" - time "time" + jsonrpc "go.osspkg.com/goppy/v3/web/jsonrpc" - context "context" + nethttp "net/http" stdjson "encoding/json" - fmt "fmt" + types "go.osspkg.com/goppy/v3/_example/web-server-gen/types" ) -func (v *JSONRPCHandler) callApiRoot(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { +type JSONRPCApiTransport struct { + handle types.Api + tags []string +} + +func NewJSONRPCApiTransport(handle types.Api, tags []string) *JSONRPCApiTransport { + return &JSONRPCApiTransport{ + handle: handle, + tags: tags, + } +} + +func (v *JSONRPCApiTransport) RouteTags() []string { + return v.tags +} + +func (v *JSONRPCApiTransport) JSONRPCApiHandlers() map[string]jsonrpc.THandleFunc { + return map[string]jsonrpc.THandleFunc{ + "api.root": v.CallRoot, + "api.auth": v.CallAuth, + } +} + +func (v *JSONRPCApiTransport) CallRoot(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { var req jsonrpcApiRootModelRequest err := stdjson.Unmarshal(param, &req) if err != nil { @@ -36,13 +62,13 @@ func (v *JSONRPCHandler) callApiRoot(ctx context.Context, webCtx web.Ctx, param return nil, err } - res.Status, err = v.handleApi.Root(ctx, inUserID, req.UserName) + res.Status, err = v.handle.Root(ctx, inUserID, req.UserName) if err != nil { return nil, err } return res, nil } -func (v *JSONRPCHandler) callApiAuth(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { +func (v *JSONRPCApiTransport) CallAuth(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { var req jsonrpcApiAuthModelRequest err := stdjson.Unmarshal(param, &req) if err != nil { @@ -99,49 +125,7 @@ func (v *JSONRPCHandler) callApiAuth(ctx context.Context, webCtx web.Ctx, param }) }() - outStatus, err = v.handleApi.Auth(ctx, inUserID, req.UserName) - if err != nil { - return nil, err - } - return res, nil -} -func (v *JSONRPCHandler) callUserName(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { - var req jsonrpcUserNameModelRequest - err := stdjson.Unmarshal(param, &req) - if err != nil { - err = fmt.Errorf("invalid request: %w", err) - return nil, err - } - var res jsonrpcUserNameModelResponse - res.Name, err = v.handleUser.Name(ctx, req.UserID) - if err != nil { - return nil, err - } - return res, nil -} -func (v *JSONRPCHandler) callPostByID(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { - var req jsonrpcPostByIDModelRequest - err := stdjson.Unmarshal(param, &req) - if err != nil { - err = fmt.Errorf("invalid request: %w", err) - return nil, err - } - var res jsonrpcPostByIDModelResponse - res.Text, err = v.handlePost.ByID(ctx, req.ID) - if err != nil { - return nil, err - } - return res, nil -} -func (v *JSONRPCHandler) callPostList(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { - var req jsonrpcPostListModelRequest - err := stdjson.Unmarshal(param, &req) - if err != nil { - err = fmt.Errorf("invalid request: %w", err) - return nil, err - } - var res jsonrpcPostListModelResponse - res.Text, err = v.handlePost.List(ctx, req.UserID) + outStatus, err = v.handle.Auth(ctx, inUserID, req.UserName) if err != nil { return nil, err } diff --git a/_example/web-server-gen/transport/jsonrpc_post_handler.go b/_example/web-server-gen/transport/jsonrpc_post_handler.go new file mode 100644 index 0000000..9305b84 --- /dev/null +++ b/_example/web-server-gen/transport/jsonrpc_post_handler.go @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +// Code generated by goppy-cli wsg. DO NOT EDIT. +package transport + +import ( + context "context" + stdjson "encoding/json" + + types "go.osspkg.com/goppy/v3/_example/web-server-gen/types" + + fmt "fmt" + + web "go.osspkg.com/goppy/v3/web" + + jsonrpc "go.osspkg.com/goppy/v3/web/jsonrpc" +) + +type JSONRPCPostTransport struct { + handle types.Post + tags []string +} + +func NewJSONRPCPostTransport(handle types.Post, tags []string) *JSONRPCPostTransport { + return &JSONRPCPostTransport{ + handle: handle, + tags: tags, + } +} + +func (v *JSONRPCPostTransport) RouteTags() []string { + return v.tags +} + +func (v *JSONRPCPostTransport) JSONRPCApiHandlers() map[string]jsonrpc.THandleFunc { + return map[string]jsonrpc.THandleFunc{ + "post.byid": v.CallByID, + "post.list": v.CallList, + } +} + +func (v *JSONRPCPostTransport) CallByID(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { + var req jsonrpcPostByIDModelRequest + err := stdjson.Unmarshal(param, &req) + if err != nil { + err = fmt.Errorf("invalid request: %w", err) + return nil, err + } + var res jsonrpcPostByIDModelResponse + res.Text, err = v.handle.ByID(ctx, req.ID) + if err != nil { + return nil, err + } + return res, nil +} +func (v *JSONRPCPostTransport) CallList(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { + var req jsonrpcPostListModelRequest + err := stdjson.Unmarshal(param, &req) + if err != nil { + err = fmt.Errorf("invalid request: %w", err) + return nil, err + } + var res jsonrpcPostListModelResponse + res.Text, err = v.handle.List(ctx, req.UserID) + if err != nil { + return nil, err + } + return res, nil +} diff --git a/_example/web-server-gen/transport/jsonrpc_transport.go b/_example/web-server-gen/transport/jsonrpc_transport.go deleted file mode 100644 index 70c449c..0000000 --- a/_example/web-server-gen/transport/jsonrpc_transport.go +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. - * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. - */ - -// Code generated by goppy-cli wsg. DO NOT EDIT. -package transport - -import ( - context "context" - errors "errors" - - web "go.osspkg.com/goppy/v3/web" - - time "time" - - strings "strings" - - syncing "go.osspkg.com/syncing" - - pool "go.osspkg.com/ioutils/pool" - - logx "go.osspkg.com/logx" - - types "go.osspkg.com/goppy/v3/_example/web-server-gen/types" -) - -var ( - poolBaseResponse = pool.New[*syncing.Slice[baseResponse]](func() *syncing.Slice[baseResponse] { - return syncing.NewSlice[baseResponse](uint(2)) - }) - - errUnsupportedMethod = errors.New("unsupported method") -) - -type JSONRPCHandler struct { - routes web.ServerPool - handleApi types.Api - handleUser types.User - handlePost types.Post -} - -func NewJSONRPCHandler(routes web.ServerPool, handleApi types.Api, handleUser types.User, handlePost types.Post) *JSONRPCHandler { - return &JSONRPCHandler{ - routes: routes, - handleApi: handleApi, - handleUser: handleUser, - handlePost: handlePost, - } -} - -func (v *JSONRPCHandler) Down() error { - return nil -} - -func (v *JSONRPCHandler) Up(ctx context.Context) error { - v.routes.All(func(tag string, r web.Router) { - switch tag { - case "main", "admin": - r.Post("/rpc", func(webCtx web.Ctx) { - var req bulkRequest - if err := webCtx.BindJSON(&req); err != nil { - webCtx.String(400, err.Error()) - return - } - - res := poolBaseResponse.Get() - defer poolBaseResponse.Put(res) - - wg := syncing.NewGroup(webCtx.Context()) - wg.OnPanic(func(err error) { - logx.Error("panic", "err", err) - }) - - for _, item := range req { - item := item - switch strings.ToLower(item.Method) { - case "api.root": - wg.Background("api.root", func(ctx context.Context) { - out := baseResponse{ - Id: item.Id, - } - var err error - var result any - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() - result, err = v.callApiRoot(ctx, webCtx, item.Params) - if err != nil { - out.Error = toJSONRPCError(err) - } else { - out.Result = result - } - res.Append(out) - }) - - case "api.auth": - wg.Background("api.auth", func(ctx context.Context) { - out := baseResponse{ - Id: item.Id, - } - var err error - var result any - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() - result, err = v.callApiAuth(ctx, webCtx, item.Params) - if err != nil { - out.Error = toJSONRPCError(err) - } else { - out.Result = result - } - res.Append(out) - }) - - case "user.name": - wg.Background("user.name", func(ctx context.Context) { - out := baseResponse{ - Id: item.Id, - } - var err error - var result any - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() - result, err = v.callUserName(ctx, webCtx, item.Params) - if err != nil { - out.Error = toJSONRPCError(err) - } else { - out.Result = result - } - res.Append(out) - }) - - case "post.byid": - wg.Background("post.byid", func(ctx context.Context) { - out := baseResponse{ - Id: item.Id, - } - var err error - var result any - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() - result, err = v.callPostByID(ctx, webCtx, item.Params) - if err != nil { - out.Error = toJSONRPCError(err) - } else { - out.Result = result - } - res.Append(out) - }) - - case "post.list": - wg.Background("post.list", func(ctx context.Context) { - out := baseResponse{ - Id: item.Id, - } - var err error - var result any - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() - result, err = v.callPostList(ctx, webCtx, item.Params) - if err != nil { - out.Error = toJSONRPCError(err) - } else { - out.Result = result - } - res.Append(out) - }) - - default: - out := baseResponse{ - Id: item.Id, - } - err := errUnsupportedMethod - out.Error = toJSONRPCError(err) - res.Append(out) - - } - } - wg.Wait() - webCtx.JSON(200, bulkResponse(res.Extract())) - }) - - } - }) - return nil -} diff --git a/_example/web-server-gen/transport/jsonrpc_user_handler.go b/_example/web-server-gen/transport/jsonrpc_user_handler.go new file mode 100644 index 0000000..efdd344 --- /dev/null +++ b/_example/web-server-gen/transport/jsonrpc_user_handler.go @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +// Code generated by goppy-cli wsg. DO NOT EDIT. +package transport + +import ( + context "context" + fmt "fmt" + + stdjson "encoding/json" + + web "go.osspkg.com/goppy/v3/web" + + jsonrpc "go.osspkg.com/goppy/v3/web/jsonrpc" + + types "go.osspkg.com/goppy/v3/_example/web-server-gen/types" +) + +type JSONRPCUserTransport struct { + handle types.User + tags []string +} + +func NewJSONRPCUserTransport(handle types.User, tags []string) *JSONRPCUserTransport { + return &JSONRPCUserTransport{ + handle: handle, + tags: tags, + } +} + +func (v *JSONRPCUserTransport) RouteTags() []string { + return v.tags +} + +func (v *JSONRPCUserTransport) JSONRPCApiHandlers() map[string]jsonrpc.THandleFunc { + return map[string]jsonrpc.THandleFunc{ + "user.name": v.CallName, + } +} + +func (v *JSONRPCUserTransport) CallName(ctx context.Context, webCtx web.Ctx, param stdjson.RawMessage) (any, error) { + var req jsonrpcUserNameModelRequest + err := stdjson.Unmarshal(param, &req) + if err != nil { + err = fmt.Errorf("invalid request: %w", err) + return nil, err + } + var res jsonrpcUserNameModelResponse + res.Name, err = v.handle.Name(ctx, req.UserID) + if err != nil { + return nil, err + } + return res, nil +} diff --git a/_example/web-server-gen/types/custom.go b/_example/web-server-gen/types/custom.go deleted file mode 100644 index 6db6310..0000000 --- a/_example/web-server-gen/types/custom.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. - * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. - */ - -package types - -import ( - "context" - "fmt" -) - -func StdOut(ctx context.Context, arg []Text) error { - fmt.Println(arg) - return nil -} diff --git a/_example/web-server-gen/types/interfaces1.go b/_example/web-server-gen/types/interfaces1.go index 0e35170..134e33f 100644 --- a/_example/web-server-gen/types/interfaces1.go +++ b/_example/web-server-gen/types/interfaces1.go @@ -9,7 +9,7 @@ import "context" type Api interface { // Root - // @wsg in.userID=cookie:x-user-id + // @tb in.userID=cookie:x-user-id Root( ctx context.Context, userID int64, @@ -17,9 +17,9 @@ type Api interface { ) (status bool, err error) // Auth - // @wsg in.userID=header:x-user-id - // @wsg out.status=header:x-user-id,cookie:uid - // @wsg out.status=cookie:uid + // @tb in.userID=header:x-user-id + // @tb out.status=header:x-user-id,cookie:uid + // @tb out.status=cookie:uid Auth( ctx context.Context, userID int64, diff --git a/_example/web-server-gen/types/wsg.go b/_example/web-server-gen/types/tb.go similarity index 71% rename from _example/web-server-gen/types/wsg.go rename to _example/web-server-gen/types/tb.go index 96ca6c8..9803b49 100644 --- a/_example/web-server-gen/types/wsg.go +++ b/_example/web-server-gen/types/tb.go @@ -5,4 +5,4 @@ package types -//go:generate goppy wsg --mod=json-rpc,rest --pool=main,admin --out=./../transport +//go:generate goppy tb --mod=json-rpc,rest --iface=Api,User,Post --out=./../transport diff --git a/apigen/builder/builder.go b/apigen/builder/builder.go index e5fd289..74221ef 100644 --- a/apigen/builder/builder.go +++ b/apigen/builder/builder.go @@ -23,7 +23,6 @@ import ( type Builder struct { Out string Mods []string - Pool []string IFace map[string]struct{} Files []at.File } @@ -88,7 +87,7 @@ func (b *Builder) Build() error { continue } - err := mod.Build(b, at.GlobalMeta{PkgName: pkgName, Pool: b.Pool}, files) + err := mod.Build(b, at.GlobalMeta{PkgName: pkgName}, files) if err != nil { return fmt.Errorf("build module %q: %w", name, err) } diff --git a/apigen/module/mod-json-rpc/build_base_model.go b/apigen/module/mod-json-rpc/build_base_model.go deleted file mode 100644 index e88d5c3..0000000 --- a/apigen/module/mod-json-rpc/build_base_model.go +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. - * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. - */ - -package mod_json_rpc - -import ( - "fmt" - - . "go.osspkg.com/gogen/golang" //nolint:staticcheck - - at "go.osspkg.com/goppy/v3/apigen/types" - "go.osspkg.com/goppy/v3/apigen/util" -) - -func (v Module) buildBaseRPCModel(w at.Writer, m at.GlobalMeta) error { - baseReq := Comment(jsonGenComment). - Type().ID(modelBaseReq).Struct(). - Block( - ID(util.ToUpperCamelCase(fieldID)).String(). - Raw(fmt.Sprintf("`json:\"%s\"`", fieldID)), - ID(util.ToUpperCamelCase(fieldMethod)).String(). - Raw(fmt.Sprintf("`json:\"%s\"`", fieldMethod)), - ID(util.ToUpperCamelCase(fieldParams)).Pkg("stdjson").ID("RawMessage"). - Raw(fmt.Sprintf("`json:\"%s\"`", fieldParams)), - ) - - bulkBaseReq := Comment(jsonGenComment). - Type().ID(modelBulkBaseReq).Slice().ID(modelBaseReq) - - errRes := Comment(jsonGenComment). - Type().ID(modelBaseErr).Struct(). - Block( - ID(util.ToUpperCamelCase(fieldMessage)).String(). - Raw(fmt.Sprintf("`json:\"%s\"`", fieldMessage)), - ID(util.ToUpperCamelCase(fieldCode)).Int64(). - Raw(fmt.Sprintf("`json:\"%s\"`", fieldCode)), - ID(util.ToUpperCamelCase(fieldCtx)).Map(String(), String()). - Raw(fmt.Sprintf("`json:\"%s,omitempty\"`", fieldCtx)), - ) - - errType := Type().ID(errInterface).Interface(). - Block( - ID("GetCode").Bracket().Int64(), - ID("GetMessage").Bracket().String(), - ID("GetContext").Bracket().Map(String(), String()), - ) - - baseRes := Comment(jsonGenComment). - Type().ID(modelBaseRes).Struct(). - Block( - ID(util.ToUpperCamelCase(fieldID)).String(). - Raw(fmt.Sprintf("`json:\"%s\"`", fieldID)), - ID(util.ToUpperCamelCase(fieldResult)).Any(). - Raw(fmt.Sprintf("`json:\"%s,omitempty\"`", fieldResult)), - ID(util.ToUpperCamelCase(fieldError)).Op("*").ID("errResponse"). - Raw(fmt.Sprintf("`json:\"%s,omitempty\"`", fieldError)), - ) - - bulkBaseRes := Comment(jsonGenComment). - Type().ID(modelBulkBaseRes).Slice().ID(modelBaseRes) - - toErr := Func().ID("toJSONRPCError"). - Bracket(ID("e").Error()). - Bracket(Op("*").ID(modelBaseErr)).Block( - If().ID("e").Op("==").Nil().Block(Return().Nil()), - ID("err").Op(":=").Op("&").ID(modelBaseErr).Block(), - List(ID("te"), ID("ok")).Op(":=").ID("e").Op(".").Bracket(ID(errInterface)), - If().ID("ok").Block( - ID("err").Op(".").ID("Code").Op("="). - ID("te").Op(".").ID("GetCode").Bracket(), - ID("err").Op(".").ID("Message").Op("="). - ID("te").Op(".").ID("GetMessage").Bracket(), - ID("err").Op(".").ID("Ctx").Op("="). - ID("te").Op(".").ID("GetContext").Bracket(), - ).Else().Block( - ID("err").Op(".").ID("Message").Op("="). - ID("e").Op(".").ID("Error").Bracket(), - ), - Return().ID("err"), - ) - - t := Comment("Code generated by goppy-cli wsg. DO NOT EDIT."). - Package(m.PkgName). - Comment("go:generate easyjson"). - Import("stdjson", "encoding/json"). - Join(baseReq, bulkBaseReq, baseRes, bulkBaseRes, errRes, errType, toErr) - - return w.WriteFile(v.FilePrefix+"_model_base.go", t) -} diff --git a/apigen/module/mod-json-rpc/build_transport.go b/apigen/module/mod-json-rpc/build_transport.go deleted file mode 100644 index 42de296..0000000 --- a/apigen/module/mod-json-rpc/build_transport.go +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. - * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. - */ - -package mod_json_rpc - -import ( - "strings" - - . "go.osspkg.com/gogen/golang" //nolint:staticcheck - "go.osspkg.com/gogen/types" - "go.osspkg.com/syncing" - - at "go.osspkg.com/goppy/v3/apigen/types" - "go.osspkg.com/goppy/v3/apigen/util" -) - -func (v Module) buildTransport(w at.Writer, m at.GlobalMeta, files []at.File) error { - - t := Comment("Code generated by goppy-cli wsg. DO NOT EDIT."). - Package(m.PkgName) - - list := syncing.NewMap[string, string](10) - list.Set("context", "context") - list.Set("strings", "strings") - list.Set("errors", "errors") - //list.Set("stdjson", "encoding/json") - list.Set("syncing", "go.osspkg.com/syncing") - list.Set("pool", "go.osspkg.com/ioutils/pool") - list.Set("web", "go.osspkg.com/goppy/v3/web") - list.Set("logx", "go.osspkg.com/logx") - list.Set("time", "time") - - for _, file := range files { - for _, object := range file.Faces { - list.Set(object.Alias, object.Pkg) - for alias, link := range file.Imports.Yield() { - list.Set(alias, link) - } - } - } - - for alias, link := range list.Yield() { - t.Import(alias, link) - } - - fields := []types.Token{ - ID("routes").Pkg("web").ID("ServerPool"), - } - fieldsInit := []types.Token{ - ID("routes").Op(":").ID("routes").Op(","), - } - for _, file := range files { - for _, object := range file.Faces { - fields = append(fields, - ID("handle"+object.Name).Pkg(object.Alias).ID(object.Name), - ) - fieldsInit = append(fieldsInit, - ID("handle"+object.Name).Op(":").ID("handle"+object.Name).Op(","), - ) - } - } - - t = t.Join( - Raw(` -var ( - poolBaseResponse = pool.New[*syncing.Slice[baseResponse]](func() *syncing.Slice[baseResponse] { - return syncing.NewSlice[baseResponse](uint(2)) - }) - - errUnsupportedMethod = errors.New("unsupported method") -) -`), - Type().ID(transportName).Struct().Block(fields...), - Line(), - //-- New - Func().ID("New"+transportName).Bracket(fields...).Op("*").ID(transportName).Block( - Return().Op("&").ID(transportName).Block(fieldsInit...), - ), - Line(), - // -- Down - Func().Bracket(ID("v").Op("*").ID(transportName)). - ID("Down").Bracket().Error().Block(Return().Nil()), - // -- Up - Line(), - Func().Bracket(ID("v").Op("*").ID(transportName)). - ID("Up").Bracket(ID("ctx").Pkg("context").ID("Context")).Error(). - Block(v.buildUp(m, files)), - ) - - if err := w.WriteFile(v.FilePrefix+"_transport.go", t); err != nil { - return err - } - - return nil -} - -func (v Module) buildUp(m at.GlobalMeta, files []at.File) types.Token { - tokBase := ID("r").Op(".").ID("Post").Call( - Text("/rpc"), - Func().Bracket(ID("webCtx").Pkg("web").ID("Ctx")).Block( - // --- - Var().ID("req").ID(modelBulkBaseReq), - If(). - ID("err").Op(":="). - ID("webCtx").Op(".").ID("BindJSON").Bracket(Op("&").ID("req")).Op(";"). - ID("err").Op("!=").Nil().Block( - ID("webCtx").Op(".").ID("String").Bracket( - Raw("400"), ID("err").Op(".").ID("Error").Call(), - ), - Return(), - ), - // --- - Line(), - ID("res").Op(":=").ID("poolBaseResponse").Op(".").ID("Get").Call(), - Defer().ID("poolBaseResponse").Op(".").ID("Put").Call(ID("res")), - Line(), - ID("wg").Op(":=").Pkg("syncing").ID("NewGroup").Bracket( - ID("webCtx").Op(".").ID("Context").Call(), - ), - ID("wg").Op(".").ID("OnPanic").Bracket( - Func().Bracket(ID("err").Error()).Block( - Pkg("logx").ID("Error").Call( - Text("panic"), Text("err"), ID("err"), - ), - ), - ), - // --- - Line(), - For().List(Raw("_"), ID("item")).Op(":=").Range().ID("req").Block( - ID("item").Op(":=").ID("item"), - v.buildMethods(files), - ), - ID("wg").Op(".").ID("Wait").Call(), - ID("webCtx").Op(".").ID("JSON").Bracket( - Raw("200"), ID(modelBulkBaseRes).Bracket( - ID("res").Op(".").ID("Extract").Bracket(), - ), - ), - ), - ) - - var poolTags []types.Token //nolint:prealloc - for _, s := range m.Pool { - poolTags = append(poolTags, Text(s)) - } - - return ID("v").Op(".").ID("routes").Op(".").ID("All"). - Bracket( - Func().Bracket(ID("tag").String(), ID("r").Pkg("web").ID("Router")).Block( - Switch().ID("tag").Block( - Case().List(poolTags...).Op(":").Line().Join(tokBase), - ), - ), - ). - Line().Return().Nil() -} - -func (v Module) buildMethods(files []at.File) *Tokens { - var list []types.Token - - for _, file := range files { - for _, object := range file.Faces { - for _, method := range object.Methods { - methodName := strings.ToLower(object.Name + "." + method.Name) - list = append(list, - Case().Text(methodName).Op(":").Join( - ID("wg").Op(".").ID("Background").Call( - Text(methodName), - Func().Bracket(ID("ctx").Pkg("context").ID("Context")).Block( - ID("out").Op(":=").ID(modelBaseRes).Block( - ID(util.ToUpperCamelCase(fieldID)).Op(":"). - ID("item").Op(".").ID(util.ToUpperCamelCase(fieldID)).Op(","), - ), - - Var().ID("err").Error(), - Var().ID("result").Any(), - List(ID("ctx"), ID("cancel")).Op(":=").Pkg("context").ID("WithTimeout").Call(ID( - "ctx"), Raw("5").Op("*").Pkg("time").ID("Second")), - Defer().ID("cancel").Call(), - - List(ID("result"), ID("err")).Op("="). - ID("v").Op(".").ID("call"+object.Name+method.Name). - Call( - ID("ctx"), - ID("webCtx"), - ID("item").Op(".").ID(util.ToUpperCamelCase(fieldParams)), - ), - - If().ID("err").Op("!=").Nil().Block( - ID("out").Op(".").ID(util.ToUpperCamelCase(fieldError)).Op("="). - ID("toJSONRPCError").Bracket(ID("err")), - ).Else().Block( - ID("out").Op(".").ID(util.ToUpperCamelCase(fieldResult)).Op("="). - ID("result"), - ), - ID("res").Op(".").ID("Append").Bracket(ID("out")), - ), - ), - ), - ) - } - } - } - - list = append(list, - Default().Op(":").Join( - ID("out").Op(":=").ID(modelBaseRes).Block( - ID(util.ToUpperCamelCase(fieldID)).Op(":"). - ID("item").Op(".").ID(util.ToUpperCamelCase(fieldID)).Op(","), - ), - ID("err").Op(":=").ID("errUnsupportedMethod"), - ID("out").Op(".").ID(util.ToUpperCamelCase(fieldError)).Op("="). - ID("toJSONRPCError").Bracket(ID("err")), - - ID("res").Op(".").ID("Append").Bracket(ID("out")), - ), - ) - - return Switch().Pkg("strings").ID("ToLower").Bracket( - ID("item").Op(".").ID(util.ToUpperCamelCase(fieldMethod)), - ).Block(list...) -} diff --git a/apigen/module/mod-json-rpc/build_transport_handlers.go b/apigen/module/mod-json-rpc/build_transport_handlers.go index 8d01b14..687e222 100644 --- a/apigen/module/mod-json-rpc/build_transport_handlers.go +++ b/apigen/module/mod-json-rpc/build_transport_handlers.go @@ -7,6 +7,7 @@ package mod_json_rpc import ( "fmt" + "strings" "go.osspkg.com/do" . "go.osspkg.com/gogen/golang" //nolint:staticcheck @@ -18,200 +19,257 @@ import ( ) func (v Module) buildTransportHandlers(w at.Writer, m at.GlobalMeta, files []at.File) error { - t := Comment("Code generated by goppy-cli wsg. DO NOT EDIT."). - Package(m.PkgName) + for _, file := range files { + for _, face := range file.Faces { + t := Comment("Code generated by goppy-cli wsg. DO NOT EDIT."). + Package(m.PkgName) - list := syncing.NewMap[string, string](10) - list.Set("context", "context") - list.Set("stdjson", "encoding/json") - list.Set("web", "go.osspkg.com/goppy/v3/web") + list := syncing.NewMap[string, string](10) + list.Set("context", "context") + list.Set("fmt", "fmt") + list.Set("stdjson", "encoding/json") + list.Set("stdjson", "encoding/json") + list.Set("web", "go.osspkg.com/goppy/v3/web") + list.Set("jsonrpc", "go.osspkg.com/goppy/v3/web/jsonrpc") - var handlers []types.Token - for _, file := range files { - handlers = append(handlers, v.buildTransportHandler(list, file)...) - } + handlers := v.buildTransport(list, face) + handlers = append(handlers, v.buildTransportHandler(list, file.Imports, face)...) - for alias, link := range list.Yield() { - t.Import(alias, link) - } + for alias, link := range list.Yield() { + t.Import(alias, link) + } - if err := w.WriteFile(v.FilePrefix+"_handler.go", t.Join(handlers...)); err != nil { - return err + if err := w.WriteFile(v.FilePrefix+"_"+strings.ToLower(face.Name)+"_handler.go", t.Join(handlers...)); err != nil { + return err + } + } } return nil } -func (v Module) buildTransportHandler(imp at.ImportSetter, file at.File) []types.Token { - var models []types.Token - - for _, object := range file.Faces { - for _, method := range object.Methods { - handle := Func().Bracket(ID("v").Op("*").ID(transportName)). - ID("call"+object.Name+method.Name).Bracket( - ID("ctx").Pkg("context").ID("Context"), - ID("webCtx").Pkg("web").ID("Ctx"), - ID("param").Pkg("stdjson").ID("RawMessage"), - ).Bracket( - Any(), - Error(), - ) +func (v Module) buildTransport( + imp at.ImportSetter, object at.Face, +) (out []types.Token) { - var handleSrc []types.Token + imp.Set(object.Alias, object.Pkg) - handleSrc = append(handleSrc, - Var().ID("req").ID(fmt.Sprintf(modelNameReq, object.Name+method.Name)), - ID("err").Op(":=").Pkg("stdjson").ID("Unmarshal").Bracket( - ID("param"), Op("&").ID("req"), + fields := []types.Token{} + fieldsInit := []types.Token{} + + fields = append(fields, + ID("handle").Pkg(object.Alias).ID(object.Name), + ID("tags").Slice().String(), + ) + fieldsInit = append(fieldsInit, + ID("handle").Op(":").ID("handle").Op(","), + ID("tags").Op(":").ID("tags").Op(","), + ) + + trName := fmt.Sprintf(transportName, object.Name) + + out = append(out, + Type().ID(trName).Struct().Block(fields...), + Line(), + //-- New + Func().ID("New"+trName).Bracket(fields...).Op("*").ID(trName).Block( + Return().Op("&").ID(trName).Block(fieldsInit...), + ), + Line(), + Func().Bracket(ID("v").Op("*").ID(trName)). + ID("RouteTags").Bracket().Slice().String(). + Block( + Return().ID("v").Op(".").ID("tags"), + ), + Line(), + Func().Bracket(ID("v").Op("*").ID(trName)). + ID("JSONRPCApiHandlers").Bracket().Map(String(), Pkg("jsonrpc").ID("THandleFunc")). + Block( + Return().Map(String(), Pkg("jsonrpc").ID("THandleFunc")).Block( + func() []types.Token { + var models []types.Token + for _, method := range object.Methods { + models = append(models, + Text(strings.ToLower(object.Name+"."+method.Name)).Op(":"). + ID("v").Op(".").ID("Call"+method.Name).Op(","), + ) + } + return models + }()..., ), - If().ID("err").Op("!=").Nil().Block( - ID("err").Op("=").Pkg("fmt").ID("Errorf").Bracket( - Text("invalid request: %w"), - ID("err"), - ), - Return().List( - Nil(), - ID("err"), - )), - Var().ID("res").ID(fmt.Sprintf(modelNameRes, object.Name+method.Name)), - ) + ), + Line(), + ) + + return +} + +func (v Module) buildTransportHandler(imp at.ImportSetter, imports *syncing.Map[string, string], object at.Face) []types.Token { + var models []types.Token //nolint:prealloc + + trName := fmt.Sprintf(transportName, object.Name) - // -------------------------------------- + for _, method := range object.Methods { + handle := Func().Bracket(ID("v").Op("*").ID(trName)). + ID("Call"+method.Name).Bracket( + ID("ctx").Pkg("context").ID("Context"), + ID("webCtx").Pkg("web").ID("Ctx"), + ID("param").Pkg("stdjson").ID("RawMessage"), + ).Bracket( + Any(), + Error(), + ) - inParamArgs := make(map[string]string) - for _, p := range method.InParams { - vals, ok := method.Tags["in."+p.Name] + var handleSrc []types.Token + + handleSrc = append(handleSrc, + Var().ID("req").ID(fmt.Sprintf(modelNameReq, object.Name+method.Name)), + ID("err").Op(":=").Pkg("stdjson").ID("Unmarshal").Bracket( + ID("param"), Op("&").ID("req"), + ), + If().ID("err").Op("!=").Nil().Block( + ID("err").Op("=").Pkg("fmt").ID("Errorf").Bracket( + Text("invalid request: %w"), + ID("err"), + ), + Return().List( + Nil(), + ID("err"), + )), + Var().ID("res").ID(fmt.Sprintf(modelNameRes, object.Name+method.Name)), + ) + + // -------------------------------------- + + inParamArgs := make(map[string]string) + for _, p := range method.InParams { + vals, ok := method.Tags["in."+p.Name] + if !ok { + continue + } + paramName := "in" + util.ToUpperCamelCase(p.Name) + inParamArgs[p.Name] = paramName + handleSrc = append(handleSrc, + Var().ID(paramName). + Raw(do.IfElse(p.Slice, "[]", "")). + Raw(do.IfElse(p.Ptr, "*", "")). + Pkg(p.Pkg).ID(p.Type), + ) + for _, item := range vals { + modName, modVal, modArgs := at.TagSplit(item) + mod, ok := at.Resolve[at.ParamModule](modName) if !ok { continue } - paramName := "in" + util.ToUpperCamelCase(p.Name) - inParamArgs[p.Name] = paramName - handleSrc = append(handleSrc, - Var().ID(paramName). - Raw(do.IfElse(p.Slice, "[]", "")). - Raw(do.IfElse(p.Ptr, "*", "")). - Pkg(p.Pkg).ID(p.Type), + j := &at.Join{Tok: Comment("Module: " + mod.Name())} + err := mod.Build(j, at.ParamMeta{ + Type: at.ParamIn, + CodeName: paramName, + Import: imp, + Value: modVal, + Args: modArgs, + }, p) + util.PanicIfError(err, + "failed to build module %s: method: %s.%s, param: %s", + modName, object.Name, method.Name, p.Name, ) - for _, item := range vals { - modName, modVal, modArgs := at.TagSplit(item) - mod, ok := at.Resolve[at.ParamModule](modName) - if !ok { - continue - } - j := &at.Join{Tok: Comment("Module: " + mod.Name())} - err := mod.Build(j, at.ParamMeta{ - Type: at.ParamIn, - CodeName: paramName, - Import: imp, - Value: modVal, - Args: modArgs, - }, p) - util.PanicIfError(err, - "failed to build module %s: method: %s.%s, param: %s", - modName, object.Name, method.Name, p.Name, - ) - handleSrc = append(handleSrc, j.Tok) - } + handleSrc = append(handleSrc, j.Tok) } + } - var ( - handleIn []types.Token - ) - for _, p := range method.InParams { - - switch { - case p.Pkg == "context" && p.Type == "Context": - handleIn = append(handleIn, ID("ctx")) - default: - if name, ok := inParamArgs[p.Name]; ok { - if link, ok := file.Imports.Get(p.Pkg); ok { - imp.Set(p.Pkg, link) - } + var ( + handleIn []types.Token + ) + for _, p := range method.InParams { - handleIn = append(handleIn, ID(name)) - } else { - handleIn = append(handleIn, ID("req").Op(".").ID(util.ToUpperCamelCase(p.Name))) + switch { + case p.Pkg == "context" && p.Type == "Context": + handleIn = append(handleIn, ID("ctx")) + default: + if name, ok := inParamArgs[p.Name]; ok { + if link, ok := imports.Get(p.Pkg); ok { + imp.Set(p.Pkg, link) } + + handleIn = append(handleIn, ID(name)) + } else { + handleIn = append(handleIn, ID("req").Op(".").ID(util.ToUpperCamelCase(p.Name))) } } + } - // -------------------------------------- + // -------------------------------------- - outParamArgs := make(map[string]string) - for _, p := range method.OutParams { - vals, ok := method.Tags["out."+p.Name] + outParamArgs := make(map[string]string) + for _, p := range method.OutParams { + vals, ok := method.Tags["out."+p.Name] + if !ok { + continue + } + paramName := "out" + util.ToUpperCamelCase(p.Name) + outParamArgs[p.Name] = paramName + handleSrc = append(handleSrc, + Var().ID(paramName). + Raw(do.IfElse(p.Slice, "[]", "")). + Raw(do.IfElse(p.Ptr, "*", "")). + Pkg(p.Pkg).ID(p.Type), + ) + for _, item := range vals { + modName, modVal, modArgs := at.TagSplit(item) + mod, ok := at.Resolve[at.ParamModule](modName) if !ok { continue } - paramName := "out" + util.ToUpperCamelCase(p.Name) - outParamArgs[p.Name] = paramName + j := &at.Join{Tok: Comment("Module: " + mod.Name())} + err := mod.Build(j, at.ParamMeta{ + Type: at.ParamOut, + CodeName: paramName, + Import: imp, + Value: modVal, + Args: modArgs, + }, p) + util.PanicIfError(err, + "failed to build module %s: method: %s.%s, param: %s", + modName, object.Name, method.Name, p.Name, + ) handleSrc = append(handleSrc, - Var().ID(paramName). - Raw(do.IfElse(p.Slice, "[]", "")). - Raw(do.IfElse(p.Ptr, "*", "")). - Pkg(p.Pkg).ID(p.Type), + Defer().Func().Bracket().Block(j.Tok).Bracket(), ) - for _, item := range vals { - modName, modVal, modArgs := at.TagSplit(item) - mod, ok := at.Resolve[at.ParamModule](modName) - if !ok { - continue - } - j := &at.Join{Tok: Comment("Module: " + mod.Name())} - err := mod.Build(j, at.ParamMeta{ - Type: at.ParamOut, - CodeName: paramName, - Import: imp, - Value: modVal, - Args: modArgs, - }, p) - util.PanicIfError(err, - "failed to build module %s: method: %s.%s, param: %s", - modName, object.Name, method.Name, p.Name, - ) - handleSrc = append(handleSrc, - Defer().Func().Bracket().Block(j.Tok).Bracket(), - ) - } } + } - var ( - handleOut []types.Token - ) - for _, p := range method.OutParams { - - switch { //nolint:staticcheck - case p.Type == "error": - handleOut = append(handleOut, ID("err")) - default: - if name, ok := outParamArgs[p.Name]; ok { - if link, ok := file.Imports.Get(p.Pkg); ok { - imp.Set(p.Pkg, link) - } + var ( + handleOut []types.Token + ) + for _, p := range method.OutParams { - handleOut = append(handleOut, ID(name)) - } else { - handleOut = append(handleOut, ID("res").Op(".").ID(util.ToUpperCamelCase(p.Name))) + switch { //nolint:staticcheck + case p.Type == "error": + handleOut = append(handleOut, ID("err")) + default: + if name, ok := outParamArgs[p.Name]; ok { + if link, ok := imports.Get(p.Pkg); ok { + imp.Set(p.Pkg, link) } + + handleOut = append(handleOut, ID(name)) + } else { + handleOut = append(handleOut, ID("res").Op(".").ID(util.ToUpperCamelCase(p.Name))) } } + } - // -------------------------------------- + // -------------------------------------- - handleSrc = append(handleSrc, - List(handleOut...).Op("=").ID("v").Op(".").ID("handle"+object.Name).Op(".").ID(method.Name).Call(handleIn...), - If().ID("err").Op("!=").Nil().Block( - //ID("err").Op("=").Pkg("fmt").ID("Errorf").Bracket( - // Text("encode request: %w"), - // ID("err"), - //), - Return().List(Nil(), ID("err")), - ), - Return().List(ID("res"), Nil()), - ) + handleSrc = append(handleSrc, + List(handleOut...).Op("=").ID("v").Op(".").ID("handle").Op(".").ID(method.Name).Call(handleIn...), + If().ID("err").Op("!=").Nil().Block( + Return().List(Nil(), ID("err")), + ), + Return().List(ID("res"), Nil()), + ) - models = append(models, handle.Block(handleSrc...)) - } + models = append(models, handle.Block(handleSrc...)) } return models diff --git a/apigen/module/mod-json-rpc/common.go b/apigen/module/mod-json-rpc/common.go index cea0d77..3815881 100644 --- a/apigen/module/mod-json-rpc/common.go +++ b/apigen/module/mod-json-rpc/common.go @@ -8,28 +8,10 @@ package mod_json_rpc import "strings" const ( - transportName = "JSONRPCHandler" + transportName = "JSONRPC%sTransport" modelNameReq = "jsonrpc%sModelRequest" modelNameRes = "jsonrpc%sModelResponse" - - modelBaseReq = "baseRequest" - modelBulkBaseReq = "bulkRequest" - modelBaseRes = "baseResponse" - modelBulkBaseRes = "bulkResponse" - modelBaseErr = "errResponse" - errInterface = "TError" -) - -const ( - fieldMethod = "method" - fieldParams = "params" - fieldID = "id" - fieldResult = "result" - fieldError = "error" - fieldMessage = "message" - fieldCode = "code" - fieldCtx = "ctx" ) const ( diff --git a/apigen/module/mod-json-rpc/module.go b/apigen/module/mod-json-rpc/module.go index 77ced70..e7600e2 100644 --- a/apigen/module/mod-json-rpc/module.go +++ b/apigen/module/mod-json-rpc/module.go @@ -21,9 +21,7 @@ func (Module) Name() string { func (v Module) Build(w at.Writer, m at.GlobalMeta, files []at.File) error { return errors.Queue( - func() error { return v.buildBaseRPCModel(w, m) }, func() error { return v.buildTransportModels(w, m, files) }, - func() error { return v.buildTransport(w, m, files) }, func() error { return v.buildTransportHandlers(w, m, files) }, ) } diff --git a/apigen/types/type.go b/apigen/types/type.go index a114e56..f3d5326 100644 --- a/apigen/types/type.go +++ b/apigen/types/type.go @@ -41,7 +41,6 @@ type ImportSetter interface { type GlobalMeta struct { PkgName string - Pool []string } type FaceMeta struct { diff --git a/cmd/goppy/main.go b/cmd/goppy/main.go index 79de584..194e3c4 100644 --- a/cmd/goppy/main.go +++ b/cmd/goppy/main.go @@ -26,7 +26,7 @@ func main() { commands.CmdSetupLib(), commands.CmdSetupApp(), commands.CmdGoSite(), - commands.CmdWSG(), + commands.CmdTB(), ormb.Command(), ) diff --git a/dic/broker/universal.go b/dic/broker/universal.go index fde2db5..d5d2f96 100644 --- a/dic/broker/universal.go +++ b/dic/broker/universal.go @@ -56,7 +56,7 @@ func (u *UniversalBroker[T]) Apply(arg any) { func (u *UniversalBroker[T]) OnStart(ctx xc.Context) error { logx.Info("Universal Broker", "do", "start", "type", getTypeName[T](), "count", len(u.objects)) - if len(u.objects) == 0 { + if u.onStartCallback == nil || len(u.objects) == 0 { return nil } @@ -73,7 +73,7 @@ func (u *UniversalBroker[T]) OnStart(ctx xc.Context) error { func (u *UniversalBroker[T]) OnStop() error { logx.Info("Universal Broker", "do", "stop", "type", getTypeName[T](), "count", len(u.objects)) - if len(u.objects) == 0 { + if u.onStopCallback == nil || len(u.objects) == 0 { return nil } diff --git a/internal/commands/wsg.go b/internal/commands/tb.go similarity index 84% rename from internal/commands/wsg.go rename to internal/commands/tb.go index d9fb7c8..f14ba76 100644 --- a/internal/commands/wsg.go +++ b/internal/commands/tb.go @@ -19,16 +19,15 @@ import ( "go.osspkg.com/goppy/v3/internal/global" ) -func CmdWSG() console.CommandGetter { +func CmdTB() console.CommandGetter { return console.NewCommand(func(setter console.CommandSetter) { - setter.Setup("wsg", "generate web server api") + setter.Setup("tb", "api transport builder") setter.Flag(func(flagsSetter console.FlagsSetter) { flagsSetter.String("out", "output file specified") flagsSetter.StringVar("iface", "", "interface names (optional)") flagsSetter.StringVar("mod", "json-rpc", "generation modules (optional)") - flagsSetter.StringVar("pool", "main", "web server pool list (optional)") }) - setter.ExecFunc(func(out, _iface, _mod, _pool string) { + setter.ExecFunc(func(out, _iface, _mod string) { console.ShowDebug(true) console.Infof("--- GENERATE ---") @@ -51,10 +50,6 @@ func CmdWSG() console.CommandGetter { return strings.ToLower(strings.TrimSpace(value)) }) - pool := do.Treat[string](strings.Split(_pool, ","), func(value string, index int) string { - return strings.ToLower(strings.TrimSpace(value)) - }) - face := do.Entries[string, string, struct{}]( do.Filter[string](strings.Split(_iface, ","), func(value string, index int) bool { @@ -70,7 +65,6 @@ func CmdWSG() console.CommandGetter { Out: out, IFace: face, Mods: mods, - Pool: pool, } for _, filePath := range files { @@ -80,7 +74,7 @@ func CmdWSG() console.CommandGetter { console.Debugf("> PARSE FILE: %s", filePath) - vv, e := parser.New("@wsg", filePath) + vv, e := parser.New("@tb", filePath) if e != nil { if errors.Is(e, parser.ErrIsGenerated) { continue diff --git a/web/jsonrpc/common.go b/web/jsonrpc/common.go new file mode 100644 index 0000000..37bcc6e --- /dev/null +++ b/web/jsonrpc/common.go @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +package jsonrpc + +import ( + "errors" + + "go.osspkg.com/ioutils/pool" + "go.osspkg.com/syncing" +) + +var ( + poolResponse = pool.New[*syncing.Slice[response]](func() *syncing.Slice[response] { + return syncing.NewSlice[response](uint(2)) + }) + + poolRequest = pool.New[*bulkRequest](func() *bulkRequest { + br := make(bulkRequest, 0, 2) + return &br + }) + + ErrUnsupportedMethod = errors.New("unsupported method") +) diff --git a/web/jsonrpc/interfaces.go b/web/jsonrpc/interfaces.go new file mode 100644 index 0000000..9fb5d08 --- /dev/null +++ b/web/jsonrpc/interfaces.go @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +package jsonrpc + +import ( + "context" + "encoding/json" + + "go.osspkg.com/goppy/v3/web" +) + +type TError interface { + GetCode() int64 + GetMessage() string + GetContext() map[string]string +} + +type THandleFunc func(ctx context.Context, wc web.Ctx, p json.RawMessage) (any, error) + +type TApi interface { + JSONRPCApiHandlers() map[string]THandleFunc + RouteTags() []string +} diff --git a/web/jsonrpc/jsonrpc.go b/web/jsonrpc/jsonrpc.go new file mode 100644 index 0000000..cbffbfd --- /dev/null +++ b/web/jsonrpc/jsonrpc.go @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +package jsonrpc + +import ( + "context" + "strings" + "time" + + "go.osspkg.com/logx" + "go.osspkg.com/syncing" + + "go.osspkg.com/goppy/v3/web" +) + +type Transport interface { + Inject(r TApi) +} + +type service struct { + opt *options + handlers map[string]*syncing.Map[string, THandleFunc] + routes web.ServerPool +} + +func newTransport(routes web.ServerPool, opts ...Option) Transport { + obj := &service{ + opt: &options{ + timeout: time.Second * 5, + path: "/jsonrpc", + errHandler: func(_ string, err error) error { + return err + }, + }, + handlers: make(map[string]*syncing.Map[string, THandleFunc], 2), + routes: routes, + } + + for _, o := range opts { + o(obj.opt) + } + + return obj +} + +func (v *service) Down() error { + for tag, r := range v.handlers { + r.Reset() + + logx.Info("JSON-RPC Transport", + "do", "stop", + "tag", tag) + } + return nil +} + +func (v *service) Up() error { + v.routes.All(func(tag string, r web.Router) { + resolve, ok := v.handlers[tag] + if !ok { + return + } + + r.Post(v.opt.path, v.Handle(resolve)) + + logx.Info("JSON-RPC Transport", + "do", "start", + "tag", tag) + }) + + return nil +} + +func (v *service) Inject(r TApi) { + for _, tag := range r.RouteTags() { + resolve, ok := v.handlers[tag] + if !ok { + resolve = syncing.NewMap[string, THandleFunc](10) + v.handlers[tag] = resolve + } + + for method, handler := range r.JSONRPCApiHandlers() { + resolve.Set(method, handler) + } + } +} + +func (v *service) Handle(resolve *syncing.Map[string, THandleFunc]) func(wc web.Ctx) { + return func(wc web.Ctx) { + req := poolRequest.Get() + defer poolRequest.Put(req) + + if err := wc.BindJSON(req); err != nil { + wc.String(400, v.opt.errHandler("", err).Error()) + return + } + + res := poolResponse.Get() + defer poolResponse.Put(res) + + ctx, cancel := context.WithTimeout(wc.Context(), v.opt.timeout) + defer cancel() + + wg := syncing.NewGroup(ctx) + wg.OnPanic(func(err error) { + logx.Error("json-rpc handle panic", "err", err) + }) + + for _, item := range *req { + item := item + + method := strings.ToLower(item.Method) + + wg.Background(method, func(ctx context.Context) { + out := response{ + Id: item.Id, + } + + if handler, ok := resolve.Get(method); ok { + + result, err := handler(ctx, wc, item.Params) + if err != nil { + out.Error = errorConvert(v.opt.errHandler(method, err)) + } else { + out.Result = result + } + + } else { + out.Error = errorConvert(ErrUnsupportedMethod) + } + + res.Append(out) + }) + + } + + wg.Wait() + wc.JSON(200, bulkResponse(res.Extract())) + } +} diff --git a/web/jsonrpc/option.go b/web/jsonrpc/option.go new file mode 100644 index 0000000..a8e6e8e --- /dev/null +++ b/web/jsonrpc/option.go @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +package jsonrpc + +import "time" + +type Option func(o *options) + +type options struct { + timeout time.Duration + path string + errHandler func(method string, err error) error +} + +func Timeout(arg time.Duration) Option { + return func(o *options) { + if arg <= time.Second { + arg = time.Second + } + o.timeout = arg + } +} + +func Path(arg string) Option { + return func(o *options) { + if len(arg) == 0 { + arg = "/" + } + o.path = arg + } +} + +func ErrHandler(arg func(method string, err error) error) Option { + return func(o *options) { + if arg == nil { + arg = func(_ string, err error) error { + return err + } + } + o.errHandler = arg + } +} diff --git a/web/jsonrpc/plugin.go b/web/jsonrpc/plugin.go new file mode 100644 index 0000000..af2032a --- /dev/null +++ b/web/jsonrpc/plugin.go @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2022-2026 Mikhail Knyazhev . All rights reserved. + * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. + */ + +package jsonrpc + +import ( + "go.osspkg.com/goppy/v3/plugins" + "go.osspkg.com/goppy/v3/web" +) + +func WithTransport(opts ...Option) plugins.Kind { + return plugins.Kind{ + Inject: func(r web.ServerPool) Transport { + return newTransport(r, opts...) + }, + } +} diff --git a/_example/web-server-gen/transport/jsonrpc_model_base.go b/web/jsonrpc/types.go similarity index 63% rename from _example/web-server-gen/transport/jsonrpc_model_base.go rename to web/jsonrpc/types.go index 528d8fa..2764eb5 100644 --- a/_example/web-server-gen/transport/jsonrpc_model_base.go +++ b/web/jsonrpc/types.go @@ -3,31 +3,36 @@ * Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file. */ -// Code generated by goppy-cli wsg. DO NOT EDIT. -package transport +package jsonrpc //go:generate easyjson -import stdjson "encoding/json" +import ( + "encoding/json" +) //easyjson:json -type baseRequest struct { - Id string `json:"id"` - Method string `json:"method"` - Params stdjson.RawMessage `json:"params"` +type request struct { + Id string `json:"id"` + Method string `json:"method"` + Params json.RawMessage `json:"params"` } //easyjson:json -type bulkRequest []baseRequest +type bulkRequest []request + +func (br *bulkRequest) Reset() { + *br = (*br)[:0] +} //easyjson:json -type baseResponse struct { +type response struct { Id string `json:"id"` Result any `json:"result,omitempty"` Error *errResponse `json:"error,omitempty"` } //easyjson:json -type bulkResponse []baseResponse +type bulkResponse []response //easyjson:json type errResponse struct { @@ -35,13 +40,8 @@ type errResponse struct { Code int64 `json:"code"` Ctx map[string]string `json:"ctx,omitempty"` } -type TError interface { - GetCode() int64 - GetMessage() string - GetContext() map[string]string -} -func toJSONRPCError(e error) *errResponse { +func errorConvert(e error) *errResponse { if e == nil { return nil } diff --git a/_example/web-server-gen/transport/jsonrpc_model_base_easyjson.go b/web/jsonrpc/types_easyjson.go similarity index 72% rename from _example/web-server-gen/transport/jsonrpc_model_base_easyjson.go rename to web/jsonrpc/types_easyjson.go index 7e1804e..d8f95b1 100644 --- a/_example/web-server-gen/transport/jsonrpc_model_base_easyjson.go +++ b/web/jsonrpc/types_easyjson.go @@ -1,6 +1,6 @@ // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. -package transport +package jsonrpc import ( json "encoding/json" @@ -18,7 +18,199 @@ var ( _ easyjson.Marshaler ) -func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport(in *jlexer.Lexer, out *errResponse) { +func easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc(in *jlexer.Lexer, out *response) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + switch key { + case "id": + if in.IsNull() { + in.Skip() + } else { + out.Id = string(in.String()) + } + case "result": + if m, ok := out.Result.(easyjson.Unmarshaler); ok { + m.UnmarshalEasyJSON(in) + } else if m, ok := out.Result.(json.Unmarshaler); ok { + _ = m.UnmarshalJSON(in.Raw()) + } else { + out.Result = in.Interface() + } + case "error": + if in.IsNull() { + in.Skip() + out.Error = nil + } else { + if out.Error == nil { + out.Error = new(errResponse) + } + if in.IsNull() { + in.Skip() + } else { + (*out.Error).UnmarshalEasyJSON(in) + } + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc(out *jwriter.Writer, in response) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"id\":" + out.RawString(prefix[1:]) + out.String(string(in.Id)) + } + if in.Result != nil { + const prefix string = ",\"result\":" + out.RawString(prefix) + if m, ok := in.Result.(easyjson.Marshaler); ok { + m.MarshalEasyJSON(out) + } else if m, ok := in.Result.(json.Marshaler); ok { + out.Raw(m.MarshalJSON()) + } else { + out.Raw(json.Marshal(in.Result)) + } + } + if in.Error != nil { + const prefix string = ",\"error\":" + out.RawString(prefix) + (*in.Error).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v response) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v response) MarshalEasyJSON(w *jwriter.Writer) { + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *response) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *response) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc(l, v) +} +func easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc1(in *jlexer.Lexer, out *request) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + switch key { + case "id": + if in.IsNull() { + in.Skip() + } else { + out.Id = string(in.String()) + } + case "method": + if in.IsNull() { + in.Skip() + } else { + out.Method = string(in.String()) + } + case "params": + if in.IsNull() { + in.Skip() + } else { + if data := in.Raw(); in.Ok() { + in.AddError((out.Params).UnmarshalJSON(data)) + } + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc1(out *jwriter.Writer, in request) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"id\":" + out.RawString(prefix[1:]) + out.String(string(in.Id)) + } + { + const prefix string = ",\"method\":" + out.RawString(prefix) + out.String(string(in.Method)) + } + { + const prefix string = ",\"params\":" + out.RawString(prefix) + out.Raw((in.Params).MarshalJSON()) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v request) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc1(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v request) MarshalEasyJSON(w *jwriter.Writer) { + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc1(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *request) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc1(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *request) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc1(l, v) +} +func easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc2(in *jlexer.Lexer, out *errResponse) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -78,7 +270,7 @@ func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport(in *jl in.Consumed() } } -func easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport(out *jwriter.Writer, in errResponse) { +func easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc2(out *jwriter.Writer, in errResponse) { out.RawByte('{') first := true _ = first @@ -117,27 +309,27 @@ func easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport(out *j // MarshalJSON supports json.Marshaler interface func (v errResponse) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport(&w, v) + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc2(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v errResponse) MarshalEasyJSON(w *jwriter.Writer) { - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport(w, v) + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc2(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *errResponse) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport(&r, v) + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc2(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *errResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport(l, v) + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc2(l, v) } -func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport1(in *jlexer.Lexer, out *bulkResponse) { +func easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc3(in *jlexer.Lexer, out *bulkResponse) { isTopLevel := in.IsStart() if in.IsNull() { in.Skip() @@ -154,7 +346,7 @@ func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport1(in *j *out = (*out)[:0] } for !in.IsDelim(']') { - var v3 baseResponse + var v3 response if in.IsNull() { in.Skip() } else { @@ -169,7 +361,7 @@ func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport1(in *j in.Consumed() } } -func easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport1(out *jwriter.Writer, in bulkResponse) { +func easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc3(out *jwriter.Writer, in bulkResponse) { if in == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { out.RawString("null") } else { @@ -187,27 +379,27 @@ func easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport1(out * // MarshalJSON supports json.Marshaler interface func (v bulkResponse) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport1(&w, v) + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc3(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v bulkResponse) MarshalEasyJSON(w *jwriter.Writer) { - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport1(w, v) + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc3(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *bulkResponse) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport1(&r, v) + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc3(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *bulkResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport1(l, v) + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc3(l, v) } -func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport2(in *jlexer.Lexer, out *bulkRequest) { +func easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc4(in *jlexer.Lexer, out *bulkRequest) { isTopLevel := in.IsStart() if in.IsNull() { in.Skip() @@ -224,7 +416,7 @@ func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport2(in *j *out = (*out)[:0] } for !in.IsDelim(']') { - var v6 baseRequest + var v6 request if in.IsNull() { in.Skip() } else { @@ -239,7 +431,7 @@ func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport2(in *j in.Consumed() } } -func easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport2(out *jwriter.Writer, in bulkRequest) { +func easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc4(out *jwriter.Writer, in bulkRequest) { if in == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { out.RawString("null") } else { @@ -257,215 +449,23 @@ func easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport2(out * // MarshalJSON supports json.Marshaler interface func (v bulkRequest) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport2(&w, v) + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc4(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v bulkRequest) MarshalEasyJSON(w *jwriter.Writer) { - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport2(w, v) + easyjson6601e8cdEncodeGoOsspkgComGoppyV3WebJsonrpc4(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *bulkRequest) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport2(&r, v) + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc4(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *bulkRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport2(l, v) -} -func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport3(in *jlexer.Lexer, out *baseResponse) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - switch key { - case "id": - if in.IsNull() { - in.Skip() - } else { - out.Id = string(in.String()) - } - case "result": - if m, ok := out.Result.(easyjson.Unmarshaler); ok { - m.UnmarshalEasyJSON(in) - } else if m, ok := out.Result.(json.Unmarshaler); ok { - _ = m.UnmarshalJSON(in.Raw()) - } else { - out.Result = in.Interface() - } - case "error": - if in.IsNull() { - in.Skip() - out.Error = nil - } else { - if out.Error == nil { - out.Error = new(errResponse) - } - if in.IsNull() { - in.Skip() - } else { - (*out.Error).UnmarshalEasyJSON(in) - } - } - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport3(out *jwriter.Writer, in baseResponse) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"id\":" - out.RawString(prefix[1:]) - out.String(string(in.Id)) - } - if in.Result != nil { - const prefix string = ",\"result\":" - out.RawString(prefix) - if m, ok := in.Result.(easyjson.Marshaler); ok { - m.MarshalEasyJSON(out) - } else if m, ok := in.Result.(json.Marshaler); ok { - out.Raw(m.MarshalJSON()) - } else { - out.Raw(json.Marshal(in.Result)) - } - } - if in.Error != nil { - const prefix string = ",\"error\":" - out.RawString(prefix) - (*in.Error).MarshalEasyJSON(out) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v baseResponse) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport3(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v baseResponse) MarshalEasyJSON(w *jwriter.Writer) { - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport3(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *baseResponse) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport3(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *baseResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport3(l, v) -} -func easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport4(in *jlexer.Lexer, out *baseRequest) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - switch key { - case "id": - if in.IsNull() { - in.Skip() - } else { - out.Id = string(in.String()) - } - case "method": - if in.IsNull() { - in.Skip() - } else { - out.Method = string(in.String()) - } - case "params": - if in.IsNull() { - in.Skip() - } else { - if data := in.Raw(); in.Ok() { - in.AddError((out.Params).UnmarshalJSON(data)) - } - } - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport4(out *jwriter.Writer, in baseRequest) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"id\":" - out.RawString(prefix[1:]) - out.String(string(in.Id)) - } - { - const prefix string = ",\"method\":" - out.RawString(prefix) - out.String(string(in.Method)) - } - { - const prefix string = ",\"params\":" - out.RawString(prefix) - out.Raw((in.Params).MarshalJSON()) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v baseRequest) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport4(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v baseRequest) MarshalEasyJSON(w *jwriter.Writer) { - easyjson6850ac6fEncodeGoOsspkgComGoppyV3ExampleWebServerGenTransport4(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *baseRequest) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport4(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *baseRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson6850ac6fDecodeGoOsspkgComGoppyV3ExampleWebServerGenTransport4(l, v) + easyjson6601e8cdDecodeGoOsspkgComGoppyV3WebJsonrpc4(l, v) }