![](https://private-user-images.githubusercontent.com/35725554/327168970-52078fd7-c05c-42b7-b181-c41be575ea82.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjQxNzQzMDYsIm5iZiI6MTcyNDE3NDAwNiwicGF0aCI6Ii8zNTcyNTU1NC8zMjcxNjg5NzAtNTIwNzhmZDctYzA1Yy00MmI3LWIxODEtYzQxYmU1NzVlYTgyLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MjAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODIwVDE3MTMyNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTI1ZTViZGM0OTY1NDlhNjMzZjM1OWFjNjFjZDI5YmJhYjRiZTc4ZmQ5M2IyNmMzNzg5ODg1YmQyOWVhYTQ3MzgmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.BfcphyOxuUM1fT8-zubSCdkrn_cectBYuGeizxyyRTs)
Art generated by copilot DALL-E 3
KM (Kanna Mapper) helps you auto-generate mapping between types and supports configuration using a TOML configuration file.
Unlike other mappers, KM leverages code generation and does not use reflection, which means there are no errors during runtime, and offers much better performance than other solutions that use reflection.
✔️ Doesn't use reflection
✔️ Leverage fails early so nothing will generate in case of errors
✔️ Configurable
✔️ Different generation styles
✔️ Concurrent generation
go install github.com/khatibomar/km@latest
Flags:
-config string
mapping configuration file (default "km.toml")
-debug
log result instead of writing to files
-routines int
number of routines (default 1)
First, create km.toml
file, this file will hold the mapping configuration, and it's considered the root directory of the generator, which means if we have folder folder/sub_folder/km.toml
the sub_folder
is the root directory for mapper and you can't map things in folder
[settings]
style="value"
module="github.com/khatibomar/km/"
path_from_module="testdata"
[[mappings]]
[mappings.source]
name = "Car"
path = "./car/car.go"
[mappings.destination]
name = "CarDTO"
path = "./dto/car_dto.go"
ignore = ["Owner"]
[mappings.destination.map]
FactoryModel = "Model"
[[mappings]]
[mappings.source]
name = "User"
path = "./user/user.go"
[mappings.destination]
name = "UserDTO"
path = "./dto/user_dto.go"
we are going to use testdata
folder
So in settings
we set PathFromModule
to testdata
this setting is nothing but how we can access the config file starting from the root of the project itself, not the config.
This is the tree of the testdata
before running generation
94 B ┌─ car.go
94 B ┌─ car
163 B │ ┌─ user.go
163 B ├─ user
103 B │ ┌─ car_dto.go
157 B │ ├─ user_dto.go
260 B ├─ dto
483 B ├─ km.toml
1000 B testdata
after that run km
which will generate km_gen.go
in the destination path.
tree after generation
94 B ┌─ car.go
94 B ┌─ car
163 B │ ┌─ user.go
163 B ├─ user
483 B ├─ km.toml
103 B │ ┌─ car_dto.go
157 B │ ├─ user_dto.go
363 B │ ├─ km_gen.go
623 B ├─ dto
1363 B testdata
we can see that km_gen.go
is generated in the destination path.
package dto
import (
"github.com/khatibomar/km/testdata/car"
"github.com/khatibomar/km/testdata/user"
)
func (dest CarDTO) FromCar(src car.Car) CarDTO {
dest.FactoryModel = src.Model
dest.Color = src.Color
dest.Speed = src.Speed
return dest
}
func (dest UserDTO) FromUser(src user.User) UserDTO {
dest.Name = src.Name
dest.Age = src.Age
dest.MetaData = src.MetaData
return dest
}
setting | Description |
---|---|
style | specify mapping style, default style is value |
module | is the module of the repository |
path_from_module | how we can access the config file starting from the root of the project itself |
setting | Description |
---|---|
name | name of the type |
path | where can locate the type, root starts from config file |
setting | Description |
---|---|
name | name of the type |
path | where can locate the type, root starts from config file |
ignore | fields to ignore during mapping |
map | map from destination to source field |
Approach | Description | Use Case |
---|---|---|
pointer | Modifies the existing instance of the destination directly. | When you want to modify the existing instance of destination without creating a new one. |
value | Creates a copy of the destination struct, modifies the copy, and returns it, leaving the original intact. | When you want to keep the original destination struct intact and produce a modified copy. |
standalone | A standalone function that takes destination as a parameter and returns a modified version of it. | When you want to keep the modification logic separate from the struct's methods or need more flexibility in how you apply the modification. |
func (dest CarDTO) FromCar(src car.Car) CarDTO {
dest.FactoryModel = src.Model
dest.Color = src.Color
dest.Speed = src.Speed
return dest
}
func (dest *CarDTO) FromCar(src car.Car) {
dest.FactoryModel = src.Model
dest.Color = src.Color
dest.Speed = src.Speed
}
func CarDTOFromCar(dest CarDTO, src car.Car) CarDTO {
dest.FactoryModel = src.Model
dest.Color = src.Color
dest.Speed = src.Speed
return dest
}
- Refactor code
- Adding more tests
- Support for map <-> struct
- Better documentation
- Handle conversion between types
- Recursive mapping for complex fields