gen

package
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 23, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DetectLocalImportPrefixes added in v0.6.0

func DetectLocalImportPrefixes(startPath string) []string

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:

  1. Parses the package and extracts function declarations
  2. Gathers all required imports from function signatures
  3. Generates wrapper implementations for each function
  4. Formats the code with proper imports
  5. 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:

  1. Scans the AST for wrapper declarations (TODO calls or implementation comments)
  2. Finds the wrapped function's declaration in the package or imports
  3. Generates wrapper implementation code using WriteFunctionWrapper
  4. Replaces old declarations with generated code using AST node replacements
  5. Adds missing imports automatically
  6. 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:

  1. Checks if path ends with "..." for recursive processing
  2. Processes all Go files in the directory
  3. If recursive, processes all subdirectories (excluding hidden dirs and node_modules)
  4. 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:

  1. Parses the package containing the file
  2. Calls RewriteAstFile to perform the actual rewriting
  3. 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

func ImplFromString(str string) (Impl, error)

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

func (impl Impl) String() 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:

  1. Wrapper type declaration (struct{})
  2. String() method (always generated)
  3. Description methods (if ImplDescription is set): Name, NumArgs, ArgNames, ArgTypes, etc.
  4. Call method (if ImplCallWrapper is set): Calls function with []any arguments
  5. CallWithStrings method (if ImplCallWithStringsWrapper is set): Parses string arguments
  6. CallWithNamedStrings method (if ImplCallWithNamedStringsWrapper is set): Uses map[string]string
  7. 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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL