structer

package module
v0.0.0-...-a648134 Latest Latest
Warning

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

Go to latest
Published: Feb 9, 2020 License: MIT Imports: 15 Imported by: 3

README

Structer
========

**Note**: This badly needed a lick of paint before Go Modules came onto the scene,
and should now be considered obsolete/unmaintained. Having said that, if you depend
on this and you run into trouble with it, please open an issue and I'll see if I
can help.

-----

Structer is a tool for dismantling struct definitions to try to ease the agony
of code generation.

Structer requires Go 1.9.

It ties together `go/types <https://godoc.org/go/types>`_ and `go/ast
<https://godoc.org/go/ast>`_ to try to simplify recursively walking through a
type declaration, importing relevant pacakges and extracting required code and
documentation.

The API is very much a moving target at this point - the main use case is
supporting my alternative `msgp code generator
<https://github.com/shabbyrobe/msgpgen>`_ (which supplements `msgp
<https://github.com/tinylib/msgp>`_ with some sorely needed conveniences), but
any reports of other use cases that could be supported would be great!

Efficiency is not a major goal of this project - sewing together the disparate
and disjointed APIs of `go/types`, `go/ast` and `go/doc` is the overriding
priority.

The core of structer is the ``TypePackageSet``, which allows you to import
complete packages and their ASTs::

    tpset := structer.NewTypePackageSet()
    pkg, err := tpset.Import("path/to/pkg")

You can then recursively walk type definitions (importing external defs as you
like by calling back to ``TypePackageSet``) in order to generate code. Either
fully implement ``structer.TypeVisitor`` yourself, or just part of it using
``structer.PartialTypeVisitor``::

    ppv := &structer.PartialTypeVisitor{
        EnterStructFunc: func(s s structer.StructInfo) error {
            return nil
        },
        LeaveStructFunc: func(s structer.StructInfo) error {
            return nil
        },
        EnterFieldFunc: func(s s structer.StructInfo, field *types.Var, tag string) error {
            return nil
        },
        LeaveFieldFunc: func(s s structer.StructInfo, field *types.Var, tag string) error {
            return nil
        },
        VisitBasic(t *types.Basic) error {
            fmt.Printf("Found basic leaf node %s", t.String())
            return nil
        },
        VisitNamed(t *types.Named) error {
            fmt.Printf("Found named leaf node %s, importing", t.String())
            _, err := tpset.ImportNamed(t)
            return err
        },
    }

``Walk`` will visit every part of a compound type declaration and stop only at
``types.Basic`` or ``types.Named`` declarations.

``Walk`` shouldn't even have trouble with this crazy thing::

    type Pants struct {
        Foo struct {
            Bar map[struct{ X, Y int}]map[struct{ Y, Z int}]struct {
                Baz []*Pants
            }
        }
    }
    

Structer also allows you to extract all constant values across all imported
packages that have a certain type::

    tpset := structer.NewTypePackageSet()
    pkg, err := tpset.Import("path/to/pkg")
    consts, err := tpset.ExtractConstants(structer.NewTypeName("path/to/pkg", "MyEnum"), false)


Known limitations
-----------------

- API is very unstable
- Not enough tests yet
- Poor documentation
- ``TypeVisitor`` should possibly have  ``EnterMap`` and ``LeaveMap``.
  ``EnterMapKey`` and ``LeaveMapValue`` may be sufficient, but they're less clear.

Documentation

Index

Constants

View Source
const (
	LogTypeSet = "typeset"

	// Log code indicating an error occurred in the types.Config.Error
	// callback, but execution continued.
	LogTypesConfigError = 1

	// Log code indicating an error was returned by types.Config.Check, but
	// execution continued.
	LogTypeCheck = 2
)
View Source
const (
	NoPackage     PackageKind = ""
	VendorPackage             = "vendor"
	SystemPackage             = "system"
	UserPackage               = "user"
)

Variables

View Source
var (
	BuildContext = build.Default
)
View Source
var EnumType = NewTypeName("github.com/shabbyrobe/structer", "Enum")
View Source
var (
	// step over the current node. used in Enter calls to go to the
	// next type without descending.
	WalkOver = errors.New("stop")
)

