{"id":13412955,"url":"https://github.com/reedom/convergen","last_synced_at":"2026-01-15T00:36:01.729Z","repository":{"id":61624683,"uuid":"534930112","full_name":"reedom/convergen","owner":"reedom","description":"A type-to-type copy function code generator.","archived":false,"fork":false,"pushed_at":"2025-09-20T02:23:48.000Z","size":288,"stargazers_count":48,"open_issues_count":2,"forks_count":8,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-20T02:24:49.098Z","etag":null,"topics":["code-generator","ddd-patterns","golang"],"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/reedom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"reedom"}},"created_at":"2022-09-10T07:51:46.000Z","updated_at":"2025-09-20T00:00:04.000Z","dependencies_parsed_at":"2023-11-18T03:21:01.128Z","dependency_job_id":"4dfa39b1-f9d0-4499-9581-d1114bdd1e10","html_url":"https://github.com/reedom/convergen","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/reedom/convergen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reedom%2Fconvergen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reedom%2Fconvergen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reedom%2Fconvergen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reedom%2Fconvergen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reedom","download_url":"https://codeload.github.com/reedom/convergen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reedom%2Fconvergen/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28439800,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T00:34:46.850Z","status":"ssl_error","status_checked_at":"2026-01-15T00:34:46.551Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["code-generator","ddd-patterns","golang"],"created_at":"2024-07-30T20:01:31.564Z","updated_at":"2026-01-15T00:36:01.075Z","avatar_url":"https://github.com/reedom.png","language":"Go","readme":"Convergen\n=========\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/reedom/convergen.svg)](https://pkg.go.dev/github.com/reedom/convergen) \n[![Go Report Card](https://goreportcard.com/badge/github.com/reedom/convergen)](https://goreportcard.com/report/github.com/reedom/convergen) \n![Coverage](https://img.shields.io/badge/Coverage-67.3%25-yellow)\n\nConvergen is a code generator that creates functions for type-to-type copy.\nIt generates functions that copy field to field between two types.\n\nNotation Table\n--------------\n\n| notation                                  | location           | summary                                                                               |\n|-------------------------------------------|--------------------|---------------------------------------------------------------------------------------|\n| :match \u0026lt;`name` \u0026#124; `none`\u003e          | interface, method  | Sets the field matcher algorithm (default: `name`).                                   |\n| :style \u0026lt;`return` \u0026#124; `arg`\u003e         | interface, method  | Sets the style of the assignee variable input/output (default: `return`).             |\n| :recv \u0026lt;_var_\u003e                          | method             | Specifies the source value as a receiver of the generated function.                   |\n| :reverse                                  | \tmethod            | Reverses the copy direction. Might be useful with receiver form.                      |\n| :case\t                                    | interface, method  | Sets case-sensitive for name match (default).                                         |\n| :case:off\t                                | interface, method  | Sets case-insensitive for name match.                                                 |\n| :getter\t                                  | interface, method  | Includes getters for name match.                                                      |\n| :getter:off\t                              | interface, method  | Excludes getters for name match (default).                                            |\n| :stringer                                 | \tinterface, method | Calls String() if appropriate in name match.                                          |\n| :stringer:off                             | \tinterface, method | Calls String() if appropriate in name match (default).                                |\n| :typecast\t                                | interface, method\t | Allows type casting if appropriate in name match.                                     |\n| :typecast:off                             | \tinterface, method | Suppresses type casting if appropriate in name match (default).                       |\n| :skip \u0026lt;_dst field pattern_\u003e            | method             | Marks the destination field to skip copying. Regex is allowed in /…/ syntax.          |\n| :map \u0026lt;_src_\u003e \u0026lt;_dst field_\u003e          | method             | the pair as assign source and destination.                                            |\n| :conv \u0026lt;_func_\u003e \u0026lt;_src_\u003e [_to field_] | method             | Converts the source value by the converter and assigns its result to the destination. |\n| :literal \u0026lt;_dst_\u003e \u0026lt;_literal_\u003e        | method             | Assigns the literal expression to the destination.                                    |\n| :preprocess \u0026lt;_func_\u003e                   | method             | Calls the function at the beginning of the convergen func.                            |\n| :postprocess \u0026lt;_func_\u003e                  | method             | Calls the function at the end of the convergen function.                              |\n\nSample\n------\n\nTo use Convergen, write a generator code in the following convention:\n\n```go\n//go:build convergen\n\npackage sample\n\nimport (\n    \"time\"\n\n    \"github.com/sample/myapp/domain\"\n    \"github.com/sample/myapp/storage\"\n)\n\n//go:generate go run github.com/reedom/convergen@v8.0.5\ntype Convergen interface {\n    // :typecast\n    // :stringer\n    // :map Created.UnixMilli() Created\n    DomainToStorage(*domain.User) *storage.User\n}\n```\n\nConvergen generates code similar to the following:\n\n```go\n// Code generated by github.com/reedom/convergen\n// DO NOT EDIT.\n\npackage sample\n\nimport (\n    \"time\"\n\n    \"github.com/sample/myapp/domain\"\n    \"github.com/sample/myapp/storage\"\n)\n\nfunc DomainToStorage(src *domain.User) (dst *storage.User) {\n    dst = \u0026storage.User{}\n    dst.ID = int64(src.ID)\n    dst.Name = src.Name\n    dst.Status = src.Status.String()\n    dst.Created = src.Created.UnixMilli()\n\n    return\n}\n```\n\nfor these struct types:\n\n```go\npackage domain\n\nimport (\n    \"time\"\n)\n\ntype User struct {\n    ID      int\n    Name    string\n    Status  Status\n    Created time.Time\n}\n\ntype Status string\n\nfunc (s Status) String() string {\n    return string(s)\n}\n```\n\noutputs:\n\n```go\npackage storage\n\ntype User struct {\n    ID      int64\n    Name    string\n    Status  string\n    Created int64\n}\n```\n\nInstallation and Introduction\n-----------------------------\n\n### Use as a Go generator\n\nTo use Convergen as a Go generator, install the module in your Go project directory via go get:\n\n```shell\n$ go get -u github.com/reedom/convergen@latest\n```\n\nThen, write a generator as follows:\n\n```go\n//go:generate go run github.com/reedom/convergen@v0.8.2\ntype Convergen interface {\n    …\n}\n````\n\n### Use as a CLI command\n\nTo use Convergen as a CLI command, install the command via go install:\n\n```shell\n$ go install github.com/reedom/convergen@latest\n```\n\nYou can then generate code by calling:\n\n```shell\n$ convergen any-codegen-defined-code.go\n```\n\nThe CLI help shows:\n\n```shell\nUsage: convergen [flags] \u003cinput path\u003e\n\nBy default, the generated code is written to \u003cinput path\u003e.gen.go\n\nFlags:\n  -dry\n        Perform a dry run without writing files.\n  -log\n        Write log messages to \u003coutput path\u003e.log.\n  -out string\n        Set the output file path.\n  -print\n        Print the resulting code to STDOUT as well.\n```\n\nNotations\n---------\n\n### `:convergen`\n\nUse the `:convergen` notation to mark an interface as a converter definition.\n\nBy default, Convergen only looks for an interface named \"`Convergen`\" as a converter definition block.\nYou can use the `:convergen` notation to enable Convergen to recognize other interface names as well.\nThis is especially useful if you want to define methods with the same name but different receivers.\n\n__Available locations__\n\ninterface\n\n__Format__\n\n```text\n\":convergen\"\n```\n\n__Examples__\n\n```go\n// :convergen\ntype TransportConvergen interface {\n    // :recv t\n    ToDomain(*trans.Model) *domain.Model \n}\n\n// :convergen\ntype PersistentConvergen interface {\n    // :recv t\n    ToDomain(*persistent.Model) *domain.Model \n}\n\n```\n\n\n### `:match \u003calgorithm\u003e`\n\nUse the `:match` notation to set the field matcher algorithm.\n\n__Default__\n\n`:match name`\n\n__Available locations__\n\ninterface, method\n\n__Format__\n\n```text\n\":match\" \u003calgorithm\u003e\n\nalgorithm = \"name\" | none\"\n```\n\n__Examples__\n\nWith `name` match, the generator matches fields or getter names (and their types) to generate\nthe conversion code.\n\n\n```go\npackage model\n\ntype User struct {\n    ID   int\n    Name string\n}\n```\n```go\npackage web\n\ntype User struct {\n    id   int\n    name string\n}\n\nfunc (u *User) ID() int {\n  return u.id\n}\n```\n```go\n// :match name \ntype Convergen interface {\n    ToStorage(*User) *storage.User\n}\n```\n\nConvergen generates:\n\n```go\nfunc ToStorage(src *User) (dst *storage.User) {\n    dst := \u0026storage.User{}\n    dst.ID = src.ID()\n    dst.Name = src.name\n\n    return\n}\n```\n\nWith `none` match, Convergen only processes fields or getters that have been explicitly\nspecified using `:map` and `:conv`.\n\n### `:style \u003cstyle\u003e`\n\nUse the `:style` notation to set the style of the assignee variable input/output.\n\n__Default__\n\n`:style return`\n\n__Available locations__\n\ninterface, method\n\n__Format__\n\n```text\n\":style\" style\n\nstyle = \"arg\" | \"return\"\n```\n\n__Examples__\n\nExamples of `return` style:\n\nBasic:\n\n```go\nfunc ToStorage(src *domain.Pet) (dst *storage.Pet) {\n```\n\nWith error:\n\n```go\nfunc ToStorage(src *domain.Pet) (dst *storage.Pet, err error) {\n```\n\nWith receiver:\n\n```go\nfunc (src *domain.Pet) ToStorage() (dst *storage.Pet) {\n```\n\nExamples of `arg` style:\n\nBasic:\n\n```go\nfunc ToStorage(dst *storage.Pet, src *domain.Pet) {\n```\n\nWith error:\n\n```go\nfunc ToStorage(dst *storage.Pet, src *domain.Pet) (err error) {\n```\n\nWith receiver:\n\n```go\nfunc (src *domain.Pet) ToStorage(dst *storage.Pet) {\n```\n\n### `:recv \u003cvar\u003e`\n\nUse the `:recv` notation to specify the source value as a receiver of the generated function.\n\nAccording to the Go language specification, the receiver type must be defined in the same\npackage as the generated code.\n\nBy convention, the \u0026lt;_var_\u003e should be the same identifier as the methods of the type defines.\n\n__Default__\n\nNo receiver is used.\n\n__Available locations__\n\nmethod\n\n__Format__\n\n```text\n\":recv\" var\n\nvar = variable-identifier \n```\n\n__Examples__\n\nIn the following example, assume that `domain.User` is defined in another file under the same\ndirectory (package). It also assumes that other methods use `u` as their receiver variable name.\n\n\n```go\npackage domain\n\nimport (\n    \"github.com/sample/myapp/storage\"\n)\n\ntype Convergen interface {\n    // :recv u\n    ToStorage(*User) *storage.User  \n}\n```\n\nThe generated code will be:\n\n```go\npackage domain\n\nimport (\n    \"github.com/sample/myapp/storage\"\n)\n\ntype User struct {\n    ID   int\n    Name string\n}\n\nfunc (u *User) ToStorage() (dst *storage.User) {\n    dst = \u0026storage.User{}\n    dst.ID = int64(u.ID)  \n    dst.Name = u.Name\n\n    return\n}\n```\n\n### `:reverse`\n\nReverse copy direction. Might be useful with receiver form.  \nTo use `:reverse`, `:style arg` is required. (Otherwise it can't have any data source to copy from.)\nCurrently, `:reverse` not supported additional arguments.\n\n__Default__\n\nCopy in normal direction. In receiver form, receiver to a variable in argument.\n\n__Available locations__\n\nmethod\n\n__Format__\n\n```text\n\":reverse\"\n```\n\n__Examples__\n\n```go\npackage domain\n\nimport (\n    \"github.com/sample/myapp/storage\"\n)\n\ntype Convergen interface {\n    // :style arg\n    // :recv u\n    // :reverse\n    FromStorage(*User) *storage.User  \n}\n```\n\nWill have:\n\n```go\npackage domain\n\nimport (\n    \"github.com/sample/myapp/storage\"\n)\n\ntype User struct {\n    ID   int\n    Name string\n}\n\nfunc (u *User) FromStorage(src *storage.User) {\n    u.ID = int(src.User)  \n    u.Name = src.Name\n}\n```\n\n### `:case` / `:case:off`\n\nThis notation controls case-sensitive or case-insensitive matches in field and method names. \n\nIt is applicable to `:match name`, `:getter`, and `:skip` notations.   \nOther notations like `:map` and `:conv` retain case-sensitive matches.\n\n__Default__\n\n\":case\"\n\n__Available locations__\n\ninterface, method\n\n__Format__\n\n```go\n\":case\"\n\":case:off\"\n```\n\n__Examples__\n\n```go\n// interface level notation makes \":case:off\" as default.\n// :case:off\ntype Convergen interface {\n    // Turn on case-sensitive match for names.\n    // :case\n    ToUserModel(*domain.User) storage.User\n\n    // Adopt the default, case-insensitive match in this case.\n    ToCategoryModel(*domain.Category) storage.Category\n}\n```\n\n### `:getter` / `:getter:off`\n\nInclude getters for name match.\n\n__Default__\n\n`:getter:off`\n\n__Available locations__\n\ninterface, method\n\n__Format__\n\n```text\n\":getter\"\n\":getter:off\"\n```\n\n__Examples__\n\nWith those models:\n\n```go\npackage domain\n\ntype User struct {\n    name string\n}\n\nfunc (u *User) Name() string {\n    return u.name\n}\n```\n\n```go\npackage storage\n\ntype User struct {\n    Name string\n}\n```\n\nThe default Convergen behaviour can't find the private `name` and won't notice the getter.  \nSo, with the following we'll get…\n\n```go\ntype Convergen interface {\n    ToStorageUser(*domain.User) *storage.User\n}\n````\n\n```go\nfunc ToStorageUser(src *domain.User) (dst *storage.User)\n    dst = \u0026storage.User{}\n    // no match: dst.Name\n\n    return\n}\n```\n\nAnd with `:getter` we'll have…\n\n```go\ntype Convergen interface {\n    // :getter\n    ToStorageUser(*domain.User) *storage.User\n}\n````\n\n```go\nfunc ToStorageUser(src *domain.User) (dst *storage.User)\n    dst = \u0026storage.User{}\n    dst.Name = src.Name()\n\n    return\n}\n```\n\nAlternatively, you can get the same result with `:map`.  \nThis is worth learning since `:getter` affects the entire method - `:map` allows you to get\nthe result selectively. \n\n```go\ntype Convergen interface {\n    // :map Name() Name\n    ToStorageUser(*domain.User) *storage.User\n}\n```\n\n### `:stringer` / `:stringer:off`\n\nWhen matching field names, call the String() method of a custom type if it exists.\n\nBy default, Convergen has no way of knowing how to assign a custom type to a string.  \nUsing the :stringer notation will tell Convergen to look for a String() method on any custom\ntypes and use it when appropriate.\n\n__Default__\n\n`:stringer:off`\n\n__Available locations__\n\ninterface, method\n\n__Format__\n\n```text\n\":stringer\"\n\":stringer:off\"\n```\n\n__Examples__\n\nConsider the following code:\n\n```go\npackage domain\n\ntype User struct {\n    Status Status\n}\n\ntype Status struct {\n    status string\n}\n\nfunc (s Status) String() string {\n    return string(s)\n}\n\nvar (\n    NotVerified = Status{\"notVerified\"}\n    Verified    = Status{\"verified\"}\n    Invalidated = Status{\"invalidated\"}\n)\n```\n\n```go\npackage storage\n\ntype User struct {\n    String string\n}\n```\n\nWithout any additional notations, Convergen has no idea how to assign the `Status` type to a string.\nBy adding `:stringer` notation to the Convergen interface, we're telling Convergen to look for a\n`String()` method on any custom types and use it when appropriate:\n\n```go\ntype Convergen interface {\n    // :stringer\n    ToStorageUser(*domain.User) *storage.User\n}\n```\n\nConvergen will generate the following code:\n\n```go\nfunc ToStorageUser(src *domain.User) (dst *storage.User)\n    dst = \u0026storage.User{}\n    dst.Status = src.Status.String()\n\n    return\n}\n```\n\nAlternatively, you can achieve the same result with `:map`. However, `:stringer` affects \nthe entire method, while `:map` allows you to specify the fields to map selectively:\n\n```go\ntype Convergen interface {\n    // :map Status.String() Name\n    ToStorageUser(*domain.User) *storage.User\n}\n```\n\n### `:typecast`\n\nAllow type casting if appropriate in name match.\n\n__Default__\n\n`:typecast:off`\n\n__Available locations__\n\ninterface, method\n\n__Format__\n\n```text\n\":typecast\"\n\":typecast:off\"\n```\n\n__Examples__\n\nWith those models:\n\n```go\npackage domain\n\ntype User struct {\n    ID     int\n    Name   string\n    Status Status\n}\n\ntype Status string\n\n```\n\n```go\npackage storage\n\ntype User struct {\n    ID     int64  \n    Name   string\n    Status string\n}\n```\n\nConvergen respects types strictly. It will give up copying fields if their types do not match.\nNote that Convergen relies on the [types.AssignableTo(V, T Type) bool][] method from the standard\npackages. This means that the judgment is done by the type system of Go itself, not by a dumb\nstring type name match.\n\n[types.AssignableTo(V, T Type) bool]: https://pkg.go.dev/go/types#AssignableTo\n\nWithout `:typecast` turned on:\n\n```go\ntype Convergen interface {\n    ToDomainUser(*storage.User) *domain.User\n}\n````\n\nWe'll get:\n\n```go\nfunc ToDomainUser(src *storage.User) (dst *domain.User)\n    dst = \u0026domain.User{}\n    // no match: dst.ID\n    dst.Name = src.Name\n    // no match: dst.Status\n\n    return\n}\n```\n\nWith `:typecast` it turned on:\n\n```go\ntype Convergen interface {\n\t  // :typecast\n    ToDomainUser(*storage.User) *domain.User\n}\n````\n\n```go\nfunc ToDomainUser(src *storage.User) (dst *domain.User)\n    dst = \u0026domain.User{}\n    dst.ID = int(src.ID)\n    dst.Name = src.Name\n    dst.Status = domain.Status(src.Status)\n\n    return\n}\n```\n\n### `:skip \u003cdst field pattern\u003e`\n\nMark the destination field to skip copying.\n\nA method can have multiple :skip lines that enable skipping multiple fields.  \nOther than field-path match, it accepts [regular expression][] match. To specify,\nwrap the expression with `/`.  \n`:case` / `:case:off` affects `:skip`.\n\n[regular expression]: https://github.com/google/re2/wiki/Syntax\n\n__Available locations__\n\nmethod\n\n__Format__\n\n```text\n\":skip\" dst-field-pattern\n\ndst-field-pattern  = field-path | regexp\nfield-path         = { identifier \".\" } identifier\nregexp             = \"/\" regular-expression \"/\" \n```\n\n__Examples__\n\nSuppose we have the following domain and storage structs:\n\n```go\npackage domain\n\ntype User struct {\n    ID      int\n    Name    string\n    Email   string\n    Address Address\n}\n\ntype Address struct {\n    Street  string\n    City    string\n    ZipCode string\n}\n```\n\nIf we want to skip copying the Name field of the storage.User struct,\nwe can use the :skip notation as follows:\n\n```go\ntype Convergen interface {\n    // :skip Name\n    ToStorage(*domain.User) *storage.User\n}\n```\n\nIf we want to skip copying multiple fields, we can use multiple :skip notations:\n\n```go\ntype Convergen interface {\n    // :skip Name\n    // :skip Email\n    ToStorage(*domain.User) *storage.User\n}\n```\n\nWe can also use regular expressions to match multiple fields:\n\n```go\ntype Convergen interface {\n    // :skip /^Name|Email$/\n    ToStorage(*domain.User) *storage.User\n}\n```\n\nThis will result in the same generated code as the previous example.\n\n### `:map \u003csrc\u003e \u003cdst field\u003e`\n\nSpecify a field mapping rule.\n\nWhen to use:\n- copying a value between fields having different names.\n- assigning a method's result value to a destination field.\n- assigning additional argument value to a destination field.\n\nA method can have multiple `:map` lines that enable mapping multiple fields.\n\n`:case:off` does not affect `:map`;\n\u0026lt;src\u003e and \u0026lt;dst field\u003e are compared in a case-sensitive manner.  \n\n__Available locations__\n\nmethod\n\n__Format__\n\n```text\n\":map\" src dst-field\n\nsrc                   = field-or-method-chain | templated-value { \".\" field-or-getter-chain }\ndst-field             = field-path\nfield-path            = { identifier \".\" } identifier\nfield-or-getter-chain = { (identifier | getter) \".\" } (identifier | getter)\ngetter                = identifier \"()\"  \ntemplated-value       = \"$\" number-of-argument\n```\nArguments start from $1 for the first additional argument, $2 for the second, and so on.\n\n__Examples__\n\nIn the following example, two fields have the same meaning but different names.\n\n```go\npackage domain\n\ntype User struct {\n    ID   int\n    Name string\n}\n```\n```go\npackage storage\n\ntype User struct {\n    UserID int\n    Name   string\n}\n```\n\nWe can use `:map` to connect them:\n\n```go\ntype Convergen interface {\n    // Map the \"ID\" field in domain.User to the \"UserID\" field in storage.User.\n    // :map ID UserID\n    ToStorage(*domain.User) *storage.User\n}\n```\n\n```go\nfunc ToStorage(src *domain.User) (dst *storage.User) {\n    dst = storage.User{}\n    dst.UserID = src.ID\n    dst.Name = src.Name\n    \n    return\n}\n```\n\nIn the following example, Status is a custom type with a method to retrieve its raw value.\n\n```go\npackage domain\n\ntype User struct {\n    ID     int\n    Name   string\n    Status Status\n}\n\ntype Status int\n\nfunc (s Status) Int() int {\n    return int(s)\n}\n\nvar (\n    NotVerified = Status(1)\n    Verified    = Status(2)\n    Invalidated = Status(3)\n)\n```\n```go\npackage storage\n\ntype User struct {\n    UserID int\n    Name   string\n    Status int\n}\n```\n\nWe can use `:map` to apply the method's return value to assign:\n\n```go\ntype Convergen interface {\n    // Map the \"ID\" field in domain.User to the \"UserID\" field in storage.User.\n    // Map the result of the \"Status.Int()\" method in domain.User to the \"Status\" field in storage.User.\n    // :map ID UserID\n    // :map Status.Int() Status\n    ToStorage(*domain.User) *storage.User\n}\n```\n\n```go\nfunc ToStorage(src *domain.User) (dst *storage.User) {\n    dst = storage.User{}\n    dst.UserID = src.ID\n    dst.Name = src.Name\n    dst.Status = src.Status.Int()\n\n    return\n}\n```\n\nWe can use `:map` to apply the additional arguments to assign:\n\n```go\ntype Convergen interface {\n    // Map the \"ID\" field in domain.User to the \"UserID\" field in storage.User.\n    // Map the second additional argument to the \"Status\" field in storage.User.\n    // :map ID UserID\n    // :map $2 Status\n    ToStorage(*domain.User,int) *storage.User\n}\n```\n\n```go\nfunc ToStorage(src *domain.User, arg0 int) (dst *storage.User) {\n    dst = storage.User{}\n    dst.UserID = src.ID\n    dst.Name = src.Name\n    dst.Status = arg0\n\n    return\n}\n```\n\nNote that the method's return value should be compatible with the destination field.  \nIf they are not compatible, you can use `:typecast` or `:stringer` to help Convergen\nwith the conversion.  \nAlternatively, you can use `:conv` notation to define a custom conversion function.\n\n### `:conv \u003cfunc\u003e \u003csrc\u003e [dst field]`\n\nConvert the source value by the converter and assign its result to the destination.\n\n_func_ must accept _src_ value as the sole argument and return either   \n  a) a single value that is compatible with the _dst_, or  \n  a) a pair of variables as (_dst_, error).   \nFor the latter case, the method definition should have `error` in return value(s). \n\nYou can omit _dst field_ if the source and destination field paths are exactly the same.\n\n`:case:off` does not take effect on `:conv` as  \u0026lt;src\u003e and \u0026lt;dst field\u003e are compared\nin a case-sensitive manner.\n\n__Available locations__\n\nmethod\n\n__Format__\n\n```text\n\":conv\" func src [dst-field]\n\nfunc                  = identifier\nsrc                   = field-or-method-chain\ndst-field             = field-path\nfield-path            = { identifier \".\" } identifier\nfield-or-getter-chain = { (identifier | getter) \".\" } (identifier | getter)\ngetter                = identifier \"()\"  \n```\n\n__Examples__\n\n```go\npackage domain\n\ntype User struct {\n    ID    int\n    Email string\n}\n```\n```go\npackage storage\n\ntype User struct {\n    ID    int\n    Email string\n}\n```\n\nTo store an encrypted Email field, we can use a converter function:\n\n```go\nimport (\n    // The referenced library should have been imported anyhow.\n    _ \"github.com/sample/myapp/crypto\"\n)\n\ntype Convergen interface {\n    // :conv crypto.Encrypt Email\n    ToStorage(*domain.User) *storage.User\n}\n```\n\nThis results in:\n\n```go\nimport (\n    \"github.com/sample/myapp/crypto\"\n    _ \"github.com/sample/myapp/crypto\"\n)\n\nfunc ToStorage(src *domain.User) (dst *storage.User) {\n    dst = storage.User{}\n    dst.ID = src.ID\n    dst.Email = crypto.Encrypt(src.Email)\n\n    return\n}\n```\n\nIf you want to use a converter function that returns an error, you should add `error`\nto the return values of the converter method as well:\n\n```go\nimport (\n    // The referenced library should have been imported anyhow.\n    _ \"github.com/sample/myapp/crypto\"\n)\n\ntype Convergen interface {\n    // :conv crypto.Decrypt Email\n    FromStorage(*storage.User) (*domain.User, error)\n}\n```\n\nThis results in:\n\n```go\nimport (\n    \"github.com/sample/myapp/crypto\"\n    _ \"github.com/sample/myapp/crypto\"\n)\n\nfunc ToStorage(src *storage.User) (dst *domain.User, err error) {\n    dst = domain.User{}\n    dst.ID = src.ID\n    dst.Email, err = crypto.Decrypt(src.Email)\n    if err != nil {\n        return\n    }\n\n    return\n}\n```\n\n### `:literal \u003cdst\u003e \u003cliteral\u003e`\n\nAssign a literal expression to the destination field.\n\n__Available locations__\n\nmethod\n\n__Format__\n\n```text\n\":literal\"  dst literal\n```\n\n__Examples__\n\n```go\ntype Convergen interface {\n    // :literal Created time.Now()\n    FromStorage(*storage.User) *domain.User()\n}\n```\n\n### `:preprocess \u003cfunc\u003e` / `:postprocess \u003cfunc\u003e`\n\nCall the function at the beginning(`preprocess`) or at the end(`postprocess`) of the convergen function.\n\n__Available locations__\n\nmethod\n\n__Format__\n\n```text\n\":preprocess\"  func\n\":postprocess\" func\n\nfunc  = identifier\n```\n\n__Examples__\n\n```go\ntype Convergen interface {\n    // :preprocess prepareInput\n    // :postprocess cleanUpOutput\n    FromStorage(*storage.User) *domain.User\n}\n\nfunc prepareInput(dst *domain.User, src *storage.User) {\n    // do something before conversion\n    return src\n}\n\nfunc cleanUpOutput(dst *domain.User, src *storage.User) *domain.User {\n    // do something after conversion\n    return dst\n}\n```\n\nWhen FromStorage is called, the prepareInput function will be called before the conversion takes place. \nThen the FromStorage method will be executed. \nFinally, the cleanUpOutput function will be called with the output result after the \nconversion has taken place.\n\n```go\ntype Convergen interface {\n    // :preprocess prepareInput\n    // :postprocess cleanUpOutput\n    FromStorage(*storage.User) (*domain.User, error)\n}\n\nfunc prepareInput(dst *domain.User, src *storage.User) error {\n    // do something before conversion\n    return nil\n}\n\nfunc cleanUpOutput(dst *domain.User, src *storage.User) error {\n    // do something after conversion\n    return dst\n}\n```\n\n\nThe `preprocess` and `postprocess` functions also support additional arguments:\n```go\ntype Convergen interface {\n    // :preprocess prepareInput\n    // :postprocess cleanUpOutput\n    FromStorage(*storage.User,int, string) (*domain.User, error)\n}\n\nfunc prepareInput(dst *domain.User, src *storage.User, arg0 int, arg1 string) error {\n    // do something before conversion\n    return nil\n}\n\nfunc cleanUpOutput(dst *domain.User, src *storage.User, arg0 int, arg1 string) error {\n    // do something after conversion\n    return dst\n}\n```\n\n\nContributing\n------------\n\nFor those who want to contribute, there are several ways to do it, including:\n\n- Reporting bugs or issues that you encounter while using Convergen.\n- Suggesting new features or improvements to the existing ones.\n- Implementing new features or fixing bugs by making a pull request to the project.\n- Improving the documentation or examples to make it easier for others to use Convergen.\n- Creating a project's logo to help with its branding.\n- Showing your support by giving the project a star.\n\nBy contributing to the project, you can help make it better and more useful for everyone.\nSo, if you're interested, feel free to get involved!\n","funding_links":["https://github.com/sponsors/reedom"],"categories":["发电机","Generators"],"sub_categories":["检索及分析资料库","Search and Analytic Databases"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freedom%2Fconvergen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freedom%2Fconvergen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freedom%2Fconvergen/lists"}