{"id":19816101,"url":"https://github.com/nofeaturesonlybugs/call","last_synced_at":"2026-06-15T07:33:09.584Z","repository":{"id":57639670,"uuid":"432606595","full_name":"nofeaturesonlybugs/call","owner":"nofeaturesonlybugs","description":"Package call is a small wrapper around the official reflect package that eases dynamic method calling on Go types.","archived":false,"fork":false,"pushed_at":"2022-06-12T16:26:36.000Z","size":70,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-28T19:39:29.483Z","etag":null,"topics":["go","golang","method-call","methods","reflect","reflection"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nofeaturesonlybugs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-11-28T03:09:04.000Z","updated_at":"2021-12-12T19:41:12.000Z","dependencies_parsed_at":"2022-08-27T20:02:21.740Z","dependency_job_id":null,"html_url":"https://github.com/nofeaturesonlybugs/call","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/nofeaturesonlybugs/call","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nofeaturesonlybugs%2Fcall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nofeaturesonlybugs%2Fcall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nofeaturesonlybugs%2Fcall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nofeaturesonlybugs%2Fcall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nofeaturesonlybugs","download_url":"https://codeload.github.com/nofeaturesonlybugs/call/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nofeaturesonlybugs%2Fcall/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34353193,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","method-call","methods","reflect","reflection"],"created_at":"2024-11-12T10:08:19.776Z","updated_at":"2026-06-15T07:33:09.557Z","avatar_url":"https://github.com/nofeaturesonlybugs.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Go Reference](https://pkg.go.dev/badge/github.com/nofeaturesonlybugs/call.svg)](https://pkg.go.dev/github.com/nofeaturesonlybugs/call)\n[![Go Report Card](https://goreportcard.com/badge/github.com/nofeaturesonlybugs/call)](https://goreportcard.com/report/github.com/nofeaturesonlybugs/call)\n[![Build Status](https://app.travis-ci.com/nofeaturesonlybugs/call.svg?branch=master)](https://app.travis-ci.com/nofeaturesonlybugs/call)\n[![codecov](https://codecov.io/gh/nofeaturesonlybugs/call/branch/master/graph/badge.svg)](https://codecov.io/gh/nofeaturesonlybugs/call)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nPackage `call` is a small wrapper around the official reflect package that eases dynamic function or method calls.\n\n`call` can be useful for rigging routes to handlers on Go types in a dynamic fashion. An example of that will follow but let's first see some examples of `call` in action.\n\n## A Useless Case\n\n```go\nfn := func(str string, num int) {\n    fmt.Printf(\"str=%v num=%v\\n\", str, num)\n}\n\nf := call.StatFunc(fn)\nf.Call(f.Args())\n\n// prints:\n// str= num=0\n```\n\nThe call to `StatFunc` returns a `Func` type that can be used to create function arguments and then invoke the function as seen by `f.Call(f.Args())`.\n\nWhen `Args()` creates arguments it creates zero values for the argument types.\n\n## Setting Argument Values\n\nThe previous example is somewhat useless because the function is called with a zero-value for each argument. Now we'll set the argument values using the `Pointers` field of `Args`:\n\n```go\n// Same function as before.\nfn := func(str string, num int) {\n    fmt.Printf(\"str=%v num=%v\\n\", str, num)\n}\n\nf := call.StatFunc(fn)\n// Args contains two slices that give us access to the created arguments.\nargs := f.Args()\nfor k := range args.Values {\n    // args.Pointers are pointers to the arguments.\n    pointer := args.Pointers[k]\n    switch p := pointer.(type) {\n    case *string:\n        *p = \"Hi!\"\n    case *int:\n        *p = 42\n    }\n    // args.Values are reflect.Value of the argument.\n    value := args.Values[k].Interface()\n    fmt.Printf(\"%T %v %T\\n\", value, value, pointer)\n}\nf.Call(args)\n\n// prints:\n// string Hi! *string\n// int 42 *int\n// str=Hi! num=42\n```\n\n## Struct Arguments\n\nThe `Pointers` field is also useful for unmarshaling data into function arguments:\n\n```go\ntype Request struct {\n    Str string `json:\"str\"`\n    Num int    `json:\"num\"`\n}\nfn := func(req Request) {\n    fmt.Printf(\"str=%v num=%v\\n\", req.Str, req.Num)\n}\n\ndata := []byte(`{\"str\" : \"Hi!\", \"num\" : 42}`)\nf := call.StatFunc(fn)\nargs := f.Args()\n// For brevity we unmarshal straight into args.Pointers[0]\nif err := json.Unmarshal(data, args.Pointers[0]); err != nil {\n    fmt.Println(err)\n    return\n}\nf.Call(args)\n\n// prints:\n// str=Hi! num=42\n```\n\n## Interface Arguments\n\nWhen an argument is an interface I its value is `I(nil)` and its pointer is also `nil`:\n\n```go\n// Interfaces are always passed as nil.\nfn := func(w http.ResponseWriter) {\n    fmt.Println(w)\n}\n\nf := call.StatFunc(fn)\nargs := f.Args()\n// When an argument represents an interface I its Values is an I(nil)\n// and its Pointers is nil.\nfmt.Println(args.Values[0].Interface(), args.Pointers[0])\nf.Call(args)\n\n// prints:\n// \u003cnil\u003e \u003cnil\u003e\n// \u003cnil\u003e\n```\n\n## Interface Arguments \u0026 Pruning \u003csup\u003eThe Beginnings of an http.Handler\u003c/sup\u003e\n\nSince interface types are provided as nil values by `Args()` you may wish to configure the `*Func` to stop managing such types. You do this by calling `PruneIn()`, which accepts a variadic list of `reflect.Type`:\n\n```go\n// In order to prune a type we need its reflect.Type.  Let's pretend we're writing\n// a more general purpose http.Handler and want to prune http.ResponseWriter\n// and *http.Request from types created via `Args()`:\nTypeRequest := reflect.TypeOf((*http.Request)(nil))\n// This is the idomatic way to get the type of a nil interface.\nTypeResponseWriter := reflect.TypeOf((*http.ResponseWriter)(nil)).Elem()\n\nfn := func( w http.ResponseWriter, req *http.Request, req SomeStructType ) {\n}\n\n// We're going to take the standard http.Handler signature and dynamically invoke fn\nhandler := func( w http.ResponseWriter, req *http.Request ) {\n    f := call.StatFunc(fn)\n    pruned := f.PruneIn(TypeRequest, TypeResponseWriter)\n    args := f.Args()\n    //\n    // Before invoking f we should see if we can provide any pruned arguments:\n    for _, arg := range pruned {\n        switch arg.T {\n            case TypeRequest:\n                args.Values[arg.N] = reflect.ValueOf(req)\n            case TypeResponseWriter:\n                args.Values[arg.N] = reflect.ValueOf(w)\n        }\n    }\n    //\n    // NB   A more useful handler would potentially unmarshal req.Body\n    //      into args.Pointers that could accept it.\n    //\n    f.Call(args)\n}\n```\n\n## A Better http.Handler\n\nLet's take some ideas from the previous snippet and create a `http.Handler` factory:\n\n```go\nTypeRequest := reflect.TypeOf((*http.Request)(nil))\nTypeResponseWriter := reflect.TypeOf((*http.ResponseWriter)(nil)).Elem()\n\n// Factory accepts a function and turns it into an http.Handler.\nFactory := func(opaque interface{}) http.Handler {\n\tf := call.StatFunc(opaque)\n\tpruned := f.PruneIn(TypeRequest, TypeResponseWriter)\n\t//\n\t// The created handler does not represent a \"production-ready\" http.Handler but it does\n\t// demonstrate how \"package call\" can be used to:\n\t//\t+ invoke end-of-chain handlers with adhoc or variable signatures\n\t//\t+ how to unmarshal and provide data to the handler arguments\n\t//\t\t(by unmarshaling application/json requests, for example)\n\tfn := func(w http.ResponseWriter, req *http.Request) {\n\t\targs := f.Args()\n\t\t// Before invoking f we should see if we can provide any pruned arguments:\n\t\tfor _, arg := range pruned {\n\t\t\tswitch arg.T {\n\t\t\tcase TypeRequest:\n\t\t\t\targs.Values[arg.N] = reflect.ValueOf(req)\n\t\t\tcase TypeResponseWriter:\n\t\t\t\targs.Values[arg.N] = reflect.ValueOf(w)\n\t\t\t}\n\t\t}\n\t\t//\n\t\t// If the request is application/json we will unmarshal into any arguments\n\t\t// that are struct.\n\t\t// NB:  An intelligent handler factory would have examined f.InCreate and possibly set a\n\t\t//\t\thasJSON=true|false flag and could theoretically skip this logic block if the\n\t\t//\t\tend-of-chain handler doesn't have targets for JSON data.\n\t\tif req.Header.Get(\"Content-Type\") == \"application/json\" {\n\t\t\tbody, err := io.ReadAll(req.Body)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor _, arg := range f.InCreate {\n\t\t\t\tif arg.T.Kind() != reflect.Struct {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif err = json.Unmarshal(body, args.Pointers[arg.N]); err != nil {\n\t\t\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//\n\t\t// NB:  This handler doesn't do anything with any return values.  A better handler\n\t\t// \t\tfactory would probably make use of any error returned or possibly accept\n\t\t//\t\tsome type of result and then write to the response appropriately.\n\t\tf.Call(args)\n\t}\n\treturn http.HandlerFunc(fn)\n}\n\ntype LoginRequest struct {\n\tUsername string `json:\"username\"`\n\tPassword string `json:\"password\"`\n}\nLogin := func(w http.ResponseWriter, post LoginRequest) {\n\tfmt.Fprintf(w, \"%v\", post)\n}\nLogout := func(w http.ResponseWriter) {\n\tfmt.Fprint(w, \"Logged out!\")\n}\n\nmux := http.NewServeMux()\nmux.Handle(\"/login\", Factory(Login))\nmux.Handle(\"/logout\", Factory(Logout))\n\n// /login\nw := httptest.NewRecorder()\nw.Body = \u0026bytes.Buffer{}\nreq := httptest.NewRequest(http.MethodPost, \"/login\", bytes.NewBufferString(`{\"username\":\"test\",\"password\":\"s3cr3t\"}`))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nmux.ServeHTTP(w, req)\nfmt.Println(w.Body.String())\n\n// /logout\nw = httptest.NewRecorder()\nw.Body = \u0026bytes.Buffer{}\nreq = httptest.NewRequest(http.MethodPost, \"/logout\", nil)\nmux.ServeHTTP(w, req)\nfmt.Println(w.Body.String())\n\n// prints:\n// {test s3cr3t}\n// Logged out!\n```\n\n## API Consistency and Breaking Changes\n\nI am making a very concerted effort to break the API as little as possible while adding features or fixing bugs. However this software is currently in a pre-1.0.0 version and breaking changes _are_ allowed under standard semver. As the API approaches a stable 1.0.0 release I will list any such breaking changes here and they will always be signaled by a bump in _minor_ version.\n\n-   0.1.x ⭢ 0.2.0\n    -   Several types have been renamed to be more ergonomic:\n        -   `Methods` renamed to `Instance`\n        -   `MethodInfo` renamed to `Method`\n        -   `MethodResult` renamed to `Result`\n        -   Fields `InCacheArgs` and `InCreateArgs` have had the `Args` suffix dropped and are now simply `InCache` and `InCreate`.\n        -   `Receiver` type dropped entirely; the `Rebind()` function now exists on `Instance`.\n    -   `call` now supports invoking methods on types or regular functions. To support this a new type `Func` has been introduced. `Func` was created by pulling several fields out of `Method` (previously `MethodInfo`). `Method` retains access to this extracted information by embedding `*Func`; in other words `Func` is for calling regular functions, `Method` is for calling functions that have receivers, and `Method` is a superset of `Func`.\n    -   Added a new `type Methods []Method` which has a helper function for finding a method by name; note that this `Methods` type is not the same nor is it compatible with `Methods` type in the previous release.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnofeaturesonlybugs%2Fcall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnofeaturesonlybugs%2Fcall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnofeaturesonlybugs%2Fcall/lists"}