Skip to content

Instantly share code, notes, and snippets.

@msoap
Created July 22, 2021 14:59
Show Gist options
  • Select an option

  • Save msoap/4b25842e078658018bb565224fec0ef1 to your computer and use it in GitHub Desktop.

Select an option

Save msoap/4b25842e078658018bb565224fec0ef1 to your computer and use it in GitHub Desktop.

Revisions

  1. msoap created this gist Jul 22, 2021.
    108 changes: 108 additions & 0 deletions generics_handlers.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,108 @@
    package main

    import (
    "encoding/json"
    "fmt"
    )

    func main() {
    app := NewApp()
    server := NewServer()

    server.addHandler("method01", convertHandler(app.Handler01))
    server.addHandler("method02", convertHandler(app.Handler02))

    out, err := server.run("method01", `{"id": 1}`)
    fmt.Printf("Handler01: got resp: %v, err: %v\n", out, err)

    out, err = server.run("method01", `{"id": 11}`)
    fmt.Printf("Handler01: got resp: %v, err: %v\n", out, err)

    out, err = server.run("method02", `{"name": "AAA"}`)
    fmt.Printf("Handler02: got resp: %v, err: %v\n", out, err)

    out, err = server.run("method02", `{"name": 123}`)
    fmt.Printf("Handler02: got resp: %v, err: %v\n", out, err)
    }

    // app handlers -----------------------
    type App struct {
    }

    func NewApp() *App {
    return &App{}
    }

    type Request01 struct {
    ID int `json:"id"`
    }

    type Response01 struct {
    Error string `json:"error"`
    }

    func (app *App) Handler01(req Request01) (*Response01, error) {
    fmt.Printf("Handler01: got req: %v\n", req)
    if req.ID > 10 {
    return nil, fmt.Errorf("too big ID: %d", req.ID)
    }

    return &Response01{}, nil
    }

    type Request02 struct {
    Name string `json:"name"`
    }

    type Response02 struct {
    Status int `json:"status"`
    }

    func (app *App) Handler02(req Request02) (*Response02, error) {
    fmt.Printf("Handler02: got req: %+v\n", req)

    return &Response02{Status: len(req.Name)}, nil
    }

    // convert handler to generic -----
    func convertHandler[TIN any, TOUT any](fn func(req TIN) (*TOUT, error)) func(payload []byte) ([]byte, error) {
    return func(payload []byte) ([]byte, error) {
    var req TIN
    err := json.Unmarshal(payload, &req)
    if err != nil {
    return nil, err
    }

    resp, err := fn(req)
    if err != nil {
    return nil, err
    }

    return json.Marshal(*resp)
    }
    }

    // server -------------------------
    type Server struct {
    handlers map[string]func([]byte) ([]byte, error)
    }

    func NewServer() *Server {
    return &Server{handlers: make(map[string]func([]byte) ([]byte, error))}
    }

    func (s *Server) addHandler(name string, fn func(payload []byte) ([]byte, error)) {
    s.handlers[name] = fn
    }

    func (s *Server) run(method string, payload string) (string, error) {
    if handler, ok := s.handlers[method]; ok {
    out, err := handler([]byte(payload))
    if err != nil {
    return "", err
    }
    return string(out), nil
    }

    return "", fmt.Errorf("method %s not found", method)
    }