codegenutil

package module
v0.0.10 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2022 License: Apache-2.0, BSD-3-Clause Imports: 9 Imported by: 0

README

go-codegenutil

Basic code generation utilities for Go metaprogrammers.

The codegenutil package provides primitives for representing Go identifiers as (import path, name) pairs--the *codegenutil.Symbol type--as well as a codegenutil.Package type that represents a unique name for a Go package.

The unusedimport package provides the import pruning features of the goimports command in library form and can be used without depending on the other packages in this library.

The codetemplate package provides an alternative to the "text/template" package for writing Go code templates. It is based on a 2022 fork of the "text/template" package and uses the same parser. Such templates can use *codegenutil.Symbol values directly as well as a "header" function to insert the package statement and imports blocks. codetemplate will ensure an import exists for each identifier from other package, and it will format the symbol according to the local name of the generated import statement.

Example

import (
    "github.com/meta-programming/go-codegenutil"
    "github.com/meta-programming/go-codegenutil/codetemplate"
)

func example() {
    // Parse the template.
    template, err := codetemplate.Parse(`// Package mypkg does neat things.
{{header}}

import "automaticallyremoved"

var result1 = {{.maxFn1}}(1, 2)
var result2 = {{.maxFn2}}(1, 2)

func main() {
    fmt.Printf("%f and %f\n", result1, result2)
    fmt.Printf("The people's choice: %f\n", {{.peoplesChoice}})
}
`)
    if err != nil {
        fmt.Printf("error parsing template: %v", err)
        return
    }

    // Execute the template with whatever data you'd like, and use symbols directly.
    filePackage := codegenutil.AssumedPackageName("abc.xyz/mypkg")
    code := &strings.Builder{}
    if err := template.Execute(codegenutil.NewFileImports(filePackage), code, map[string]*codegenutil.Symbol{
        "maxFn1":        codegenutil.Sym("math", "Max"),
        "maxFn2":        codegenutil.Sym("alternative/math", "Max"),
        "peoplesChoice": filePackage.Symbol("result2"),
    }); err != nil {
        fmt.Printf("error executing template: %v", err)
        return
    }

    fmt.Print(code.String())
}

Outputs:

// Package mypkg does neat things.
package mypkg

import (
	"math"

	math2 "alternative/math"
)

var result1 = math.Max(1, 2)
var result2 = math2.Max(1, 2)

func main() {
	fmt.Printf("%f and %f\n", result1, result2)
	fmt.Printf("The people's choice: %f\n", result2)
}

Documentation

Overview

Package codegenutil provides utilities for generating code in Go.

The current ontology is as follows:

Package: Uniquely identifies a package using an import path. Also contains the "package name," which is typically inferred from the import path.

Imports: Keeps track of a set of imports within a Go file.

Symbol: A (Package, string) pair where the string is an identifer. "math.Max" is a textual representation of a symbol where "math" is the Package and "Max" is the local identifier.

Index

Constants

This section is empty.

Variables

View Source
var BuiltinPackage = &Package{}

BuiltinPackage, which is the zero value for Package.

Functions

func IsValidIdentifier added in v0.0.10

func IsValidIdentifier(arg string) bool

IsValidIdentifier reports if the argument is a valid Go identifier.

Types

type FileImports

type FileImports struct {
	// contains filtered or unexported fields
}

FileImports captures information about import entries in a Go file and the package of the Go file itself.

func NewFileImports

func NewFileImports(p *Package, opts ...FileImportsOption) *FileImports

NewFileImports returns a new *FileImports object with no imports.

func (*FileImports) Add

func (fi *FileImports) Add(pkg *Package, alias string) *ImportSpec

Add adds an import to the given package using the given alias.

If alias is empty, the import spec will have no alias, and the package name of the package will be used.

If the package name or alias conflicts with an existing import, an alias will be generated.

func (*FileImports) Find

func (fi *FileImports) Find(p *Package) *ImportSpec

Find returns the import spec corresponding a given package or nil if the package wasn't found.

It is possible to have multiple imports of a package, and this function will return the first.

func (*FileImports) Format added in v0.0.5

func (fi *FileImports) Format(includePackageStatement bool) string

Format returns prints a valid Go imports block containing all of the imports. If true is passed, a package statement is included above the imports block.

func (*FileImports) List added in v0.0.4

func (fi *FileImports) List() []*ImportSpec

List returns all of the import specs for the FileImports object.

func (*FileImports) Package added in v0.0.4

func (fi *FileImports) Package() *Package

Package returns the Package of the file in which the imports appear.

func (*FileImports) String added in v0.0.4

func (fi *FileImports) String() string

String prints a valid Go imports block containing all of the imports.

type FileImportsOption

type FileImportsOption struct {
	// contains filtered or unexported fields
}

FileImportsOption is an option that can be passed to NewImportsFor to customize its behavior.

func CustomPackageNameSuggester

func CustomPackageNameSuggester(fn func(pkg *Package, tryImportSpec func(localPackageName string) (acceptable bool))) FileImportsOption

CustomPackageNameSuggester returns an option for customizing how an Imports object chooses the package name to use for an import path and whether that package name is an alias.

