From 57d11ffdea0cdfe946beda95b76be7891b7e0971 Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Sat, 22 Jun 2024 22:00:57 +0200 Subject: [PATCH] docs: input/output formats --- docs/.vitepress/config.mts | 6 +- docs/changelog.md | 7 +- docs/guide/format.md | 77 +++++++++++++++++++ docs/reference/converter.md | 4 +- docs/reference/define-settings.md | 32 +++++++- docs/reference/enum.md | 10 +-- docs/reference/extend.md | 2 +- docs/reference/ignoreMissing.md | 4 +- docs/reference/ignoreUnexported.md | 4 +- docs/reference/matchIgnoreCase.md | 2 +- docs/reference/name.md | 2 +- docs/reference/output.md | 46 ++++++++++- docs/reference/settings.md | 16 ++-- docs/reference/skipCopySameType.md | 4 +- docs/reference/struct.md | 2 +- docs/reference/useUnderlyingTypeMethods.md | 2 +- .../useZeroValueOnPointerInconsistency.md | 2 +- docs/reference/variables.md | 16 ++++ docs/reference/wrapErrors.md | 2 +- docs/reference/wrapErrorsUsing.md | 2 +- example/format/assignvariables/input.gen.go | 48 ++++++++++++ example/format/assignvariables/input.go | 35 +++++++++ example/format/common/common.go | 47 +++++++++++ .../interfacefunction/generated/generated.go | 43 +++++++++++ example/format/interfacefunction/input.go | 25 ++++++ .../interfacetostruct/generated/generated.go | 51 ++++++++++++ example/format/interfacetostruct/input.go | 35 +++++++++ 27 files changed, 490 insertions(+), 36 deletions(-) create mode 100644 docs/guide/format.md create mode 100644 docs/reference/variables.md create mode 100644 example/format/assignvariables/input.gen.go create mode 100644 example/format/assignvariables/input.go create mode 100644 example/format/common/common.go create mode 100644 example/format/interfacefunction/generated/generated.go create mode 100644 example/format/interfacefunction/input.go create mode 100644 example/format/interfacetostruct/generated/generated.go create mode 100644 example/format/interfacetostruct/input.go diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 00da6184..18c937b0 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -42,12 +42,13 @@ export default defineConfig({ sidebar: [ { text: "Intro", link: "/" }, { text: "Use-Cases", link: "/use-case" }, + { text: "Error early", link: "/guide/error-early" }, { text: "Guides", items: [ { text: "Getting Started", link: "/guide/getting-started" }, { text: "Installation", link: "/guide/install" }, - { text: "Error early", link: "/guide/error-early" }, + { text: "Input/Output formats", link: "/guide/format" }, { text: "Convert Enums", link: "/guide/enum" }, { text: "Output into same package", @@ -77,7 +78,7 @@ export default defineConfig({ { text: "Settings Overview", link: "/reference/settings" }, { text: "Enums", link: "/reference/enum" }, { - text: "Converter", + text: "Conversion", collapsed: true, items: [ { text: "converter", link: "/reference/converter" }, @@ -85,6 +86,7 @@ export default defineConfig({ { text: "name", link: "/reference/name" }, { text: "output", link: "/reference/output" }, { text: "struct", link: "/reference/struct" }, + { text: "variables", link: "/reference/variables" }, ], }, { diff --git a/docs/changelog.md b/docs/changelog.md index 1ea6be13..51441542 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,7 +6,12 @@ import GH from './GH.vue'; ## unreleased -- tbd +- Add two input-output formats. See + [Guide: Input/Output formats](guide/format.md) for help to choose a format. + The new formats allow you to call top level functions without the need to + instantiate a struct to call methods on it. + - Add [`variables`](reference/variables.md) setting + - Add [`output:format`](reference/output.md#output-format) setting ## v1.4.1 diff --git a/docs/guide/format.md b/docs/guide/format.md new file mode 100644 index 00000000..0042f47f --- /dev/null +++ b/docs/guide/format.md @@ -0,0 +1,77 @@ +# Input and output formats + +Goverter supports three different input output format combinations. This guide +is for you to decide the formats you want to use. + +[[toc]] + +## interface to struct + +This is the default when using +[`goverter:converter`](../reference/converter.md). + +**Pros**: + +- All goverter features are supported +- The interface is usable before goverter generated the implementation. + - reduces the occurence of compile errors because of missing or outdated + generated implementation. + - allows using generated methods in custom methods + +**Cons**: + +- You need to initialize the implementation. +- You need to call methods on the struct to execute conversions + +::: details Example (click me) +::: code-group +<<< @../../example/format/interfacetostruct/input.go +<<< @../../example/format/common/common.go +<<< @../../example/format/interfacetostruct/generated/generated.go [generated/generated.go] +::: + +## variables to assign-variable + +This is the default when using +[`goverter:variables`](../reference/converter.md). + +**Pros**: + +- All goverter features are supported. +- The variables are usable before goverter generated the implementation. + - reduces the occurence of compile errors because of missing or outdated + generated implementation. + - allows using generated functions in custom methods +- You can execute conversions directly without having to initializing a struct + +**Cons**: + +- Possible runtime overhead when Go cannot optimizen variables the same as + functions. Benchmark your use-case, if speed is really important to you. + +::: details Example (click me) +::: code-group +<<< @../../example/format/assignvariables/input.go +<<< @../../example/format/common/common.go +<<< @../../example/format/assignvariables/input.gen.go +::: + +## interface to functions + +**Pros**: + +- You can execute conversions directly without having to initializing a struct + +**Cons**: + +- The interface is only used for defining the conversions and is otherwise not + usable +- The use-case [`map` method with converter](../reference/map.md#method-with-converter) is + unsupported without replacement. + +::: details Example (click me) +::: code-group +<<< @../../example/format/interfacefunction/input.go +<<< @../../example/format/common/common.go +<<< @../../example/format/interfacefunction/generated/generated.go +::: diff --git a/docs/reference/converter.md b/docs/reference/converter.md index c4e64c53..17583700 100644 --- a/docs/reference/converter.md +++ b/docs/reference/converter.md @@ -1,7 +1,7 @@ # Setting: converter -`converter` accepts no arguments and can be defined as [converter -comment](./define-settings.md#converter). +`converter` accepts no arguments and can be defined as [conversion +comment](./define-settings.md#conversion). `converter` instructs goverter to generate an implementation for the given interface. You can have multiple converters in one package. diff --git a/docs/reference/define-settings.md b/docs/reference/define-settings.md index 7ed6b3a6..4e915784 100644 --- a/docs/reference/define-settings.md +++ b/docs/reference/define-settings.md @@ -28,9 +28,10 @@ type Converter interface { the resolved settings would be the same as with the example below for Converter. -## Converter +## Conversion -You can define settings on the converter interface by prefixing them with `goverter:`. +When using [`goverter:converter`](converter.md), then you can define settings +on the converter interface by prefixing them with `goverter:`. ```go // goverter:converter @@ -41,9 +42,22 @@ type Converter interface { } ``` +When using [`goverter:variables`](variables.md), then you can define settings +on the `var`-block by prefixing them with `goverter:`. + +```go +// goverter:variables +// goverter:setting yes +// goverter:setting2 no +var ( + Convert func(source Input) Output +) +``` + ## Method -You can define settings on the converter methods by prefixing them with `goverter:`. +When using [`goverter:converter`](converter.md), then you can define settings +on the converter methods by prefixing them with `goverter:`. ```go // goverter:converter @@ -54,6 +68,18 @@ type Converter interface { } ``` +When using [`goverter:variables`](variables.md), then you can define settings +on the function variables by prefixing them with `goverter:`. + +```go +// goverter:variables +var ( + // goverter:setting yes + // goverter:setting2 no + Convert func(source Input) Output +) +``` + ### Inheritance Method settings can be inherited for all methods if they are defined on the CLI diff --git a/docs/reference/enum.md b/docs/reference/enum.md index 519159f2..b943ee64 100644 --- a/docs/reference/enum.md +++ b/docs/reference/enum.md @@ -10,7 +10,7 @@ type](https://go.dev/ref/spec#Underlying_types) of (`float`, `string`, or ## enum `enum [yes|no]` can be defined as [CLI argument](./define-settings.md#cli) or -[converter comment](./define-settings.md#converter). `enum` is enabled per +[conversion comment](./define-settings.md#conversion). `enum` is enabled per default. `enum` allows you to disable enum support in goverter. @@ -25,8 +25,8 @@ default. `enum:unknown ACTION|KEY` can be defined as [CLI -argument](./define-settings.md#cli), [converter -comment](./define-settings.md#converter) or [method +argument](./define-settings.md#cli), [conversion +comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method). This setting is [inheritable](./define-settings.md#inheritance). @@ -77,8 +77,8 @@ statement. ## enum:exclude `enum:exclude [PACKAGE:]NAME` can be defined as [CLI -argument](./define-settings.md#cli) or [converter -comment](./define-settings.md#converter). +argument](./define-settings.md#cli) or [conversion +comment](./define-settings.md#conversion). You can exclude falsely detected enums with exclude. This is useful when a type [qualifies as enum](#definition) but isn't one. If `PACKAGE` is unset, goverter diff --git a/docs/reference/extend.md b/docs/reference/extend.md index c6fd141a..058a9999 100644 --- a/docs/reference/extend.md +++ b/docs/reference/extend.md @@ -1,7 +1,7 @@ # Setting: extend `extend [PACKAGE:]TYPE...` can be defined as [CLI argument](./define-settings.md#cli) or -[converter comment](./define-settings.md#converter). +[conversion comment](./define-settings.md#conversion). If a type cannot be converted automatically, you can manually define an implementation with `:extend` for the missing mapping. Keep in mind, diff --git a/docs/reference/ignoreMissing.md b/docs/reference/ignoreMissing.md index bc65ad49..329e9d2f 100644 --- a/docs/reference/ignoreMissing.md +++ b/docs/reference/ignoreMissing.md @@ -1,8 +1,8 @@ # Setting: ignoreMissing `ignoreMissing [yes,no]` is a [boolean setting](./define-settings.md#boolean) and -can be defined as [CLI argument](./define-settings.md#cli), [converter -comment](./define-settings.md#converter) or [method +can be defined as [CLI argument](./define-settings.md#cli), [conversion +comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method). This setting is [inheritable](./define-settings.md#inheritance). diff --git a/docs/reference/ignoreUnexported.md b/docs/reference/ignoreUnexported.md index 82ae1855..2c5f053b 100644 --- a/docs/reference/ignoreUnexported.md +++ b/docs/reference/ignoreUnexported.md @@ -1,8 +1,8 @@ # Setting: ignoreUnexported `ignoreUnexported [yes,no]` is a [boolean setting](./define-settings.md#boolean) -and can be defined as [CLI argument](./define-settings.md#cli), [converter -comment](./define-settings.md#converter) or [method +and can be defined as [CLI argument](./define-settings.md#cli), [conversion +comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method). This setting is [inheritable](./define-settings.md#inheritance). diff --git a/docs/reference/matchIgnoreCase.md b/docs/reference/matchIgnoreCase.md index d0e1fd3e..7a9062d3 100644 --- a/docs/reference/matchIgnoreCase.md +++ b/docs/reference/matchIgnoreCase.md @@ -3,7 +3,7 @@ `matchIgnoreCase [yes,no]` is a [boolean setting](./define-settings.md#boolean) and can be defined as [CLI argument](./define-settings.md#cli), -[converter comment](./define-settings.md#converter) or +[conversion comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method). This setting is [inheritable](./define-settings.md#inheritance). diff --git a/docs/reference/name.md b/docs/reference/name.md index 50efad44..b70644f7 100644 --- a/docs/reference/name.md +++ b/docs/reference/name.md @@ -1,7 +1,7 @@ # Setting: name `name NAME` can be defined as [CLI argument](./define-settings.md#cli) or -[converter comment](./define-settings.md#converter). +[conversion comment](./define-settings.md#conversion). `name` instructs goverter to use the given *name* for the generated struct. By default goverter will use the interface name and append `Impl` at the end. diff --git a/docs/reference/output.md b/docs/reference/output.md index f26cc946..27a8d171 100644 --- a/docs/reference/output.md +++ b/docs/reference/output.md @@ -1,9 +1,11 @@ # Setting: output +[[toc]] + ## output:file `output:file FILE` can be defined as [CLI argument](./define-settings.md#cli) or -[converter comment](./define-settings.md#converter). Default is +[conversion comment](./define-settings.md#conversion). Default is `./generated/generated.go`. `output:file` sets the generated output file of a converter interface. The file @@ -28,10 +30,50 @@ the same. See this more complex example: <<< @../../example/output-multiple-files/b/generated.go [b/generated.go] ::: +## output:format + +`output:format FORMAT` can be defined as [CLI argument](./define-settings.md#cli) or +[conversion comment](./define-settings.md#conversion). Default `struct` + +Specify the output FORMAT for the conversion methods. See [Guide: Input/Output formats](../guide/format.md) + +### output:format struct + +Output an implementation of the conversion interface by creating a struct with methods. + +::: details Example (click me) +::: code-group +<<< @../../example/format/interfacetostruct/input.go +<<< @../../example/format/common/common.go +<<< @../../example/format/interfacetostruct/generated/generated.go [generated/generated.go] +::: + +### output:format assign-variable + +Output an init function assiging an implementation for all function variables. + +::: details Example (click me) +::: code-group +<<< @../../example/format/assignvariables/input.go +<<< @../../example/format/common/common.go +<<< @../../example/format/assignvariables/input.gen.go +::: + +### output:format function + +Output a function for each method in the conversion interface. + +::: details Example (click me) +::: code-group +<<< @../../example/format/interfacefunction/input.go +<<< @../../example/format/common/common.go +<<< @../../example/format/interfacefunction/generated/generated.go [generated/generated.go] +::: + ## output:package `output:package [PACKAGE][:NAME]` can be defined as -[CLI argument](./define-settings.md#cli) or [converter comment](./define-settings.md#converter). +[CLI argument](./define-settings.md#cli) or [conversion comment](./define-settings.md#conversion). Default is `:generated`. ### output:package PACKAGE diff --git a/docs/reference/settings.md b/docs/reference/settings.md index e5008860..b57fbc7d 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -4,21 +4,23 @@ Before configuring settings here it is useful to understand how [converter generation works](../explanation/generation.md) and how to [configure nested settings](../guide/configure-nested.md). -## Converter +## Conversion These settings can only be defined as [CLI argument](./define-settings.md#cli) or -[converter comment](./define-settings.md#converter). +[conversion comment](./define-settings.md#conversion). - [`converter` marker comment for conversion interfaces](./converter.md) - [`enum [yes|no]` enable / disable enum support](./enum.md#enum-detect) - [`enum:exclude [PACKAGE:]NAME` exclude wrongly detected enums](./enum.md#enum-exclude) - [`extend [PACKAGE:]TYPE...` add custom methods for conversions](./extend.md) - [`name NAME` rename generated struct](./name.md) -- [`output:file FILE` set the output directory for a converter](./output.md#outputfile) -- [`output:package [PACKAGE:]NAME` set the output package for a converter](./output.md#outputpackage) -- [`struct:comment COMMENT` add comments to generated struct](./struct.md#structcomment-comment) +- [`output:file FILE` set the output directory for a converter](./output.md#output-file) +- [`output:format FORMAT` set the output format](./output.md#output-format) +- [`output:package [PACKAGE:]NAME` set the output package for a converter](./output.md#output-package) +- [`struct:comment COMMENT` add comments to generated struct](./struct.md#struct-comment-comment) +- [`variables` marker comment for variable blocks](./variables.md) -## Method: +## Method These settings can only be defined as [method comment](./define-settings.md#method). @@ -38,7 +40,7 @@ These settings can only be defined as [method comment](./define-settings.md#meth ### Method (inheritable) These settings can be defined as [CLI argument](./define-settings.md#cli), -[converter comment](./define-settings.md#converter) or +[conversion comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method) and are [inheritable](./define-settings.md#inheritance). diff --git a/docs/reference/skipCopySameType.md b/docs/reference/skipCopySameType.md index 213abc0e..465ba59c 100644 --- a/docs/reference/skipCopySameType.md +++ b/docs/reference/skipCopySameType.md @@ -1,8 +1,8 @@ # Setting: skipCopySameType `skipCopySameType [yes,no]` is a [boolean setting](./define-settings.md#boolean) -and can be defined as [CLI argument](./define-settings.md#cli), [converter -comment](./define-settings.md#converter) or [method +and can be defined as [CLI argument](./define-settings.md#cli), [conversion +comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method). This setting is [inheritable](./define-settings.md#inheritance). diff --git a/docs/reference/struct.md b/docs/reference/struct.md index 712fdc79..dcd7bd50 100644 --- a/docs/reference/struct.md +++ b/docs/reference/struct.md @@ -3,7 +3,7 @@ ## struct:comment COMMENT `struct:comment COMMENT` can be defined as [CLI argument](./define-settings.md#cli) -or [converter comment](./define-settings.md#converter). +or [conversion comment](./define-settings.md#conversion). `struct:comment` instructs goverter to add a comment line to the generated struct. It can be configured multiple times to add multiline comments. Prefix diff --git a/docs/reference/useUnderlyingTypeMethods.md b/docs/reference/useUnderlyingTypeMethods.md index 7a6eb842..a870f47f 100644 --- a/docs/reference/useUnderlyingTypeMethods.md +++ b/docs/reference/useUnderlyingTypeMethods.md @@ -3,7 +3,7 @@ `useUnderlyingTypeMethods [yes,no]` is a [boolean setting](./define-settings.md#boolean) and can be defined as [CLI argument](./define-settings.md#cli), -[converter comment](./define-settings.md#converter) or +[conversion comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method). This setting is [inheritable](./define-settings.md#inheritance). diff --git a/docs/reference/useZeroValueOnPointerInconsistency.md b/docs/reference/useZeroValueOnPointerInconsistency.md index a262778a..3d000a48 100644 --- a/docs/reference/useZeroValueOnPointerInconsistency.md +++ b/docs/reference/useZeroValueOnPointerInconsistency.md @@ -3,7 +3,7 @@ `useZeroValueOnPointerInconsistency [yes,no]` is a [boolean setting](./define-settings.md#boolean) and can be defined as [CLI argument](./define-settings.md#cli), -[converter comment](./define-settings.md#converter) or +[conversion comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method). This setting is [inheritable](./define-settings.md#inheritance). diff --git a/docs/reference/variables.md b/docs/reference/variables.md new file mode 100644 index 00000000..d59971fe --- /dev/null +++ b/docs/reference/variables.md @@ -0,0 +1,16 @@ +# Setting: variables + +`variables` accepts no arguments and can be defined as [conversion +comment](./define-settings.md#conversion). + +`variables` instructs goverter to generate an implementation for the given +variables. You can have multiple variables blocks in one package. + +See [output](./output.md) to control the output location/package of the +generated converter. + +::: code-group +<<< @../../example/format/assignvariables/input.go +<<< @../../example/format/common/common.go +<<< @../../example/format/assignvariables/input.gen.go +::: diff --git a/docs/reference/wrapErrors.md b/docs/reference/wrapErrors.md index cdeaefd1..1bb34fc0 100644 --- a/docs/reference/wrapErrors.md +++ b/docs/reference/wrapErrors.md @@ -3,7 +3,7 @@ `wrapErrors [yes,no]` is a [boolean setting](./define-settings.md#boolean) and can be defined as [CLI argument](./define-settings.md#cli), -[converter comment](./define-settings.md#converter) or +[conversion comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method). This setting is [inheritable](./define-settings.md#inheritance). diff --git a/docs/reference/wrapErrorsUsing.md b/docs/reference/wrapErrorsUsing.md index 5838de09..00b1cbff 100644 --- a/docs/reference/wrapErrorsUsing.md +++ b/docs/reference/wrapErrorsUsing.md @@ -2,7 +2,7 @@ `wrapErrorsUsing [PACKAGE]` is a [boolean setting](./define-settings.md#boolean) and can be defined as [CLI argument](./define-settings.md#cli), -[converter comment](./define-settings.md#converter) or +[conversion comment](./define-settings.md#conversion) or [method comment](./define-settings.md#method). This setting is [inheritable](./define-settings.md#inheritance). diff --git a/example/format/assignvariables/input.gen.go b/example/format/assignvariables/input.gen.go new file mode 100644 index 00000000..9b69f6f4 --- /dev/null +++ b/example/format/assignvariables/input.gen.go @@ -0,0 +1,48 @@ +// Code generated by github.com/jmattheis/goverter, DO NOT EDIT. +//go:build !goverter + +package house + +import common "github.com/jmattheis/goverter/example/format/common" + +func init() { + ConvertApartment = func(source common.DBApartment) common.APIApartment { + var commonAPIApartment common.APIApartment + commonAPIApartment.Position = source.Position + commonAPIApartment.Owner = ConvertPerson(source.Owner) + commonAPIApartment.OwnerName = source.Owner.Name + if source.CoResident != nil { + commonAPIApartment.CoResident = make([]common.APIPerson, len(source.CoResident)) + for i := 0; i < len(source.CoResident); i++ { + commonAPIApartment.CoResident[i] = ConvertPerson(source.CoResident[i]) + } + } + return commonAPIApartment + } + ConvertHouse = func(source common.DBHouse) common.APIHouse { + var commonAPIHouse common.APIHouse + commonAPIHouse.Address = source.Address + commonAPIHouse.Apartments = ConvertToApartmentMap(source.Apartments) + return commonAPIHouse + } + ConvertPerson = func(source common.DBPerson) common.APIPerson { + var commonAPIPerson common.APIPerson + commonAPIPerson.ID = source.ID + commonAPIPerson.MiddleName = SQLStringToPString(source.MiddleName) + pString := source.Name + commonAPIPerson.FirstName = &pString + if source.Friends != nil { + commonAPIPerson.Friends = make([]common.APIPerson, len(source.Friends)) + for i := 0; i < len(source.Friends); i++ { + commonAPIPerson.Friends[i] = ConvertPerson(source.Friends[i]) + } + } + commonAPIPerson.Info = commonInfoToCommonInfo(source.Info) + return commonAPIPerson + } +} +func commonInfoToCommonInfo(source common.Info) common.Info { + var commonInfo common.Info + commonInfo.Birthplace = source.Birthplace + return commonInfo +} diff --git a/example/format/assignvariables/input.go b/example/format/assignvariables/input.go new file mode 100644 index 00000000..ce3bf0dd --- /dev/null +++ b/example/format/assignvariables/input.go @@ -0,0 +1,35 @@ +package house + +import ( + "database/sql" + + "github.com/jmattheis/goverter/example/format/common" +) + +// goverter:variables +// goverter:output:format assign-variable +// goverter:extend SQLStringToPString +// goverter:extend ConvertToApartmentMap +var ( + ConvertHouse func(source common.DBHouse) common.APIHouse + // goverter:map Name FirstName + // goverter:ignore Age + ConvertPerson func(source common.DBPerson) common.APIPerson + // goverter:map Owner.Name OwnerName + ConvertApartment func(source common.DBApartment) common.APIApartment +) + +func SQLStringToPString(value sql.NullString) *string { + if value.Valid { + return &value.String + } + return nil +} + +func ConvertToApartmentMap(source []common.DBApartment) map[uint]common.APIApartment { + m := make(map[uint]common.APIApartment) + for _, apartment := range source { + m[apartment.Position] = ConvertApartment(apartment) // !! this is not supported in some formats + } + return m +} diff --git a/example/format/common/common.go b/example/format/common/common.go new file mode 100644 index 00000000..25fb304a --- /dev/null +++ b/example/format/common/common.go @@ -0,0 +1,47 @@ +package common + +import "database/sql" + +type DBHouse struct { + Address string + Apartments []DBApartment +} + +type DBApartment struct { + Position uint + Owner DBPerson + CoResident []DBPerson +} + +type DBPerson struct { + ID int + Name string + MiddleName sql.NullString + Friends []DBPerson + Info Info +} + +type APIHouse struct { + Address string + Apartments map[uint]APIApartment +} + +type APIApartment struct { + Position uint + Owner APIPerson + OwnerName string + CoResident []APIPerson +} + +type APIPerson struct { + ID int + MiddleName *string + FirstName *string + Friends []APIPerson + Info Info + Age int +} + +type Info struct { + Birthplace string +} diff --git a/example/format/interfacefunction/generated/generated.go b/example/format/interfacefunction/generated/generated.go new file mode 100644 index 00000000..375e50bc --- /dev/null +++ b/example/format/interfacefunction/generated/generated.go @@ -0,0 +1,43 @@ +// Code generated by github.com/jmattheis/goverter, DO NOT EDIT. +//go:build !goverter + +package generated + +import ( + common "github.com/jmattheis/goverter/example/format/common" + interfacefunction "github.com/jmattheis/goverter/example/format/interfacefunction" +) + +func ConvertApartment(source common.DBApartment) common.APIApartment { + var commonAPIApartment common.APIApartment + commonAPIApartment.Position = source.Position + commonAPIApartment.Owner = ConvertPerson(source.Owner) + commonAPIApartment.OwnerName = source.Owner.Name + if source.CoResident != nil { + commonAPIApartment.CoResident = make([]common.APIPerson, len(source.CoResident)) + for i := 0; i < len(source.CoResident); i++ { + commonAPIApartment.CoResident[i] = ConvertPerson(source.CoResident[i]) + } + } + return commonAPIApartment +} +func ConvertPerson(source common.DBPerson) common.APIPerson { + var commonAPIPerson common.APIPerson + commonAPIPerson.ID = source.ID + commonAPIPerson.MiddleName = interfacefunction.SQLStringToPString(source.MiddleName) + pString := source.Name + commonAPIPerson.FirstName = &pString + if source.Friends != nil { + commonAPIPerson.Friends = make([]common.APIPerson, len(source.Friends)) + for i := 0; i < len(source.Friends); i++ { + commonAPIPerson.Friends[i] = ConvertPerson(source.Friends[i]) + } + } + commonAPIPerson.Info = commonInfoToCommonInfo(source.Info) + return commonAPIPerson +} +func commonInfoToCommonInfo(source common.Info) common.Info { + var commonInfo common.Info + commonInfo.Birthplace = source.Birthplace + return commonInfo +} diff --git a/example/format/interfacefunction/input.go b/example/format/interfacefunction/input.go new file mode 100644 index 00000000..151e1db1 --- /dev/null +++ b/example/format/interfacefunction/input.go @@ -0,0 +1,25 @@ +package house + +import ( + "database/sql" + + "github.com/jmattheis/goverter/example/format/common" +) + +// goverter:converter +// goverter:output:format function +// goverter:extend SQLStringToPString +type Converter interface { + // goverter:map Owner.Name OwnerName + ConvertApartment(source common.DBApartment) common.APIApartment + // goverter:map Name FirstName + // goverter:ignore Age + ConvertPerson(source common.DBPerson) common.APIPerson +} + +func SQLStringToPString(value sql.NullString) *string { + if value.Valid { + return &value.String + } + return nil +} diff --git a/example/format/interfacetostruct/generated/generated.go b/example/format/interfacetostruct/generated/generated.go new file mode 100644 index 00000000..d30b27e9 --- /dev/null +++ b/example/format/interfacetostruct/generated/generated.go @@ -0,0 +1,51 @@ +// Code generated by github.com/jmattheis/goverter, DO NOT EDIT. +//go:build !goverter + +package generated + +import ( + common "github.com/jmattheis/goverter/example/format/common" + interfacetostruct "github.com/jmattheis/goverter/example/format/interfacetostruct" +) + +type ConverterImpl struct{} + +func (c *ConverterImpl) ConvertApartment(source common.DBApartment) common.APIApartment { + var commonAPIApartment common.APIApartment + commonAPIApartment.Position = source.Position + commonAPIApartment.Owner = c.ConvertPerson(source.Owner) + commonAPIApartment.OwnerName = source.Owner.Name + if source.CoResident != nil { + commonAPIApartment.CoResident = make([]common.APIPerson, len(source.CoResident)) + for i := 0; i < len(source.CoResident); i++ { + commonAPIApartment.CoResident[i] = c.ConvertPerson(source.CoResident[i]) + } + } + return commonAPIApartment +} +func (c *ConverterImpl) ConvertHouse(source common.DBHouse) common.APIHouse { + var commonAPIHouse common.APIHouse + commonAPIHouse.Address = source.Address + commonAPIHouse.Apartments = interfacetostruct.ConvertToApartmentMap(c, source.Apartments) + return commonAPIHouse +} +func (c *ConverterImpl) ConvertPerson(source common.DBPerson) common.APIPerson { + var commonAPIPerson common.APIPerson + commonAPIPerson.ID = source.ID + commonAPIPerson.MiddleName = interfacetostruct.SQLStringToPString(source.MiddleName) + pString := source.Name + commonAPIPerson.FirstName = &pString + if source.Friends != nil { + commonAPIPerson.Friends = make([]common.APIPerson, len(source.Friends)) + for i := 0; i < len(source.Friends); i++ { + commonAPIPerson.Friends[i] = c.ConvertPerson(source.Friends[i]) + } + } + commonAPIPerson.Info = c.commonInfoToCommonInfo(source.Info) + return commonAPIPerson +} +func (c *ConverterImpl) commonInfoToCommonInfo(source common.Info) common.Info { + var commonInfo common.Info + commonInfo.Birthplace = source.Birthplace + return commonInfo +} diff --git a/example/format/interfacetostruct/input.go b/example/format/interfacetostruct/input.go new file mode 100644 index 00000000..dd1540d7 --- /dev/null +++ b/example/format/interfacetostruct/input.go @@ -0,0 +1,35 @@ +package house + +import ( + "database/sql" + + "github.com/jmattheis/goverter/example/format/common" +) + +// goverter:converter +// goverter:output:format struct +// goverter:extend SQLStringToPString +// goverter:extend ConvertToApartmentMap +type Converter interface { + ConvertHouse(source common.DBHouse) common.APIHouse + // goverter:map Owner.Name OwnerName + ConvertApartment(source common.DBApartment) common.APIApartment + // goverter:map Name FirstName + // goverter:ignore Age + ConvertPerson(source common.DBPerson) common.APIPerson +} + +func SQLStringToPString(value sql.NullString) *string { + if value.Valid { + return &value.String + } + return nil +} + +func ConvertToApartmentMap(c Converter, source []common.DBApartment) map[uint]common.APIApartment { + m := make(map[uint]common.APIApartment) + for _, apartment := range source { + m[apartment.Position] = c.ConvertApartment(apartment) // !! this is not supported in some formats + } + return m +}