Functions

func ActuallyImplements

func ActuallyImplements(typ types.Type, ifaceTyp types.Type) bool

types.Implements is a bit more low level - this will report true if the iface is a named type with an underlying interface, and also if the checked type is assignable as a pointer as well as without.

func CaptureErrors

func CaptureErrors(h func(error)) option

func IsInvalid

func IsInvalid(typ types.Type) bool

func IsObjectInvalid

func IsObjectInvalid(obj types.Object) bool

func Walk

func Walk(tn TypeName, t types.Type, visitor TypeVisitor) error

Walk walks a type recursively, visiting each child type using the visitor provided.

This allows you to traverse complex nested definitions, like struct { Foo map[struct{X, Y int}]struct{ Bar map[struct{Z, N}]string } } and respond to each node. Each node will have access to complete information about the type from the golang "types" package. If your visitor carries a reference to TypePackageSet as well, you can do complex lookups on the visited types.

It is designed for use with code generators that want to dismantle a struct and respond dynamically to the types therein.

Types

type ASTPackage

type ASTPackage struct {
	AST *ast.Package

	CommentMap ast.CommentMap

	// all file names, not filtered by build, found in the package, relative
	// to FullPath
	Files []string

	// all file contents, not filtered by build, found in the package, relative
	// to FullPath. the ast package makes it unreasonably difficult to get at this.
	Contents map[string][]byte

	// package identifier (i.e. for import "foo/bar/baz", the Name would be 'baz'
	Name string

	// import path to package - "foo/bar/baz"
	Path string

	// filesystem path to package - "/path/to/my/go/src/foo/bar/baz"
	FullPath string

	// ast.Package unhelpfully indexes by absolute path. this is not useful
	// for us.
	FileASTs map[string]*ast.File
}

type ASTPackageSet

type ASTPackageSet struct {
	ParseDoc bool
	FileSet  *token.FileSet
	Packages map[string]*ASTPackage

	// Docs on single struct declarations appear on ast.GenDecl, but if we
	// search for a types.Object, we get an ast.TypeSpec. For multi-struct
	// parenthesised declarations, the doc appears on the ast.TypeSpec, i.e:
	//     type (
	//         // yep
	//         foo struct {}
	//     )
	//
	// We get around this by indexing all GenDecls by TypeSpec, so we can look
	// them back up later.
	//
	// More info:
	// https://stackoverflow.com/questions/19580688/go-parser-not-detecting-doc-comments-on-struct-type
	//
	GenDecls map[*ast.TypeSpec]*ast.GenDecl

	Decls map[token.Pos]ast.Decl

	Imported map[string]bool
}

func NewASTPackageSet

func NewASTPackageSet() *ASTPackageSet

func (*ASTPackageSet) Add

func (p *ASTPackageSet) Add(dir string, pkg string) error

Add adds the package, found at source path "dir", to the ASTPackageSet. GOPATH is not inferred automatically, nor is pkg appended to dir by default.

If "" is passed to dir, GOPATH/src + pkg is implied.

func (*ASTPackageSet) FindComment

func (p *ASTPackageSet) FindComment(pkgPath string, pos token.Pos) (docstr string, err error)

func (*ASTPackageSet) FindNodeByPackagePathPos

func (p *ASTPackageSet) FindNodeByPackagePathPos(pkgPath string, pos token.Pos) ast.Node

func (*ASTPackageSet) ParentDecl

func (p *ASTPackageSet) ParentDecl(pkgPath string, pos token.Pos) ast.Node

type ASTPosFinder

type ASTPosFinder struct {
	Pos  token.Pos
	Node ast.Node
}

func (*ASTPosFinder) Visit

func (a *ASTPosFinder) Visit(node ast.Node) (w ast.Visitor)

type ASTStackPosFinder

type ASTStackPosFinder struct {
	Pos   token.Pos
	Stack []ast.Node
	Found bool
}

func (*ASTStackPosFinder) Visit

func (a *ASTStackPosFinder) Visit(node ast.Node) (w ast.Visitor)

type Config

type Config struct {
	IncludeTests bool
}

