Documentation
¶
Index ¶
- Constants
- Variables
- func ClearAllRegisteredConverters()
- func EnableLookUpCache()
- func NewNoopLogger() *slog.Logger
- func PrintRegisteredConverters(logger *slog.Logger)
- func RegisterAllBuiltinConverters()
- func RegisterBuiltinConverters(config BuiltInConverterConfig)
- func RegisterConverter(converter Converter)
- func UseTestVersion()
- func Version() string
- type BuiltInConverterConfig
- type Config
- type ConfigMapper
- func (m *ConfigMapper) MapBuiltInConverterConfig(in mapper.BuiltInConverter) BuiltInConverterConfig
- func (m *ConfigMapper) MapConverterFunctions(list *[]string) []ConvertFunctionConfig
- func (m *ConfigMapper) MapLibraryConverterConfig(in mapper.BuiltInConverter) LibraryConverterConfig
- func (m *ConfigMapper) MapPackagesConfig(packages map[string]pkl.Package, all pkl.All) (map[string][]PackageConfig, error)
- type ConvertFunctionConfig
- type Converter
- type ConverterContext
- type ConverterInfo
- type ConverterTestCase
- type DecoratorMode
- type Descriptor
- type FieldConfig
- type FieldInterceptor
- type FieldInterceptorProvider
- type FileManager
- type FuncInfo
- type GeneratedTypeOrchestrator
- type Generator
- type GoldenTestCase
- type GoldenTestCaseFromTestData
- type GoldenTestCaseRunOptions
- type GoldenTestCaseRunOptionsFunc
- type LibraryConverterConfig
- type LookupContext
- type MarkdownTestCase
- type Mode
- type NameMatch
- type OptionFunc
- type Options
- type Output
- type PackageConfig
- type Parser
- type Pointer
- type StandardConversionOrchestrator
- type StructConfig
- type StructFieldInfo
- type StructInfo
- type Symbol
- type SymbolMetadata
- type TypeInfo
Constants ¶
const BinaryName = "github.com/toniphan21/go-mapper-gen"
const CommentWidth = 80
Variables ¶
var BuiltinConverters = builtinConverters{
IdenticalType: &identicalTypeConverter{},
Slice: &sliceConverter{},
TypeToPointer: &typeToPointerConverter{},
PointerToType: &pointerToTypeConverter{},
Numeric: &numericConverter{},
Functions: &functionsConverter{},
FunctionsStrict: &strictFunctionsConverter{},
}
var BuiltinFieldInterceptor = builtinFieldInterceptor{ NilIfZero: &nilIfZeroFieldInterceptor{}, UseFunction: func(symbol string) FieldInterceptor { return &useFunctionFieldInterceptor{ symbol: symbol, } }, UseMethod: func(variableSymbol string, methodName string) FieldInterceptor { return &useFunctionFieldInterceptor{ symbol: variableSymbol, method: methodName, } }, }
var Default = defaultCfValue{ Output: Output{ PkgName: Placeholder.CurrentPackageName, FileName: "gen_mapper.go", TestFileName: "gen_mapper_test.go", }, Mode: ModeTypes, InterfaceName: "iMapper", ImplementationName: "iMapperImpl", ConstructorName: "new_iMapper", SourceToTargetFuncName: "To{TargetStructName}", SourceFromTargetFuncName: "From{TargetStructName}", DecoratorMode: DecoratorModeAdaptive, DecoratorInterfaceName: "iMapperDecorator", DecoratorNoOpName: "iMapperDecoratorNoOp", DecorateFuncName: "decorate{FunctionName}", TargetPkgPath: Placeholder.CurrentPackage, }
var GeneratorUtil = &genUtil{}
var LookUpTotalHits uint64
var Placeholder = cfPlaceHolder{
TargetStructName: "{TargetStructName}",
SourceStructName: "{SourceStructName}",
CurrentPackage: "{CurrentPackage}",
CurrentPackageName: "{CurrentPackageName}",
FunctionName: "{FunctionName}",
}
var Test = &testHelper{
goldenTest: &goldenTest{},
converterTest: &converterTest{},
bddTest: &bddTest{},
}
var TypeUtil = &typeUtil{}
Functions ¶
func ClearAllRegisteredConverters ¶
func ClearAllRegisteredConverters()
func EnableLookUpCache ¶
func EnableLookUpCache()
func NewNoopLogger ¶
func RegisterAllBuiltinConverters ¶
func RegisterAllBuiltinConverters()
func RegisterBuiltinConverters ¶
func RegisterBuiltinConverters(config BuiltInConverterConfig)
func RegisterConverter ¶
func RegisterConverter(converter Converter)
RegisterConverter adds a new Converter to the global converter registry.
Converters are selected by the mapper generator based on:
- Whether they report true from CanConvert(...)
- Their assigned priority (use converter { priority = List(...) } config)
The priority determines via converter { priority } configuration. The ordering between converters that can handle the same source and target types.
Registering a converter does not trigger any generation work. It merely appends the converter to the internal registry so that the code generator can discover it later.
Converters should be stateless and safe for repeated reuse. The registry is typically read during generator initialization or when resolving the appropriate converter for a given assignment.
Example:
RegisterConverter(&StringToIntConverter{})
Passing the same converter instance multiple times is allowed but generally discouraged unless intentional.
func UseTestVersion ¶
func UseTestVersion()
Types ¶
type BuiltInConverterConfig ¶
type BuiltInConverterConfig struct {
UseIdentical bool
UseSlice bool
UseTypeToPointer bool
UsePointerToType bool
UseNumeric bool
UseFunctions bool
UseFunctionsStrict bool
}
func (*BuiltInConverterConfig) EnableAll ¶
func (c *BuiltInConverterConfig) EnableAll()
type Config ¶
type Config struct {
BuiltInConverters BuiltInConverterConfig
LibraryConverters LibraryConverterConfig
ConverterFunctions []ConvertFunctionConfig
ConverterPriorities []string
Packages map[string][]PackageConfig
}
func MakeConfig ¶ added in v0.4.0
func MakeConfig(cfg pkl.Config, provider FieldInterceptorProvider) (*Config, error)
func ParseConfig ¶
func ParseConfig(path string, provider FieldInterceptorProvider) (*Config, error)
type ConfigMapper ¶ added in v0.7.0
type ConfigMapper struct {
Provider FieldInterceptorProvider
}
func (*ConfigMapper) MapBuiltInConverterConfig ¶ added in v0.7.0
func (m *ConfigMapper) MapBuiltInConverterConfig(in mapper.BuiltInConverter) BuiltInConverterConfig
func (*ConfigMapper) MapConverterFunctions ¶ added in v0.7.0
func (m *ConfigMapper) MapConverterFunctions(list *[]string) []ConvertFunctionConfig
func (*ConfigMapper) MapLibraryConverterConfig ¶ added in v0.7.0
func (m *ConfigMapper) MapLibraryConverterConfig(in mapper.BuiltInConverter) LibraryConverterConfig
func (*ConfigMapper) MapPackagesConfig ¶ added in v0.7.0
func (m *ConfigMapper) MapPackagesConfig(packages map[string]pkl.Package, all pkl.All) (map[string][]PackageConfig, error)
type ConvertFunctionConfig ¶
type Converter ¶
type Converter interface {
// Init is called once before code generation starts.
// It allows the converter to initialize internal state, validate assumptions,
// or prepare data required during code generation. Init must not emit code.
// If no initialization is required, the implementation may be a no-op.
Init(parser Parser, config Config, logger *slog.Logger)
// Info returns metadata describing the converter.
Info() ConverterInfo
// CanConvert reports whether this converter can convert a value of
// sourceType into a value of targetType. Implementations typically use
// TypeUtil to perform type analysis.
//
// CanConvert must be pure and must not modify file state.
CanConvert(ctx LookupContext, targetType, sourceType types.Type) bool
// ConvertField emits the assignment code that writes the converted value from
// source into target. Returning nil suppresses emission.
//
// GeneratorUtil may be used to build complex expression trees.
ConvertField(ctx ConverterContext, target, source Symbol) jen.Code
}
Converter defines the contract for converting a source value or field into a target value or field during code generation.
type ConverterContext ¶
type ConverterContext interface {
context.Context
LookupContext
// JenFile returns the current jennifer file used for code generation.
// Converters may append generated code to this file.
JenFile() *jen.File
// Parser returns the type parser used to inspect and analyze Go types
// during conversion.
Parser() Parser
// NextVarName returns a unique variable name for use in generated code.
// It guarantees no name collisions within the current generation scope.
NextVarName() string
// Run executes runner if the context has not been cancelled.
// If the context is done, Run returns nil and runner is not executed.
// This allows converters to respect generator-defined timeouts without
// explicitly checking ctx.Done().
Run(converter Converter, runner func() jen.Code) jen.Code
// EmitTraceComments indicates whether the converter should emit trace comments
// for debugging or inspection purposes. It returns false by default.
EmitTraceComments() bool
}
ConverterContext provides shared capabilities and state for converters during code generation. It embeds context.Context to support cancellation and timeouts defined by the generator.
type ConverterInfo ¶
ConverterInfo describes metadata about a Converter. It is primarily used for debugging, logging, and trace comments in generated code.
type ConverterTestCase ¶
type ConverterTestCase struct {
Name string
AdditionalCode []string
Config *Config
Imports map[string]string
GoModGoVersion string
GoModRequires map[string]string
GoModModule string
TargetType string
SourceType string
EmitTraceComments bool
TargetSymbolWithoutFieldName bool
SourceSymbolWithoutFieldName bool
TargetSymbolMetadata SymbolMetadata
SourceSymbolMetadata SymbolMetadata
ExpectedCanConvert bool
ExpectedImports []string
ExpectedCode []string
PrintSetUp bool
}
type DecoratorMode ¶
type DecoratorMode int
const ( DecoratorModeAdaptive DecoratorMode = iota DecoratorModeNever DecoratorModeAlways )
type Descriptor ¶ added in v0.5.0
type Descriptor struct {
// contains filtered or unexported fields
}
func (*Descriptor) FieldIndex ¶ added in v0.5.0
func (d *Descriptor) FieldIndex() int
func (*Descriptor) FieldName ¶ added in v0.5.0
func (d *Descriptor) FieldName() string
func (*Descriptor) FieldType ¶ added in v0.5.0
func (d *Descriptor) FieldType() types.Type
func (*Descriptor) StructFields ¶ added in v0.5.0
func (d *Descriptor) StructFields() map[string]StructFieldInfo
func (*Descriptor) StructType ¶ added in v0.5.0
func (d *Descriptor) StructType() types.Type
type FieldConfig ¶
func (FieldConfig) Flip ¶
func (c FieldConfig) Flip() FieldConfig
type FieldInterceptor ¶ added in v0.5.0
type FieldInterceptor interface {
GetType() string
GetOptions() map[string]any
Init(parser Parser, logger *slog.Logger)
InterceptCanConvert(converter Converter, ctx LookupContext, targetType, sourceType types.Type) bool
InterceptConvertField(converter Converter, ctx ConverterContext, target, source Symbol) jen.Code
}
type FieldInterceptorProvider ¶ added in v0.5.0
type FieldInterceptorProvider interface {
MakeFieldInterceptor(typ string, options map[string]any) FieldInterceptor
}
func DefaultFieldInterceptorProvider ¶ added in v0.5.0
func DefaultFieldInterceptorProvider() FieldInterceptorProvider
type FileManager ¶
type FileManager interface {
MakeJenFile(parser Parser, currentPkg *packages.Package, config PackageConfig) *jen.File
JenFiles() map[string]*jen.File
}
func DefaultFileManager ¶
func DefaultFileManager() FileManager
type GeneratedTypeOrchestrator ¶
type GeneratedTypeOrchestrator struct {
Generated TypeInfo
Target TypeInfo
GeneratedToTarget func(ctx ConverterContext, target, source Symbol) jen.Code
GeneratedToTargetToOther func(ctx ConverterContext, target, source Symbol, targetToOther Converter) jen.Code
TargetToGenerated func(ctx ConverterContext, target, source Symbol) jen.Code
OtherToTargetToGenerated func(ctx ConverterContext, target, source Symbol, otherToTarget Converter) jen.Code
// contains filtered or unexported fields
}
GeneratedTypeOrchestrator is simpler version of StandardConversionOrchestrator which one side is fixed. It provides 4 ways of conversion
For example: Given you want to convert A -> B, A is a generated type which usually provided via a generated tool such as grpc or sqlc. GeneratedTypeOrchestrator provides 4 routing ways:
- GeneratedToTarget A -> B
- GeneratedToTargetToOther A -> B -> T if B -> T possible
- TargetToGenerated B -> A
- OtherToTargetToGenerated T -> B -> A if T -> B possible
Usage: In your Converter.Init():
```
// similar to StandardConversionOrchestrator, you can simply set nil if you don't want to match the route
c.orchestrator = gen.GeneratedTypeOrchestrator{
Generated: Generated type, aka. A's type info,
Target: B's type info,
GeneratedToTarget: func to emit code when match route 1) A -> B
GeneratedToTargetToOther: func to emit code when match route 2) A -> B -> T
TargetToGenerated: func to emit code when match route 3) B -> A
OtherToTargetToGenerated: func to emit code when match route 4) T -> B -> A
}
``` Then in your Converter.CanConvert() ```
return c.orchestrator.CanConvert(c, ctx, targetType, sourceType)
``` and in your Converter.ConvertField()
```
return ctx.Run(c, opts, func() jen.Code {
return c.orchestrator.PerformConvert(c, ctx, target, source, opts)
})
``` The GeneratedTypeOrchestrator actually just a wrapper of StandardConversionOrchestrator.
func (*GeneratedTypeOrchestrator) CanConvert ¶
func (o *GeneratedTypeOrchestrator) CanConvert(c Converter, ctx LookupContext, targetType, sourceType types.Type) bool
func (*GeneratedTypeOrchestrator) PerformConvert ¶
func (o *GeneratedTypeOrchestrator) PerformConvert(c Converter, ctx ConverterContext, target, source Symbol) jen.Code
type Generator ¶
type Generator interface {
Generate(currentPkg *packages.Package, configs []PackageConfig) error
}
type GoldenTestCase ¶
type GoldenTestCaseFromTestData ¶
type GoldenTestCaseFromTestData struct {
Name string
PkgPath string
GoModGoVersion string
GoModRequires map[string]string
GoModModule string
GoSumFileContent []byte
SourceFiles map[string]string
PklFile string
GoldenFile string
OutputFileName string
PrintSetup bool
PrintActual bool
PrintDiff bool
}
func (*GoldenTestCaseFromTestData) ToGoldenTestCase ¶
func (g *GoldenTestCaseFromTestData) ToGoldenTestCase() GoldenTestCase
type GoldenTestCaseRunOptions ¶
type GoldenTestCaseRunOptions struct {
SetupConverter func()
}
type GoldenTestCaseRunOptionsFunc ¶
type GoldenTestCaseRunOptionsFunc func(opts *GoldenTestCaseRunOptions)
func TestWithSetupConverter ¶
func TestWithSetupConverter(fn func()) GoldenTestCaseRunOptionsFunc
type LibraryConverterConfig ¶
type LookupContext ¶
type LookupContext interface {
// LookUp searches the global converter registry for a converter that
// can convert a value of sourceType to targetType, excluding the provided
// currentConverter (if non-nil).
//
// This helper is intended for converter implementations that need to
// delegate or reuse existing conversion rules. A common use-case is a
// SliceConverter that converts []T -> []V by looking up a converter for
// T -> V and then generating per-element conversion code.
//
// Selection rules (implementation contract):
// 1. The registry is scanned for converters c where c.CanConvert(targetType, sourceType)
// returns true.
// 2. The currentConverter parameter is excluded from consideration to avoid
// trivial self-selection (if currentConverter == nil, no exclusion occurs).
// 3. From the remaining candidates, the converter with the highest priority
// (your package's ordering rule: lower numeric value = higher priority)
// is chosen. If multiple converters share the same priority, the selection
// must be deterministic (for example: registration order or stable sorting).
//
// Return value:
// - (Converter, true) if a matching converter was found.
// - (nil, false) if no converter in the registry can perform the conversion.
LookUp(current Converter, targetType, sourceType types.Type) (Converter, error)
TargetDescriptor() *Descriptor
SourceDescriptor() *Descriptor
// Logger returns a slog handler that can be used for logging during
// code generation.
Logger() *slog.Logger
}
type MarkdownTestCase ¶
type MarkdownTestCase struct {
Name string
Content string
Headers []string
SourceFiles map[string][]byte
GoldenFiles map[string][]byte
GoModFileContent []byte
GoSumFileContent []byte
PklDevFileContent []byte
}
func (*MarkdownTestCase) IsNameMatch ¶
func (tc *MarkdownTestCase) IsNameMatch(term string) bool
func (*MarkdownTestCase) ToGoldenTestCase ¶
func (tc *MarkdownTestCase) ToGoldenTestCase() GoldenTestCase
type OptionFunc ¶
type OptionFunc func(*Options)
func WithFileManager ¶
func WithFileManager(fileManager FileManager) OptionFunc
func WithLogger ¶
func WithLogger(logger *slog.Logger) OptionFunc
type PackageConfig ¶
type Parser ¶
type Parser interface {
SourceDir() string
SourcePackages() []*packages.Package
FindStruct(pkgPath string, name string) (StructInfo, bool)
FindFunction(pkgPath string, name string) (FuncInfo, bool)
FindVariableMethods(pkgPath string, name string) []FuncInfo
}
func DefaultParser ¶
type StandardConversionOrchestrator ¶
type StandardConversionOrchestrator struct {
Source TypeInfo
Target TypeInfo
SourceToTarget func(ctx ConverterContext, target, source Symbol) jen.Code
OtherToSourceToTarget func(ctx ConverterContext, target, source Symbol, otherToSource Converter) jen.Code
SourceToTargetToOther func(ctx ConverterContext, target, source Symbol, targetToOther Converter) jen.Code
OtherToSourceToTargetToOther func(ctx ConverterContext, target, source Symbol, otherToSource, targetToOther Converter) jen.Code
TargetToSource func(ctx ConverterContext, target, source Symbol) jen.Code
OtherToTargetToSource func(ctx ConverterContext, target, source Symbol, otherToTarget Converter) jen.Code
TargetToSourceToOther func(ctx ConverterContext, target, source Symbol, sourceToOther Converter) jen.Code
OtherToTargetToSourceToOther func(ctx ConverterContext, target, source Symbol, otherToTarget, sourceToOther Converter) jen.Code
}
StandardConversionOrchestrator provide a framework to a converter want to convert A -> B. it provides 8 routes check and invoke function to emit code.
For example: Given you want to convert A -> B, there are 8 standard routes:
- SourceToTarget A -> B
- OtherToSourceToTarget T -> A -> B if T -> A possible
- SourceToTargetToOther A -> B -> T if B -> T possible
- OtherToSourceToTargetToOther T -> A -> B -> V if T -> A and B -> V possible
flipped cases:
- TargetToSource B -> A
- OtherToTargetToSource T -> B -> A if T -> B possible
- TargetToSourceToOther B -> A -> T if A -> T possible
- OtherToTargetToSourceToOther T -> B -> A -> V if T -> B and A -> V possible
Usage: In your Converter.Init():
```
c.orchestrator = gen.StandardConversionOrchestrator{
Source: A's type info,
Target: B's type info,
SourceToTarget: func to emit code when match route 1) A -> B
OtherToSourceToTarget: nil if you don't want route 2)
SourceToTargetToOther: func to emit code when match route 3) A -> B -> T
OtherToSourceToTargetToOther: nil if you don't want route 4)
TargetToSource: func to emit code when match route 5) B -> A
OtherToTargetToSource: func to emit code when match route 6) T -> B -> A
TargetToSourceToOther: nil if you don't want route 7)
OtherToTargetToSourceToOther: nil if you don't want route 8)
}
``` Then in your Converter.CanConvert() ```
return c.orchestrator.CanConvert(c, ctx, targetType, sourceType)
``` and in your Converter.ConvertField()
```
return ctx.Run(c, opts, func() jen.Code {
return c.orchestrator.PerformConvert(c, ctx, target, source, opts)
})
``` There is a simpler version of StandardConversionOrchestrator called GeneratedTypeOrchestrator which one side is fixed, it means you only have 4 cases.
func (*StandardConversionOrchestrator) CanConvert ¶
func (o *StandardConversionOrchestrator) CanConvert(c Converter, ctx LookupContext, targetType, sourceType types.Type) bool
func (*StandardConversionOrchestrator) PerformConvert ¶
func (o *StandardConversionOrchestrator) PerformConvert(c Converter, ctx ConverterContext, target, source Symbol) jen.Code
type StructConfig ¶
type StructConfig struct {
MapperName string
TargetPkgPath string
TargetStructName string
SourcePkgPath string
SourceStructName string
SourceToTargetFuncName string
SourceFromTargetFuncName string
DecoratorMode DecoratorMode
DecorateFuncName string
Pointer Pointer
Fields FieldConfig
SourceFieldInterceptors map[string]FieldInterceptor
TargetFieldInterceptors map[string]FieldInterceptor
UseGetter bool
GenerateSourceToTarget bool
GenerateSourceFromTarget bool
}
type StructFieldInfo ¶
type StructInfo ¶
type StructInfo struct {
Type types.Type
Fields map[string]StructFieldInfo
}
type Symbol ¶
type Symbol struct {
VarName string
FieldName *string
Type types.Type
Metadata SymbolMetadata
}
Symbol represents a reference to either a variable or a field expression used during code generation.
VarName is the name of the local variable (e.g. "src"). FieldName is optional. If nil, the Symbol refers to the entire variable (e.g. "src"). If non-nil, the Symbol refers to a field of that variable (e.g. "src.Field"). This allows converters to handle both top-level assignments and assignments targeting specific struct fields.
Type is the Go type of the referenced value. Converters use this when determining whether they can perform a conversion.
Examples:
Symbol{VarName: "src", FieldName: nil} // "src"
Symbol{VarName: "dst", FieldName: &("Name")} // "dst.Name"
func (Symbol) ToIndexedSymbol ¶
type SymbolMetadata ¶
Source Files
¶
- config.go
- converter.go
- converter_base.go
- converter_basic.go
- converter_functions.go
- converter_lookup.go
- converter_orchestrator.go
- converter_registration.go
- field_interceptor.go
- file_manager.go
- generator.go
- generator_util.go
- new.go
- parser.go
- test_bdd.go
- test_converter.go
- test_golden.go
- test_helper.go
- type_util.go
- util.go
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
generator
command
|
|
|
go-mapper-gen
command
|
|
|
converters
|
|
|
internal
|
|
|
pkg
|
|
|
pkl
Code generated from Pkl module `gomappergen.Config`.
|
Code generated from Pkl module `gomappergen.Config`. |
|
pkl/mapper
Code generated from Pkl module `gomappergen.mapper`.
|
Code generated from Pkl module `gomappergen.mapper`. |
|
pkl/mapper/decoratormode
Code generated from Pkl module `gomappergen.mapper`.
|
Code generated from Pkl module `gomappergen.mapper`. |
|
pkl/mapper/pointer
Code generated from Pkl module `gomappergen.mapper`.
|
Code generated from Pkl module `gomappergen.mapper`. |