The suggestion function takes three arguments:

1) importPath is the path of the package being imported, such as "x/y/z" in the import `import x "x/y/z"`.

2) packageNameInPackageClause is the name of the package from the package clause of Go files that define the package. This is often the last element of the import path, but it frequently differs. For example, "blah" is typically the package name for an import like "x/y/blah/v3" because of how Go's module system works. This value may also be the empty string, which indicates the package name is unknown.

3) callback is the function that should be called with package name suggestions. fn should call the callback function with different candidate names until the callback returns false, at which point the function should stop suggesting package names. The arguments to the callback are the package name to use and whether or not that package name should be considered an alias.

func WithImports

func WithImports(pkgs ...*Package) FileImportsOption

WithImports returns an option that add all of the provided package to the returned *FileImports.

type ImportSpec

type ImportSpec struct {
	// contains filtered or unexported fields
}

ImportSpec is an entry within the set of imports of a Go file. It does not contain formatting information, like import order.

func (*ImportSpec) FileLocalPackageName

func (is *ImportSpec) FileLocalPackageName() string

FileLocalPackageName returns the package name used to identify this package within the Go file using this import spec.

func (*ImportSpec) IsExplicit

func (is *ImportSpec) IsExplicit() bool

ImportSpec returns true if the import spec has an explicit package name (sometimes called an "alias" or "package name alias")

func (*ImportSpec) PackageName

func (is *ImportSpec) PackageName() *Package

PackageName returns the package designated by the import.

type Package

type Package struct {
	// contains filtered or unexported fields
}

Package identifies a package by its import path and the package name used to declare the package (i.e. the "xyz" in "package xyz" statement).

func AssumedPackageName

func AssumedPackageName(importPath string) *Package

AssumedPackageName returns the assumed name of the package according the the package definition's package clause based purely on the package's import path.

Per https://golang.org/ref/spec#Import_declarations: "If the PackageName is omitted, it defaults to the identifier specified in the package clause of the imported package." The file being loaded is not available in gopoet (and many go tools), so this function needs to be used.

Note: path.Base differs from the package name guesser used by most tools. See https://pkg.go.dev/golang.org/x/tools/internal/imports#ImportPathToAssumedName.

func ExplicitPackageName

func ExplicitPackageName(importPath, packageName string) *Package

ExplicitPackageName is used to construct an explicit PackageName in case AssumedPackageName is insufficient.

func (*Package) ImportPath

func (p *Package) ImportPath() string

ImportPath returns the value in the import statement used to import the package.

This path typically identifies a package uniquely, but that is not necesarily the case from the Go spec. https://go.dev/ref/spec#ImportSpec.

func (*Package) IsBuiltin added in v0.0.9

func (p *Package) IsBuiltin() bool

IsBuiltin returns true if the package represents the builtin package, which is used for symbols like "int".

func (*Package) Name

func (p *Package) Name() string

Name is the string that appears in the "package clause" of the defining go file.

See https://go.dev/ref/spec#PackageClause and https://go.dev/ref/spec#PackageName.

func (*Package) Symbol

func (p *Package) Symbol(idName string) *Symbol

Symbol returns a new Symbol within the given package.

type Symbol

type Symbol struct {
	// contains filtered or unexported fields
}

Symbol in this package is used for a (PackageName, string) pair that pair

func Sym added in v0.0.7

func Sym(importPath, name string) *Symbol

Sym is syntax sugar for AssumedPackageName(importPath).Symbol(name).

func (*Symbol) GoCode added in v0.0.5

func (s *Symbol) GoCode(imports *FileImports) string

GoCode formats the symbol in a given printing context.

The Imports argument is the set of imports currently imported in the file. If the symbol's import is not in the set of import specs.

func (*Symbol) Name

func (s *Symbol) Name() string

Name returns the local name of the symbol. For symbol "github.com/meta-programming/go-codegenutil".Foo, Name() would return "Foo".

If the symbol is a "QualifiedSymbol"[1], this is the "identifier" part of the symbol

func (*Symbol) Package

func (s *Symbol) Package() *Package

Package returns the package name of the symbol.

This should not be nil. If symbol is a local symbol for code in a file inside package "abc/xyz", the package should be set to AssumedPackageName("abc/xzy").

Directories

Path Synopsis
Package codetemplate uses a fork of "text/template" to print Go code more concisely.
Package codetemplate uses a fork of "text/template" to print Go code more concisely.
Package debugutil contains utility functions, mostly helpful for writting tests, related to code generation.
Package debugutil contains utility functions, mostly helpful for writting tests, related to code generation.
Package template implements data-driven templates for generating textual output.
Package template implements data-driven templates for generating textual output.
internal/fmtsort
Package fmtsort provides a general stable ordering mechanism for maps, on behalf of the fmt and text/template packages.
Package fmtsort provides a general stable ordering mechanism for maps, on behalf of the fmt and text/template packages.
Package unusedimports provides the import pruning features of the `goimports` command in library form.
Package unusedimports provides the import pruning features of the `goimports` command in library form.

Jump to

Keyboard shortcuts

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