type ConstValue

type ConstValue struct {
	Name  TypeName
	Value constant.Value
}

type Consts

type Consts struct {
	Type       TypeName
	Underlying TypeName
	IsEnum     bool
	Values     []*ConstValue
}

func (*Consts) SortedValues

func (e *Consts) SortedValues() []*ConstValue

type Enum

type Enum interface {
	IsEnum()
}

type Log

type Log interface {
	Log(category string, code int, message string, args ...interface{})
}

type MultiVisitor

type MultiVisitor struct {
	Visitors []TypeVisitor
}

MultiVisitor allows you to wrap multiple visitors and call each of them in sequence for each node in the type definition.

If one of the visitors returns an error, the Visit/Leave/Enter methods will abort immediately rather than complete.

func (*MultiVisitor) EnterArray

func (p *MultiVisitor) EnterArray(ctx WalkContext, t *types.Array) error

func (*MultiVisitor) EnterField

func (p *MultiVisitor) EnterField(ctx WalkContext, s StructInfo, field *types.Var, tag string) error

func (*MultiVisitor) EnterMapElem

func (p *MultiVisitor) EnterMapElem(ctx WalkContext, ft *types.Map, elem types.Type) error

func (*MultiVisitor) EnterMapKey

func (p *MultiVisitor) EnterMapKey(ctx WalkContext, ft *types.Map, key types.Type) error

func (*MultiVisitor) EnterPointer

func (p *MultiVisitor) EnterPointer(ctx WalkContext, t *types.Pointer) error

func (*MultiVisitor) EnterSlice

func (p *MultiVisitor) EnterSlice(ctx WalkContext, t *types.Slice) error

func (*MultiVisitor) EnterStruct

func (p *MultiVisitor) EnterStruct(ctx WalkContext, s StructInfo) error

func (*MultiVisitor) LeaveArray

func (p *MultiVisitor) LeaveArray(ctx WalkContext, t *types.Array) error

func (*MultiVisitor) LeaveField

func (p *MultiVisitor) LeaveField(ctx WalkContext, s StructInfo, field *types.Var, tag string) error

func (*MultiVisitor) LeaveMapElem

func (p *MultiVisitor) LeaveMapElem(ctx WalkContext, ft *types.Map, elem types.Type) error

func (*MultiVisitor) LeaveMapKey

func (p *MultiVisitor) LeaveMapKey(ctx WalkContext, ft *types.Map, key types.Type) error

func (*MultiVisitor) LeavePointer

func (p *MultiVisitor) LeavePointer(ctx WalkContext, t *types.Pointer) error

func (*MultiVisitor) LeaveSlice

func (p *MultiVisitor) LeaveSlice(ctx WalkContext, t *types.Slice) error

func (*MultiVisitor) LeaveStruct

func (p *MultiVisitor) LeaveStruct(ctx WalkContext, s StructInfo) error

func (*MultiVisitor) VisitBasic

func (p *MultiVisitor) VisitBasic(ctx WalkContext, t *types.Basic) error

func (*MultiVisitor) VisitInterface

func (p *MultiVisitor) VisitInterface(ctx WalkContext, t *types.Interface) error

func (*MultiVisitor) VisitInvalid

func (p *MultiVisitor) VisitInvalid(ctx WalkContext, root TypeName, t *types.Basic) error

func (*MultiVisitor) VisitNamed

func (p *MultiVisitor) VisitNamed(ctx WalkContext, t *types.Named) error

type ObjectMap

type ObjectMap map[TypeName]types.Object

func (ObjectMap) SortedKeys

func (m ObjectMap) SortedKeys() TypeNames

type PackageKind

type PackageKind string

type PartialTypeVisitor

type PartialTypeVisitor struct {
	EnterStructFunc func(WalkContext, StructInfo) error
	LeaveStructFunc func(WalkContext, StructInfo) error

	EnterFieldFunc func(ctx WalkContext, s StructInfo, field *types.Var, tag string) error
	LeaveFieldFunc func(ctx WalkContext, s StructInfo, field *types.Var, tag string) error

	EnterMapKeyFunc func(ctx WalkContext, ft *types.Map, key types.Type) error
	LeaveMapKeyFunc func(ctx WalkContext, ft *types.Map, key types.Type) error

	EnterMapElemFunc func(ctx WalkContext, ft *types.Map, elem types.Type) error
	LeaveMapElemFunc func(ctx WalkContext, ft *types.Map, elem types.Type) error

	EnterPointerFunc func(ctx WalkContext, ft *types.Pointer) error
	LeavePointerFunc func(ctx WalkContext, ft *types.Pointer) error

	EnterSliceFunc func(ctx WalkContext, t *types.Slice) error
	LeaveSliceFunc func(ctx WalkContext, t *types.Slice) error

	EnterArrayFunc func(ctx WalkContext, t *types.Array) error
	LeaveArrayFunc func(ctx WalkContext, t *types.Array) error

	VisitBasicFunc     func(ctx WalkContext, t *types.Basic) error
	VisitNamedFunc     func(ctx WalkContext, t *types.Named) error
	VisitInvalidFunc   func(ctx WalkContext, root TypeName, t *types.Basic) error
	VisitInterfaceFunc func(ctx WalkContext, t *types.Interface) error
}

PartialTypeVisitor allows you to conveniently construct a visitor using only a handful of the Visit/Enter/Leave methods provided by the TypeVisitor interface.

func (*PartialTypeVisitor) EnterArray

func (p *PartialTypeVisitor) EnterArray(ctx WalkContext, t *types.Array) error

func (*PartialTypeVisitor) EnterField

func (p *PartialTypeVisitor) EnterField(ctx WalkContext, s StructInfo, field *types.Var, tag string) error

func (*PartialTypeVisitor) EnterMapElem

func (p *PartialTypeVisitor) EnterMapElem(ctx WalkContext, ft *types.Map, elem types.Type) error

func (*PartialTypeVisitor) EnterMapKey

func (p *PartialTypeVisitor) EnterMapKey(ctx WalkContext, ft *types.Map, key types.Type) error

func (*PartialTypeVisitor) EnterPointer

func (p *PartialTypeVisitor) EnterPointer(ctx WalkContext, t *types.Pointer) error

func (*PartialTypeVisitor) EnterSlice

func (p *PartialTypeVisitor) EnterSlice(ctx WalkContext, t *types.Slice) error

func (*PartialTypeVisitor) EnterStruct

func (p *PartialTypeVisitor) EnterStruct(ctx WalkContext, s StructInfo) error

func (*PartialTypeVisitor) LeaveArray

func (p *PartialTypeVisitor) LeaveArray(ctx WalkContext, t *types.Array) error

func (*PartialTypeVisitor) LeaveField

func (p *PartialTypeVisitor) LeaveField(ctx WalkContext, s StructInfo, field *types.Var, tag string) error

func (*PartialTypeVisitor) LeaveMapElem

func (p *PartialTypeVisitor) LeaveMapElem(ctx WalkContext, ft *types.Map, elem types.Type) error

func (*PartialTypeVisitor) LeaveMapKey

func (p *PartialTypeVisitor) LeaveMapKey(ctx WalkContext, ft *types.Map, key types.Type) error

func (*PartialTypeVisitor) LeavePointer

func (p *PartialTypeVisitor) LeavePointer(ctx WalkContext, t *types.Pointer) error

func (*PartialTypeVisitor) LeaveSlice

func (p *PartialTypeVisitor) LeaveSlice(ctx WalkContext, t *types.Slice) error

func (*PartialTypeVisitor) LeaveStruct

func (p *PartialTypeVisitor) LeaveStruct(ctx WalkContext, s StructInfo) error

func (*PartialTypeVisitor) VisitBasic

func (p *PartialTypeVisitor) VisitBasic(ctx WalkContext, t *types.Basic) error

func (*PartialTypeVisitor) VisitInterface

func (p *PartialTypeVisitor) VisitInterface(ctx WalkContext, t *types.Interface) error

func (*PartialTypeVisitor) VisitInvalid

func (p *PartialTypeVisitor) VisitInvalid(ctx WalkContext, root TypeName, t *types.Basic) error

