diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 00da618..18c937b 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 1ea6be1..5144154 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 0000000..8561aa1 --- /dev/null +++ b/docs/guide/format.md @@ -0,0 +1,78 @@ +# 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. All examples have this in +common. + +[[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/define-settings.md b/docs/reference/define-settings.md index 7ed6b3a..46fe216 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 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 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 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 519159f..4e6484e 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. diff --git a/docs/reference/extend.md b/docs/reference/extend.md index c6fd141..058a999 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/matchIgnoreCase.md b/docs/reference/matchIgnoreCase.md index d0e1fd3..7a9062d 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 50efad4..b70644f 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 f26cc94..27a8d17 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 e500886..f4e344f 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -4,10 +4,10 @@ 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) @@ -17,8 +17,9 @@ These settings can only be defined as [CLI argument](./define-settings.md#cli) o - [`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) +- [`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 +39,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/struct.md b/docs/reference/struct.md index 712fdc7..dcd7bd5 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 7a6eb84..a870f47 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 a262778..3d000a4 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 0000000..f86de96 --- /dev/null +++ b/docs/reference/variables.md @@ -0,0 +1,16 @@ +# Setting: converter + +`variables` accepts no arguments and can be defined as [converter +comment](./define-settings.md#converter). + +`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 cdeaefd..1bb34fc 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 5838de0..00b1cbf 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 0000000..9b69f6f --- /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 0000000..ce3bf0d --- /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 0000000..25fb304 --- /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 0000000..375e50b --- /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 0000000..151e1db --- /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 0000000..d30b27e --- /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 0000000..dd1540d --- /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 +}