{"id":14970254,"url":"https://github.com/hashicorp/go-argmapper","last_synced_at":"2025-05-16T04:05:26.235Z","repository":{"id":41976947,"uuid":"264544063","full_name":"hashicorp/go-argmapper","owner":"hashicorp","description":"A runtime dependency-injection library for Go that supports automatically chaining conversion functions to reach desired input and output types.","archived":false,"fork":false,"pushed_at":"2025-04-25T09:15:21.000Z","size":306,"stargazers_count":122,"open_issues_count":7,"forks_count":7,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-04-25T10:26:42.715Z","etag":null,"topics":["dependency-injection","go","golang","reflection"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hashicorp.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":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-05-16T23:05:52.000Z","updated_at":"2025-04-25T09:15:25.000Z","dependencies_parsed_at":"2024-06-18T15:16:58.235Z","dependency_job_id":"943dee0c-b7b4-40e1-906c-c2e362c8f373","html_url":"https://github.com/hashicorp/go-argmapper","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashicorp%2Fgo-argmapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashicorp%2Fgo-argmapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashicorp%2Fgo-argmapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashicorp%2Fgo-argmapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hashicorp","download_url":"https://codeload.github.com/hashicorp/go-argmapper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254464895,"owners_count":22075570,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["dependency-injection","go","golang","reflection"],"created_at":"2024-09-24T13:43:21.294Z","updated_at":"2025-05-16T04:05:21.221Z","avatar_url":"https://github.com/hashicorp.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-argmapper [![Godoc](https://godoc.org/github.com/hashicorp/go-argmapper?status.svg)](https://godoc.org/github.com/hashicorp/go-argmapper)\n\ngo-argmapper is a dependency-injection library for Go that supports\nautomatically chaining conversion functions to reach desired results.\ngo-argmapper is designed for runtime, reflection-based dependency injection.\n\n**API Status: Mostly Stable.** We have released HashiCorp products using\nthis library successfully, so we don't think the API will change significantly.\nFor the time being, we're retaining the 0.x version numbers to note that we\nmay still change the API and to recognize that the library has only been\nused in the real world for a short period of time.\n\n## Features\n\n**Named parameter matching.** go-argmapper can match on named arguments,\nso you can say that `from int` is different from `to int` when calling\nthe same function.\n\n**Typed parameter matching.** go-argmapper can match on types, including\ninterfaces and interface implementations. This enables the common\ndependency-injection pattern of fulfilling an interface.\n\n**\"Subtype\" labels for overloaded types.** Values can be labeled with a \"subtype\" key (a string)\nfor more fine-grained matching. A real-world use case of this is\n[protobuf Any values](https://developers.google.com/protocol-buffers/docs/proto3#any).\nThe subtype of these values can be the protobuf message name. This enables\nseparating name, type, and subtype for more fine-grained matching.\n\n**Automatic conversion function chaining.** You can configure multiple\n\"conversion functions\" that can take some set of values and return another\nset of values and go-argmapper will automatically call them in the correct\norder if necessary to reach your desired function parameter types.\n\n**Function redefinition in terms of certain types.** Functions can be\n\"redefined\" to take as input and/or output values that match user-provided\nfilters. go-argmapper will automatically call proper conversion functions\nto reach the target function.\n\n**Type conversion API.** In addition to function calling, you can use the\nautomatic conversion function chaining to convert some input values to\nany target value. go-argmapper will tell you (via an error) if this is not\npossible.\n\n## Examples\n\n### Basic Dependency Injection\n\nThe example below shows common, basic dependency injection.\n\n```go\n// This is our target function. It wants some Writer implementation.\ntarget, err := argmapper.NewFunc(func(w io.Writer) {\n\t// ... use the writer ...\n})\n\n// This is a provider that provides our io.Writer. You can imagine that\n// this may differ between test/prod, configs, etc.\nprovider := func() io.Writer { return bytes.NewBuffer(nil) }\n\n// Call our function. This will call our provider to create an io.Writer\n// and then call our target function.\nresult := target.Call(argmapper.Converter(provider))\nif result.Err() != nil {\n\tpanic(result.Err())\n}\n```\n\nThe key thing happening here is that we're registering the `provider`\nfunction as a \"converter.\" argmapper will automatically find some converter\nto provide any values we're looking for.\n\n### Named and Typed Values\n\nThe example below shows both named and typed parameters in use.\n\n```go\ntarget, err := argmapper.NewFunc(func(input struct {\n\t// This tells argmapper to fill the values in this struct rather\n\t// than provide a value for the entire struct.\n\targmapper.Struct\n\n\tA int\n\tB int\n\tPrefix string\n}) string {\n\treturn fmt.Sprintf(\"%s: %d\", in.Prefix, in.A*in.B)\n})\n\nresult := target.Call(\n\targmapper.Named(\"a\", 21),\n\targmapper.Named(\"b\", 2),\n\targmapper.Typed(\"our value is\"),\n)\nif result.Err() != nil {\n\tpanic(result.Err())\n}\n\n// This prints: \"our value is: 42\"\nprintln(result.Out(0).(string))\n```\n\nBoth `A` and `B` are of the same type, but are matched on their names.\nThis lets us get the desired value of 42, rather than `21*21`, `2*2`, etc.\n\nNote that `Prefix` is a named parameter, but we don't provide any\ninputs matching that name. In this case, argmapper by default falls back\nto treating it as a typed parameter, allowing our typed string input to\nmatch.\n\n### Explicitly Typed Values\n\nThe previous example showed `Prefix` implicitly using a typed-only\nmatch since there was no input named \"Prefix\". You can also explictly\nnote that the name doesn't matter in two ways.\n\nFirst, you can use struct tags:\n\n```go\ntarget, err := argmapper.NewFunc(func(input struct {\n\t// This tells argmapper to fill the values in this struct rather\n\t// than provide a value for the entire struct.\n\targmapper.Struct\n\n\tA int\n\tB int\n\tPrefix string `argmapper:\",typeOnly\"`\n}) string {\n\treturn fmt.Sprintf(\"%s: %d\", in.Prefix, in.A*in.B)\n})\n```\n\nYou can also use a non-struct input. Go reflection doesn't reveal\nfunction parameter names so all function parameters are by definition\ntype only:\n\n```go\ntarget, err := argmapper.NewFunc(func(string) {})\n```\n\nYou can mix and match named and typed parameters.\n\n### Conversion Function Chaining\n\nThe example below shows how conversion functions are automatically\nchained as necessary to reach your desired function.\n\n```go\n// Trivial function that takes a string and just returns it.\ntarget, err := argmapper.NewFunc(func(v string) string { return v })\n\nresult := target.Call(\n\t// \"false\" value\n\targmapper.Typed(false),\n\n\t// bool to int\n\targmapper.Converter(func(v bool) int {\n\t\tif v {\n\t\t\treturn 1\n\t\t}\n\n\t\treturn 0\n\t}),\n\n\t// int to string\n\targmapper.Converter(func(v int) string {\n\t\treturn strconv.Itoa(v)\n\t}),\n)\nif result.Err() != nil {\n\t// If we didn't have converters necessary to get us from bool =\u003e int =\u003e string\n\t// then this would fail.\n\tpanic(result.Err())\n}\n\n// Prints \"0\"\nprintln(result.Out(0).(string))\n```\n\nTyped converters preserve the name of their arguments. If the above input\nwas `Named(\"foo\", false)` rather than typed, then the name \"foo\" would\nbe attached both the string and int values generated in case any target\nfunctions requested a named parameter. In the case of this example, the name\nis carried through but carries no consequence since the final target\nfunction is just a typed parameter.\n\n### Conversion Function Cycles\n\nCycles in conversion functions are completely allowed. The example\nbelow behaves as you would expect. This is a simple direct cycle, more complex\ncycles from chaining multiple converters will also behave correctly. This\nlets you register complex sets of bidirectional conversion functions with ease.\n\n```go\n// Trivial function that takes a string and just returns it.\ntarget, err := argmapper.NewFunc(func(v string) string { return v })\n\nresult := target.Call(\n\targmapper.Typed(12),\n\targmapper.Converter(func(v int) string { return strconv.Itoa(v) }),\n\targmapper.Converter(func(v string) (int, error) { return strconv.Atoi(v) }),\n)\nif result.Err() != nil {\n\t// If we didn't have converters necessary to get us from bool =\u003e int =\u003e string\n\t// then this would fail.\n\tpanic(result.Err())\n}\n\n// Prints \"12\"\nprintln(result.Out(0).(string))\n```\n\n### Conversion Errors\n\nThe example above has a converter that returns `(int, error)`. If the final\nreturn type of a converter is `error`, go-argmapper treats that as a special\nvalue signaling if the conversion succeeded or failed.\n\nIf conversion fails, the target function call fails and the error is returned\nto the user.\n\nIn the future, we plan on retrying via other possible conversion paths\nif they are available.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhashicorp%2Fgo-argmapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhashicorp%2Fgo-argmapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhashicorp%2Fgo-argmapper/lists"}