func (*PartialTypeVisitor) VisitNamed

func (p *PartialTypeVisitor) VisitNamed(ctx WalkContext, t *types.Named) error

type StructInfo

type StructInfo struct {
	// Y U NO TypeName?
	// Because nested structs don't have a fully qualified name.
	Package string
	Name    string

	// This is the type name at the root of the Walk. This gives some context
	// recursively walking nested structs.
	Root TypeName

	Struct *types.Struct
}

type TypeMap

type TypeMap map[TypeName]types.Type

func (TypeMap) SortedKeys

func (m TypeMap) SortedKeys() TypeNames

type TypeName

type TypeName struct {
	PackagePath string

	Full string
	Name string
	// contains filtered or unexported fields
}

func ExtractTypeName

func ExtractTypeName(t types.Type) TypeName

func NewBuiltinType

func NewBuiltinType(name string) TypeName

func NewTypeName

func NewTypeName(pkgPath string, name string) TypeName

func ParseLocalName

func ParseLocalName(name string, localPkg string) (tn TypeName, err error)

WAT?

func ParseTypeName

func ParseTypeName(name string) (tn TypeName, err error)

func (TypeName) IsBefore

func (t TypeName) IsBefore(c TypeName) bool

func (TypeName) IsBuiltin

func (t TypeName) IsBuiltin() bool

func (TypeName) IsExported

func (t TypeName) IsExported() bool

func (TypeName) IsType

func (t TypeName) IsType(typ types.Type) bool

func (TypeName) String

func (t TypeName) String() string

type TypeNames

type TypeNames []TypeName

func (TypeNames) Sort

func (ns TypeNames) Sort()

func (TypeNames) Sorted

func (ns TypeNames) Sorted() TypeNames

type TypePackageSet

type TypePackageSet struct {
	Config Config

	TypePackages map[string]*types.Package
	ASTPackages  *ASTPackageSet
	Infos        map[string]types.Info

	// List of files extracted from go/build.Package, indexed by package name.
	// This is the list of files that would be built for the package on this
	// machine if "go build" or "go install" was run.
	BuiltFiles map[string][]string

	DefaultImporter types.Importer

	TypesConfig types.Config
	Objects     map[TypeName]types.Object
	Kinds       map[string]PackageKind

	// According to the types.Error documentation: "A "soft" error is an error
	// that still permits a valid interpretation of a package (such as 'unused
	// variable'); "hard" errors may lead to unpredictable
	// behavior if ignored."
	//
	// This defaults to "true" as types considers certain errors that are likelier
	// to be present before a code generation step as "hard" errors. Set this to
	// false to fail on hard errors too.
	AllowHardTypesError bool

	Log Log
}

TypePackageSet collects information about the types in all of the imported packages.

func NewTypePackageSet

func NewTypePackageSet(opts ...option) *TypePackageSet

func (*TypePackageSet) ExtractConsts

func (t *TypePackageSet) ExtractConsts(name TypeName, includeUnexported bool) (*Consts, error)

ExtractConsts extracts all constants that satisfy the supplied type from the same package.

Go provides no language-level idea of an Enum constraint, so structer provides the IsEnum interface. If the type satisfies IsEnum, it is assumed that the range of constants expresses the limit of all possible values for that type. Otherwise, it is just a bag of constants.

func (*TypePackageSet) ExtractSource

func (t *TypePackageSet) ExtractSource(name TypeName) ([]byte, error)

func (*TypePackageSet) FieldDoc

func (t *TypePackageSet) FieldDoc(tn TypeName, field string) (docstr string, err error)

FieldDoc returns the documentation for a struct field.

func (*TypePackageSet) FilePackage

func (t *TypePackageSet) FilePackage(file string) (PackageKind, string, error)

func (*TypePackageSet) FindImplementers

func (t *TypePackageSet) FindImplementers(ifaceName TypeName) (TypeMap, error)

FindImplementers lists all types in all imported user packages which implement the interface supplied in the argument.

This does not yield types from system packages or vendor packages yet.

func (*TypePackageSet) FindImportObject

