// Copyright 2022 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package main import ( "bytes" "errors" "flag" "fmt" "go/types" "os" "golang.org/x/tools/go/packages" ) const pathOfPackageRLP = "github.com/ethereum/go-ethereum/rlp" func main() { var ( pkgdir = flag.String("dir", ".", "input package") output = flag.String("out", "-", "output file (default is stdout)") genEncoder = flag.Bool("encoder", true, "generate EncodeRLP?") genDecoder = flag.Bool("decoder", false, "generate DecodeRLP?") typename = flag.String("type", "", "type to generate methods for") ) flag.Parse() cfg := Config{ Dir: *pkgdir, Type: *typename, GenerateEncoder: *genEncoder, GenerateDecoder: *genDecoder, } code, err := cfg.process() if err != nil { fatal(err) } if *output == "-" { os.Stdout.Write(code) } else if err := os.WriteFile(*output, code, 0600); err != nil { fatal(err) } } func fatal(args ...interface{}) { fmt.Fprintln(os.Stderr, args...) os.Exit(1) } type Config struct { Dir string // input package directory Type string GenerateEncoder bool GenerateDecoder bool } // process generates the Go code. func (cfg *Config) process() (code []byte, err error) { // Load packages. pcfg := &packages.Config{ Mode: packages.NeedName | packages.NeedTypes, Dir: cfg.Dir, } ps, err := packages.Load(pcfg, pathOfPackageRLP, ".") if err != nil { return nil, err } if len(ps) == 0 { return nil, fmt.Errorf("no Go package found in %s", cfg.Dir) } packages.PrintErrors(ps) // Find the packages that were loaded. var ( pkg *types.Package packageRLP *types.Package ) for _, p := range ps { if len(p.Errors) > 0 { return nil, fmt.Errorf("package %s has errors", p.PkgPath) } if p.PkgPath == pathOfPackageRLP { packageRLP = p.Types } else { pkg = p.Types } } bctx := newBuildContext(packageRLP) // Find the type and generate. typ, err := lookupStructType(pkg.Scope(), cfg.Type) if err != nil { return nil, fmt.Errorf("can't find %s in %s: %v", cfg.Type, pkg, err) } code, err = bctx.generate(typ, cfg.GenerateEncoder, cfg.GenerateDecoder) if err != nil { return nil, err } // Add build comments. // This is done here to avoid processing these lines with gofmt. var header bytes.Buffer fmt.Fprint(&header, "// Code generated by rlpgen. DO NOT EDIT.\n\n") return append(header.Bytes(), code...), nil } func lookupStructType(scope *types.Scope, name string) (*types.Named, error) { typ, err := lookupType(scope, name) if err != nil { return nil, err } _, ok := typ.Underlying().(*types.Struct) if !ok { return nil, errors.New("not a struct type") } return typ, nil } func lookupType(scope *types.Scope, name string) (*types.Named, error) { obj := scope.Lookup(name) if obj == nil { return nil, errors.New("no such identifier") } typ, ok := obj.(*types.TypeName) if !ok { return nil, errors.New("not a type") } return typ.Type().(*types.Named), nil }