Documentation
¶
Index ¶
- func DetectLocalImportPrefixes(startPath string) []string
- func PackageFunctions(pkgDir, genFilename, namePrefix string, printOnly bool, ...) error
- func RewriteAstFile(fset *token.FileSet, pkgName string, pkgFiles map[string]*ast.File, ...) (err error)
- func RewriteAstFileSource(fset *token.FileSet, pkgName string, pkgFiles map[string]*ast.File, ...) (err error)
- func RewriteDir(path string, verbose bool, printOnly io.Writer, validate bool, ...) (err error)
- func RewriteFile(filePath string, verbose bool, printOnly io.Writer, validate bool, ...) (err error)
- type Impl
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DetectLocalImportPrefixes ¶ added in v0.6.0
DetectLocalImportPrefixes attempts to auto-detect local import prefixes by finding and parsing the nearest go.mod file starting from the given path.
The function searches upward from the given directory until it finds a go.mod file, then extracts the module path to use as the local import prefix.
Parameters:
- startPath: The directory path to start searching from
Returns:
- A slice containing the detected module path with a trailing slash, or nil if no go.mod file is found
Example:
If go.mod contains: module github.com/myorg/myproject
Returns: []string{"github.com/myorg/"}
func PackageFunctions ¶
func PackageFunctions(pkgDir, genFilename, namePrefix string, printOnly bool, jsonTypeReplacements map[string]string, onlyFuncs ...string) error
PackageFunctions generates wrapper implementations for functions in a package. This function is used for the older generation approach where all wrappers are written to a single generated file.
Parameters:
- pkgDir: Directory containing the package to process
- genFilename: Name of the file to generate (e.g., "generated.go")
- namePrefix: Prefix to add to generated type names (e.g., "Func")
- printOnly: If true, print to stdout instead of writing file
- jsonTypeReplacements: Map of interface types to concrete types for JSON unmarshalling
- onlyFuncs: Optional list of specific functions to wrap; if empty, wraps all exported functions
Returns:
- error if parsing, generation, or file writing fails
The function:
- Parses the package and extracts function declarations
- Gathers all required imports from function signatures
- Generates wrapper implementations for each function
- Formats the code with proper imports
- Writes to genFilename or prints to stdout if printOnly is true
Example generated file structure:
// This file has been AUTOGENERATED!
package mypackage
import (
"context"
"reflect"
function "github.com/domonda/go-function"
)
type FuncMyFunction struct{}
// ... wrapper methods ...
func RewriteAstFile ¶
func RewriteAstFile(fset *token.FileSet, pkgName string, pkgFiles map[string]*ast.File, astFile *ast.File, filePath string, verbose bool, printTo io.Writer, validate bool, jsonTypeReplacements map[string]string, localImportPrefixes []string) (err error)
RewriteAstFile is the core rewriting logic that processes an AST file. This function performs the actual wrapper code generation and replacement.
Parameters:
- fset: Token file set for position information
- pkgName: The package name
- pkgFiles: All files in the package
- astFile: The parsed AST of the file to process
- filePath: Original file path (for error messages and writing)
- verbose: If true, print detailed information
- printTo: If not nil, print to this writer instead of modifying file
- validate: If true, check for outdated wrappers without modifying files
- jsonTypeReplacements: Map of interface types to concrete types for JSON
- localImportPrefixes: Import path prefixes to treat as "local"
Returns:
- error if wrapper generation or file writing fails
The function:
- Scans the AST for wrapper declarations (TODO calls or implementation comments)
- Finds the wrapped function's declaration in the package or imports
- Generates wrapper implementation code using WriteFunctionWrapper
- Replaces old declarations with generated code using AST node replacements
- Adds missing imports automatically
- Formats the result and writes it back
Wrapper declarations are found by:
- Variable assignments with TODO calls: var x = function.WrapperTODO(F)
- Implementation comments: // myWrapper wraps F as function.Wrapper (generated code)
func RewriteAstFileSource ¶ added in v0.5.0
func RewriteAstFileSource(fset *token.FileSet, pkgName string, pkgFiles map[string]*ast.File, astFile *ast.File, filePath string, source []byte, verbose bool, printTo io.Writer, validate bool, jsonTypeReplacements map[string]string, localImportPrefixes []string) (err error)
RewriteAstFileSource is like RewriteAstFile but accepts the source content directly. This allows for in-memory testing without requiring files on disk.
Parameters:
- fset: Token file set for position information
- pkgName: The package name
- pkgFiles: All files in the package (for finding function declarations)
- astFile: The parsed AST of the file to process
- filePath: File path for error messages (doesn't need to exist)
- source: The original source code as bytes
- verbose: If true, print detailed information
- printTo: If not nil, print to this writer instead of modifying file
- validate: If true, check for outdated wrappers without modifying files
- jsonTypeReplacements: Map of interface types to concrete types for JSON
- localImportPrefixes: Import path prefixes to treat as "local"
Returns:
- error if wrapper generation or file writing fails
This function is useful for testing as it doesn't require actual files on disk.
Example ¶
ExampleRewriteAstFileSource demonstrates in-memory wrapper generation without disk I/O. This is useful for testing or integrating wrapper generation into other tools.
// Define source code with a wrapper TODO in memory
source := []byte(`package example
import "github.com/domonda/go-function"
// Add adds two integers.
// a: First number
// b: Second number
func Add(a, b int) int {
return a + b
}
var addWrapper = function.WrapperTODO(Add)
`)
// Parse the source code into an AST
fset := token.NewFileSet()
astFile, err := parser.ParseFile(fset, "example.go", source, parser.ParseComments)
if err != nil {
panic(err)
}
// Create package files map (single file in this example)
pkgFiles := map[string]*ast.File{
"example.go": astFile,
}
// Buffer to capture the generated output
var output bytes.Buffer
// Generate wrapper code in memory
err = RewriteAstFileSource(
fset, // Token file set
"example", // Package name
pkgFiles, // All package files
astFile, // The AST file to process
"example.go", // File path (for error messages only)
source, // Original source code
false, // verbose
&output, // Write output here instead of to disk
false, // not validating
nil, // No JSON type replacements
nil, // No local import prefixes
)
if err != nil {
panic(err)
}
// Print the generated wrapper code
fmt.Println(output.String())
Output: package example import ( "context" "encoding/json" "reflect" "github.com/domonda/go-function" ) // Add adds two integers. // // a: First number // b: Second number func Add(a, b int) int { return a + b } // addWrapper wraps Add as function.Wrapper (generated code) var addWrapper addWrapperT // addWrapperT wraps Add as function.Wrapper (generated code) type addWrapperT struct{} func (addWrapperT) String() string { return "Add(a, b int) int" } func (addWrapperT) Name() string { return "Add" } func (addWrapperT) NumArgs() int { return 2 } func (addWrapperT) ContextArg() bool { return false } func (addWrapperT) NumResults() int { return 1 } func (addWrapperT) ErrorResult() bool { return false } func (addWrapperT) ArgNames() []string { return []string{"a", "b"} } func (addWrapperT) ArgDescriptions() []string { return []string{"First number", "Second number"} } func (addWrapperT) ArgTypes() []reflect.Type { return []reflect.Type{ reflect.TypeFor[int](), reflect.TypeFor[int](), } } func (addWrapperT) ResultTypes() []reflect.Type { return []reflect.Type{ reflect.TypeFor[int](), } } func (addWrapperT) Call(_ context.Context, args []any) (results []any, err error) { results = make([]any, 1) results[0] = Add(args[0].(int), args[1].(int)) // wrapped call return results, err } func (f addWrapperT) CallWithStrings(_ context.Context, strs ...string) (results []any, err error) { var a struct { a int b int } if 0 < len(strs) { err := function.ScanString(strs[0], &a.a) if err != nil { return nil, function.NewErrParseArgString(err, f, "a", strs[0]) } } if 1 < len(strs) { err := function.ScanString(strs[1], &a.b) if err != nil { return nil, function.NewErrParseArgString(err, f, "b", strs[1]) } } results = make([]any, 1) results[0] = Add(a.a, a.b) // wrapped call return results, err } func (f addWrapperT) CallWithNamedStrings(_ context.Context, strs map[string]string) (results []any, err error) { var a struct { a int b int } if str, ok := strs["a"]; ok { err := function.ScanString(str, &a.a) if err != nil { return nil, function.NewErrParseArgString(err, f, "a", str) } } if str, ok := strs["b"]; ok { err := function.ScanString(str, &a.b) if err != nil { return nil, function.NewErrParseArgString(err, f, "b", str) } } results = make([]any, 1) results[0] = Add(a.a, a.b) // wrapped call return results, err } func (f addWrapperT) CallWithJSON(_ context.Context, argsJSON []byte) (results []any, err error) { var a struct { A int B int } err = json.Unmarshal(argsJSON, &a) if err != nil { return nil, function.NewErrParseArgsJSON(err, f, argsJSON) } results = make([]any, 1) results[0] = Add(a.A, a.B) // wrapped call return results, err }
func RewriteDir ¶
func RewriteDir(path string, verbose bool, printOnly io.Writer, validate bool, jsonTypeReplacements map[string]string, localImportPrefixes []string) (err error)
RewriteDir processes a directory (and optionally subdirectories) to rewrite wrapper declarations. This is the main entry point for the code generator when invoked on directories.
Parameters:
- path: Directory path to process; append "..." for recursive processing
- verbose: If true, print detailed information about processing
- printOnly: If not nil, print generated code to this writer instead of modifying files
- validate: If true, check for outdated wrappers without modifying files
- jsonTypeReplacements: Map of interface types to concrete types for JSON unmarshalling
- localImportPrefixes: Import path prefixes to treat as "local" for import grouping
Returns:
- error if any file processing fails or validation detects issues
The function:
- Checks if path ends with "..." for recursive processing
- Processes all Go files in the directory
- If recursive, processes all subdirectories (excluding hidden dirs and node_modules)
- Skips test files (_test.go)
Example:
err := RewriteDir("./pkg/mypackage/...", true, nil, false, nil, []string{"github.com/myorg/"})
func RewriteFile ¶
func RewriteFile(filePath string, verbose bool, printOnly io.Writer, validate bool, jsonTypeReplacements map[string]string, localImportPrefixes []string) (err error)
RewriteFile processes a single Go source file to rewrite wrapper declarations.
Parameters:
- filePath: Path to the Go source file to process
- verbose: If true, print detailed information about processing
- printOnly: If not nil, print generated code to this writer instead of modifying file
- validate: If true, check for outdated wrappers without modifying files
- jsonTypeReplacements: Map of interface types to concrete types for JSON unmarshalling
- localImportPrefixes: Import path prefixes to treat as "local" for import grouping
Returns:
- error if file doesn't exist, is a directory, or processing fails
The function:
- Parses the package containing the file
- Calls RewriteAstFile to perform the actual rewriting
- Writes the modified content back to the file (or prints it)
Types ¶
type Impl ¶
type Impl int
Impl represents which wrapper interfaces should be implemented. Multiple interfaces can be combined using bitwise OR.
const ( // ImplDescription implements function.Description interface ImplDescription Impl = 1 << iota // ImplCallWrapper implements function.CallWrapper interface ImplCallWrapper // ImplCallWithStringsWrapper implements function.CallWithStringsWrapper interface ImplCallWithStringsWrapper // ImplCallWithNamedStringsWrapper implements function.CallWithNamedStringsWrapper interface ImplCallWithNamedStringsWrapper // ImplCallWithJSONWrapper implements function.CallWithJSONWrapper interface ImplCallWithJSONWrapper // ImplWrapper implements the full function.Wrapper interface (all of the above) ImplWrapper = ImplDescription | ImplCallWrapper | ImplCallWithStringsWrapper | ImplCallWithNamedStringsWrapper | ImplCallWithJSONWrapper )
func ImplFromString ¶
ImplFromString parses a string representation of an interface name into an Impl value.
Supported strings:
- "function.Wrapper" -> ImplWrapper (all interfaces)
- "function.Description" -> ImplDescription
- "function.CallWrapper" -> ImplCallWrapper
- "function.CallWithStringsWrapper" -> ImplCallWithStringsWrapper
- "function.CallWithNamedStringsWrapper" -> ImplCallWithNamedStringsWrapper
- "function.ImplCallWithJSONWrapper" -> ImplCallWithJSONWrapper
Returns an error if the string doesn't match any known interface.
func (Impl) String ¶
String returns the string representation of the Impl value. For combined implementations (bitwise OR), it returns a generic "Impl(n)" format.
func (Impl) WriteFunctionWrapper ¶
func (impl Impl) WriteFunctionWrapper(w io.Writer, funcFile *ast.File, funcDecl *ast.FuncDecl, implType, funcPackage string, neededImportLines map[string]struct{}, jsonTypeReplacements map[string]string, targetFileImports []*ast.ImportSpec) error
WriteFunctionWrapper generates a complete wrapper implementation for a function. This is the core code generation logic that produces type-safe wrapper methods.
Parameters:
- w: Writer to output generated code to
- funcFile: The AST file containing the function (needed for imports)
- funcDecl: The function declaration to wrap
- implType: Name of the generated wrapper type (e.g., "myFunctionT")
- funcPackage: Package name qualifier for the wrapped function (empty string if same package)
- neededImportLines: Map to collect all imports needed by the generated code
- jsonTypeReplacements: Map of interface types to concrete types for JSON unmarshalling
- targetFileImports: Import specs from the target file to check for conflicts
Returns:
- error if code generation fails
The generated code includes:
- Wrapper type declaration (struct{})
- String() method (always generated)
- Description methods (if ImplDescription is set): Name, NumArgs, ArgNames, ArgTypes, etc.
- Call method (if ImplCallWrapper is set): Calls function with []any arguments
- CallWithStrings method (if ImplCallWithStringsWrapper is set): Parses string arguments
- CallWithNamedStrings method (if ImplCallWithNamedStringsWrapper is set): Uses map[string]string
- CallWithJSON method (if ImplCallWithJSONWrapper is set): Unmarshals JSON to arguments
The method handles:
- context.Context as first argument (automatic detection and handling)
- Variadic parameters (...type)
- Error return values (automatic error result detection)
- Type conversions for string parsing
- Proper argument descriptions from function comments