func (t *TypePackageSet) FindImportObject(name TypeName) (types.Object, error)

func (*TypePackageSet) FindImportObjectByName

func (t *TypePackageSet) FindImportObjectByName(name string) (types.Object, error)

func (*TypePackageSet) FindImportPath

func (t *TypePackageSet) FindImportPath(pkg string, typeName string) (path string, err error)

func (*TypePackageSet) FindObject

func (t *TypePackageSet) FindObject(name TypeName) types.Object

func (*TypePackageSet) FindObjectByName

func (t *TypePackageSet) FindObjectByName(name string) (types.Object, error)

func (*TypePackageSet) Import

func (t *TypePackageSet) Import(importPath string) (*types.Package, error)

Import a package using the default GOPATH/src folder. See go/types.Importer.

func (*TypePackageSet) ImportFrom

func (t *TypePackageSet) ImportFrom(importPath, srcDir string, mode types.ImportMode) (pkg *types.Package, err error)

ImportFrom returns the imported package for the given import path when imported by a package file located in dir. See go/types.ImporterFrom.

func (*TypePackageSet) ImportNamed

func (t *TypePackageSet) ImportNamed(named *types.Named) (*types.Package, error)

func (*TypePackageSet) LocalImportName

func (t *TypePackageSet) LocalImportName(name TypeName, relPkg string) (string, error)

func (*TypePackageSet) LocalPackage

func (t *TypePackageSet) LocalPackage(packageName string) (string, error)

func (*TypePackageSet) LocalPackageFromType

func (t *TypePackageSet) LocalPackageFromType(name TypeName) (string, error)

func (*TypePackageSet) MustFindImportObject

func (t *TypePackageSet) MustFindImportObject(name TypeName) types.Object

func (*TypePackageSet) MustFindImportObjectByName

func (t *TypePackageSet) MustFindImportObjectByName(name string) types.Object

func (*TypePackageSet) MustFindObject

func (t *TypePackageSet) MustFindObject(name TypeName) types.Object

func (*TypePackageSet) MustFindObjectByName

func (t *TypePackageSet) MustFindObjectByName(name string) types.Object

func (*TypePackageSet) ResolvePath

func (t *TypePackageSet) ResolvePath(path, srcDir string) (PackageKind, string, error)

func (*TypePackageSet) TypeDoc

func (t *TypePackageSet) TypeDoc(tn TypeName) (docstr string, err error)

TypeDoc returns the documentation for a type. It can not return documentation for a package-level const or var because it uses go/doc at the moment. That may change, but it will probably require a new method.

type TypeVisitor

type TypeVisitor interface {
	EnterStruct(WalkContext, StructInfo) error
	LeaveStruct(WalkContext, StructInfo) error

	EnterField(ctx WalkContext, s StructInfo, field *types.Var, tag string) error
	LeaveField(ctx WalkContext, s StructInfo, field *types.Var, tag string) error

	EnterMapKey(ctx WalkContext, ft *types.Map, key types.Type) error
	LeaveMapKey(ctx WalkContext, ft *types.Map, key types.Type) error

	EnterMapElem(ctx WalkContext, ft *types.Map, elem types.Type) error
	LeaveMapElem(ctx WalkContext, ft *types.Map, elem types.Type) error

	EnterPointer(ctx WalkContext, ft *types.Pointer) error
	LeavePointer(ctx WalkContext, ft *types.Pointer) error

	EnterSlice(ctx WalkContext, ft *types.Slice) error
	LeaveSlice(ctx WalkContext, ft *types.Slice) error

	EnterArray(ctx WalkContext, ft *types.Array) error
	LeaveArray(ctx WalkContext, ft *types.Array) error

	VisitBasic(ctx WalkContext, t *types.Basic) error
	VisitNamed(ctx WalkContext, t *types.Named) error
	VisitInvalid(ctx WalkContext, root TypeName, t *types.Basic) error
	VisitInterface(ctx WalkContext, t *types.Interface) error
}

type WalkContext

type WalkContext interface {
	Stack() []types.Type
	Parent() types.Type
}

Directories

Path Synopsis
testpkg
doc

Jump to

Keyboard shortcuts

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