{"id":22885086,"url":"https://github.com/rendis/structsconv","last_synced_at":"2025-05-07T08:12:22.065Z","repository":{"id":132621759,"uuid":"471072223","full_name":"rendis/structsconv","owner":"rendis","description":"A mapstruct to convert struct from one type to another","archived":false,"fork":false,"pushed_at":"2023-03-23T15:40:49.000Z","size":112,"stargazers_count":4,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-07T08:12:14.322Z","etag":null,"topics":["go","golang","mapper","mapper-struct","mapping-tools","mapstruct"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rendis.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,"governance":null}},"created_at":"2022-03-17T17:07:22.000Z","updated_at":"2024-07-13T10:19:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"d9c2c2c2-3ef4-4544-bb71-52a203420e48","html_url":"https://github.com/rendis/structsconv","commit_stats":{"total_commits":33,"total_committers":1,"mean_commits":33.0,"dds":0.0,"last_synced_commit":"f09505e316605db05249e539c58e833b68c7e813"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Fstructsconv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Fstructsconv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Fstructsconv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Fstructsconv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rendis","download_url":"https://codeload.github.com/rendis/structsconv/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252839295,"owners_count":21812090,"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":["go","golang","mapper","mapper-struct","mapping-tools","mapstruct"],"created_at":"2024-12-13T19:31:45.348Z","updated_at":"2025-05-07T08:12:22.048Z","avatar_url":"https://github.com/rendis.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Structs Conversion (structsconv)\n\n[![coverage](https://api.codiga.io/project/32663/score/svg)](https://app.codiga.io/public/project/32663/structsconv/dashboard)\n[![quality](https://api.codiga.io/project/32663/status/svg)](https://app.codiga.io/public/project/32663/structsconv/dashboard)\n[![GoDoc](https://pkg.go.dev/badge/rendis/structsconv?status.svg)](https://pkg.go.dev/github.com/rendis/structsconv?tab=doc)\n---\n\nThe **structsconv** package makes it easy to convert between structs of different types. This development is inspired by\nthe java [mapstruct](https://mapstruct.org/) library.\n\n## Motivation\nThe main purpose of this library is that, within a `hexagonal architecture`, the conversion can be done without \nthe need to _dirty_ the domain layer with `tags`.\n\nFor more information on how to use, see the [examples](https://github.com/rendis/structsconv/tree/master/example).\n\n## Release Compatibility\n\n- v1.0.0: is compatible with Golang versions:\n    - 1.17+\n\n## Installation\n\n1. Install the package:\n\n```bash\ngo get -u github.com/rendis/structsconv\n```\n\n2. Import the package:\n\n```go\nimport \"github.com/rendis/structsconv\"\n```\n\n## Quick start\n\n### Default Mapping\nFields with the `same name` and `same type` are mapped by default.\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n#### simple struct \u0026rarr; simple struct\n\nStructs\n```go\n// Source struct\ntype SiteSource struct {\n  Name string\n  URL  string\n}\n```\n\n```go\n// Target struct\ntype SiteTarget struct {\n  Name string\n  URL  string\n}\n```\n\nMapping\n```go\n// Mapping struct\nsource := SiteSource{\n  Name: \"Golang\",\n  URL:  \"https://golang.org\",\n}\ntarget := SiteTarget{}\n\nstructsconv.Map(\u0026source, \u0026target)\nfmt.Printf(\"%+v\\n\", target) // Output: {Name:Golang URL:https://golang.org }\n```\n---\n\u003cbr/\u003e\n\n#### nested struct \u0026rarr; nested struct\n\nStructs\n```go\n// Source struct\ntype SiteSource struct {\n  Name string\n  URL  string\n  Meta MetaInfoSource\n}\n\ntype MetaInfoSource struct {\n    Description string\n}\n```\n\n```go\n// Target struct\ntype SiteTarget struct {\n  Name string\n  URL  string\n  Meta MetaInfoTarget\n}\n\ntype MetaInfoTarget struct {\n    Description string\n}\n```\n\nMapping\n```go\n// Mapping struct\nsource := SiteSource{\n  Name: \"Golang\",\n  URL:  \"https://golang.org\",\n  Meta: MetaInfoSource{\n    Description: \"Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.\",\n  },\n}\ntarget := SiteTarget{}\n\nstructsconv.Map(\u0026source, \u0026target)\nfmt.Printf(\"%+v\\n\", target) // Output: {Name:Golang URL:https://golang.org Meta:{Description:Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.}}\n```\n---\n\u003cbr/\u003e\n\n#### slice \u0026rarr; slice\n\nStructs\n```go\n// Source struct\ntype ProductDto struct {\n  Name     string\n  Price    int\n  Keywords []string\n  Locals   []LocalDto\n}\n\ntype LocalDto struct {\n  Id    int\n  Stock int\n}\n```\n\n```go\n// Target struct\ntype ProductDomain struct {\n  Name     string\n  Price    int\n  Keywords []string\n  Locals   []LocalDomain\n}\n\ntype LocalDomain struct {\n  Id    int\n  Stock int\n}\n```\n\nMapping\n```go\n// Mapping struct\nsource := ProductDto{\n  Name:     \"go shoes\",\n  Price:    100,\n  Keywords: []string{\"shoes\", \"sneakers\"},\n  Locals: []LocalDto{\n    {Id: 1, Stock: 10},\n    {Id: 2, Stock: 20},\n  },\n}\ntarget := ProductDomain{}\n\nstructsconv.Map(\u0026source, \u0026target)\nfmt.Printf(\"%+v\\n\", target) // Output: {Name:go shoes Price:100 Keywords:[shoes sneakers] Locals:[{Id:1 Stock:10} {Id:2 Stock:20}]}\n```\n---\n\u003cbr/\u003e\n\n#### map \u0026rarr; map\n\u003e  - Source map key type must be equal to target map key type\n\u003e  - Source map value type must be equal to target map value type\n\n\u003e Everything directly associated with the map type **must be exported**.\n\nStructs\n```go\n// Source struct\ntype SchoolDto struct {\n    Students map[string]StudentDto\n}\n\ntype StudentDto struct {\n  Id  string\n  Name string\n  Age  int\n}\n```\n\n```go\n// Target struct\ntype SchoolDomain struct {\n    Students map[string]StudentDomain\n}\n\ntype StudentDomain struct {\n  Id  string\n  Name string\n  Age  int\n}\n```\n\nMapping\n```go\n// Mapping struct\nsource := SchoolDto{\n  Students: map[string]StudentDto{\n    \"stu124\": {\n      Id:    \"stu124\",\n      Name: \"John\",\n      Age:  20,\n    },\n    \"stu125\": {\n      Id:    \"stu125\",\n      Name: \"Jane\",\n      Age:  21,\n    },\n  },\n}\ntarget := SchoolDomain{}\n\nstructsconv.Map(\u0026source, \u0026target)\nfmt.Printf(\"%+v\\n\", target) // Output: {Students:{stu124:{Id:stu124 Name:John Age:20} stu125:{Id:stu125 Name:Jane Age:21}}}\n```\n---\n\u003cbr/\u003e\n\n### Rules\nRules are used to customize the assignment. Use when desired:\n* Map fields with _different names_ or _different types_ or both.\n* Define a _constant_.\n* Ignore a field.\n* A custom mapping.\n\n\u003cbr/\u003e\n\n#### Rules for fields with different names\nFields with the `different name` and `same type` are mapped using `rules`.\n\nStructs\n```go\n// Source struct\ntype SiteSource struct {\n  Name string\n  URL  string\n}\n```\n\n```go\n// Target struct\ntype SiteTarget struct {\n  SiteName string\n  URL  string\n}\n```\n\nCreate the rules\n```go\nvar rules = structsconv.RulesSet{}   // Create a new rules set\nrules[\"SiteName\"] = \"Name\"           // Rule to map 'Name' to 'SiteName'\nsiteRulesDefinition := structsconv.RulesDefinition{\n    Rules:  rules,\n    Source: SiteSource{},            // To get the source struct type\n    Target: SiteTarget{},            // To get the target struct type\n}\n```\n\u003e The rules are created based on the **names on the target struct**.\n\u003cbr/\u003e\n\nRegister the rules\n```go\nstructsconv.RegisterRulesDefinitions(siteRulesDefinition)\n```\n\nMapping\n```go\nsource := SiteSource{\n    Name: \"Golang\",\n    URL:  \"https://golang.org\",\n}\ntarget := SiteTarget{}\n\nstructsconv.Map(\u0026source, \u0026target)\nfmt.Printf(\"%+v\\n\", target)\n```\n---\n\u003cbr/\u003e\n\n\n#### Rules for constants\nStructs\n```go\n//Source struct\ntype ItemDto struct {\n\tId   int\n\tName string\n}\n```\n\n```go\n// Target struct\ntype ItemDomain struct {\n\tId            int\n\tItemName      string\n\tConstantValue string  // Field does not exist in the source struct\n}\n```\n\nCreate the rules\n```go\nvar rules = structsconv.RulesSet{}\n\nrules[\"ItemName\"] = \"Name\"\n\nrules[\"ConstantValue\"] = func() string {\n    return \"Constant value\"\n}\n\nrulesDefinition := structsconv.RulesDefinition{\n    Rules:  rules,\n    Source: ItemDto{},\n    Target: ItemDomain{},\n}\n```\nRegister the rules\n```go\nstructsconv.RegisterRulesDefinitions(rulesDefinition)\n```\nMapping\n```go\nsource := ItemDto{\n    Id:   1,\n    Name: \"Toy\",\n}\ntarget := ItemDomain{}\n\nstructsconv.Map(\u0026source, \u0026target)\nfmt.Printf(\"%+v\\n\", target) // Output: {Id:1 ItemName:Toy ConstantValue:Constant value}\n```\n---\n\u003cbr/\u003e\n\n\n#### Rules for ignoring fields\nTo ignore a field, just set the `rules` to `nil`.\n\nStructs\n```go\n//Source struct\ntype ItemDto struct {\n\tId   int\n\tName string\n}\n```\n\n```go\n// Target struct\ntype ItemDomain struct {\n\tId            int\n\tItemName      string\n\tIgnorableValue string  // Field does not exist in the source struct\n}\n```\n\nCreate the rules\n```go\nvar rules = structsconv.RulesSet{}\n\nrules[\"ItemName\"] = \"Name\"\n\nrules[\"IgnorableValue\"] = nil\n\nrulesDefinition := structsconv.RulesDefinition{\n    Rules:  rules,\n    Source: ItemDto{},\n    Target: ItemDomain{},\n}\n```\n\nRegister the rules\n```go\nstructsconv.RegisterRulesDefinitions(rulesDefinition)\n```\n\nMapping\n```go\nsource := ItemDto{\n    Id:   1,\n    Name: \"Toy\",\n}\ntarget := ItemDomain{}\n\nstructsconv.Map(\u0026source, \u0026target)\nfmt.Printf(\"%+v\\n\", target) // Output: { Id:1 ItemName:Toy IgnorableValue:\"\" }\n```\n---\n\u003cbr/\u003e\n\n\n### Rules with arguments\n\n#### Root Struct \u0026 Current Struct\nYou will always be able to request the `root origin struct` and the `current origin struct`, to use them in custom mapping, you just need to request them. \n\nLet see an example with 2 levels of nested structs:\n```go\n// Source struct\ntype ParentRootDto struct {\n  ParentField string\n  Child1      Child1Dto\n}\n\ntype Child1Dto struct {\n  Level1Field string\n  Child       Child2Dto\n}\n\ntype Child2Dto struct {\n    Level2Field string\n}\n```\n\n```go\n// Target struct\ntype ParentRootDomain struct {\n  ParentField string       // Mapped directly\n  Child1      Child1Domain // Mapped directly\n}\n\ntype Child1Domain struct {\n  Level1Field string       // Mapped directly\n  Child       Child2Domain // Mapped directly\n  \n  ParentField string       // Custom mapping\n  Description string       // Custom mapping\n}\n\ntype Child2Domain struct {\n  Level2Field string       // Mapped directly\n  \n  ParentField string       // Custom mapping\n  Level1Field string       // Custom mapping\n  Description string       // Custom mapping\n}\n```\n\nRules for `Child1Domain`\n```go\nvar child1Rules = structsconv.RulesSet{}\n\nchild1Rules[\"ParentField\"] = func(parent ParentRootDto) string {\n    return parent.ParentField\n}\n\nchild1Rules[\"Description\"] = func(current Child1Dto, parent ParentRootDto) string {\n    return fmt.Sprintf(\"root parent = %s,  current = %s\", parent.ParentField, current.Level1Field)\n}\n\nchild1Definition := structsconv.RulesDefinition{\n    Rules:  child1Rules,\n    Source: Child1Dto{},\n    Target: Child1Domain{},\n}\n```\n\nRules for `Child2Domain`\n```go\nvar child2Rules = structsconv.RulesSet{}\n\n// Note that parent is ParentRootDto and not Child1Dto\nchild2Rules[\"ParentField\"] = func(parent ParentRootDto) string {\n    return parent.ParentField\n}\n\n// Note that parent is ParentRootDto and not Child1Dto\nchild2Rules[\"Level1Field\"] = func(parent ParentRootDto) string {\n    return parent.Child1.Level1Field\n}\n\n// Note that parent is ParentRootDto and not Child1Dto\nchild2Rules[\"Description\"] = func(parent ParentRootDto, current Child2Dto) string {\n    return fmt.Sprintf(\n        \"root parent = %s, parent = %s, current = %s\",\n        parent.ParentField, parent.Child1.Level1Field, current.Level2Field,\n    )\n}\n\nchild2Definition := structsconv.RulesDefinition{\n    Rules:  child2Rules,\n    Source: Child2Dto{},\n    Target: Child2Domain{},\n}\n```\n\u003e **Note that parent is `ParentRootDto` and not `Child1Dto`**.\n\nRegister the rules\n```go\nstructsconv.RegisterRulesDefinitions(child1Definition)\nstructsconv.RegisterRulesDefinitions(child2Definition)\n```\n\nMapping\n```go\nsource := ParentRootDto{\n    ParentField: \"ParentField (root)\",\n    Child1: Child1Dto{\n        Level1Field: \"level1 (Child of rut, parent of child2)\",\n        Child: Child2Dto{\n            Level2Field: \"level2 (last level, Child of Child1)\",\n        },\n    },\n}\n\ntarget := ParentRootDomain{}\n\nstructsconv.Map(\u0026source, \u0026target) // To long to print but works :)\nfmt.Printf(\"%+v\\n\", target)\n```\n---\n\u003cbr/\u003e\n\n#### More and more arguments\nYou can also get more than just the root struct and the current struct as an argument.\n\nBy passing arguments to the `map()` function, they will be available for request in all rules.\n\n```go\n...\nstr1 = \"str1\"\nstr2 = \"str2\"\nint1 = 1\nint2 = 2\nstruct1 = struct{}{}\nstruct2 = struct{}{}\nstructsconv.Map(\u0026source, \u0026target, str1, str2, int1, int2, struct1, struct2)\n...\n```\nTo use them in the rules, you only need to request the ones you need.\n* Request all the arguments (Ordered)\n```go \nrules[\"targetField1\"] = func(str1, str2 string, int1, int2 int, struct1 struct{}, struct2 struct{}) string {\n    return fmt.Sprintf(\"%s %s %d %d %+v %+v\", str1, str2, int1, int2, struct1, struct2)\n}\n```\n\n* Request all argument \"Unordered\" (1)\n```go\nrules[\"targetField1\"] = func(struct1 struct{}, struct2 struct{}, int1, int2 int, str1, str2 string) string {\n    return fmt.Sprintf(\"%s %s %d %d %+v %+v\", str1, str2, int1, int2, struct1, struct2)\n} \n```\n\n* Request all argument \"Unordered\" (2)\n```go\nrules[\"targetField1\"] = func(struct1 struct{}, int1 int, str1 string, struct2 struct{}, str2 string, int2 int) string {\n    return fmt.Sprintf(\"%s %s %d %d %+v %+v\", str1, str2, int1, int2, struct1, struct2)\n} \n```\n\nNotice the double quotes around the word \"Unordered\", this is because arguments can be requested out of order relative to their type, arguments of the same type must be requested in the same order as they were passed.\n\nLet's see some examples:\n```go\nstr1 = \"str1\"\nstr2 = \"str2\"\nstructsconv.Map(\u0026source, \u0026target, str1, str2)\n```\n```go\nrules[\"targetField1\"] = func(struct1 struct{}, arg1 string, arg2 string) string {\n    return fmt.Sprintf(\"%s %s\", str1, str2)\n}\n```\n```go\nrules[\"targetField1\"] = func(struct1 struct{}, arg2 string, arg1 string) string {\n    return fmt.Sprintf(\"%s %s\", str1, str2)\n}\n```\nIn both cases, the first argument is `str1` and the second is `str2`.\n\nIn summary, the rules are:\n* Order does not matter on different types.\n* Order matters on the same types.\n\nAnother way to use argument passing is through `slices`.\n```go\nstr1 = \"hello\"\nstr2 = \"world\"\nint1 = 1\nint2 = 2\nargs := []interface{}{str1, str2, int1, int2}\nstructsconv.Map(\u0026source, \u0026target, args)\n```\n```go\nrules[\"targetField1\"] = func(args []interfaces{}) string {\n    return fmt.Sprintf(\"first args = %s, last args = %d\", args[0], str2[2]) // first args = hello, last args = 2\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frendis%2Fstructsconv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frendis%2Fstructsconv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frendis%2Fstructsconv/lists"}