ipld-eth-server/vendor/github.com/dave/jennifer/jen/file.go

263 lines
7.3 KiB
Go
Raw Normal View History

package jen
import (
"bytes"
"fmt"
"regexp"
"strings"
)
// NewFile Creates a new file, with the specified package name.
func NewFile(packageName string) *File {
return &File{
Group: &Group{
multi: true,
},
name: packageName,
imports: map[string]importdef{},
hints: map[string]importdef{},
}
}
// NewFilePath creates a new file while specifying the package path - the
// package name is inferred from the path.
func NewFilePath(packagePath string) *File {
return &File{
Group: &Group{
multi: true,
},
name: guessAlias(packagePath),
path: packagePath,
imports: map[string]importdef{},
hints: map[string]importdef{},
}
}
// NewFilePathName creates a new file with the specified package path and name.
func NewFilePathName(packagePath, packageName string) *File {
return &File{
Group: &Group{
multi: true,
},
name: packageName,
path: packagePath,
imports: map[string]importdef{},
hints: map[string]importdef{},
}
}
// File represents a single source file. Package imports are managed
// automaticaly by File.
type File struct {
*Group
name string
path string
imports map[string]importdef
hints map[string]importdef
comments []string
headers []string
cgoPreamble []string
// If you're worried about generated package aliases conflicting with local variable names, you
// can set a prefix here. Package foo becomes {prefix}_foo.
PackagePrefix string
// CanonicalPath adds a canonical import path annotation to the package clause.
CanonicalPath string
}
// importdef is used to differentiate packages where we know the package name from packages where the
// import is aliased. If alias == false, then name is the actual package name, and the import will be
// rendered without an alias. If used == false, the import has not been used in code yet and should be
// excluded from the import block.
type importdef struct {
name string
alias bool
}
// HeaderComment adds a comment to the top of the file, above any package
// comments. A blank line is rendered below the header comments, ensuring
// header comments are not included in the package doc.
func (f *File) HeaderComment(comment string) {
f.headers = append(f.headers, comment)
}
// PackageComment adds a comment to the top of the file, above the package
// keyword.
func (f *File) PackageComment(comment string) {
f.comments = append(f.comments, comment)
}
// CgoPreamble adds a cgo preamble comment that is rendered directly before the "C" pseudo-package
// import.
func (f *File) CgoPreamble(comment string) {
f.cgoPreamble = append(f.cgoPreamble, comment)
}
// Anon adds an anonymous import.
func (f *File) Anon(paths ...string) {
for _, p := range paths {
f.imports[p] = importdef{name: "_", alias: true}
}
}
// ImportName provides the package name for a path. If specified, the alias will be omitted from the
// import block. This is optional. If not specified, a sensible package name is used based on the path
// and this is added as an alias in the import block.
func (f *File) ImportName(path, name string) {
f.hints[path] = importdef{name: name, alias: false}
}
// ImportNames allows multiple names to be imported as a map. Use the [gennames](gennames) command to
// automatically generate a go file containing a map of a selection of package names.
func (f *File) ImportNames(names map[string]string) {
for path, name := range names {
f.hints[path] = importdef{name: name, alias: false}
}
}
// ImportAlias provides the alias for a package path that should be used in the import block. A
// period can be used to force a dot-import.
func (f *File) ImportAlias(path, alias string) {
f.hints[path] = importdef{name: alias, alias: true}
}
func (f *File) isLocal(path string) bool {
return f.path == path
}
var reserved = []string{
/* keywords */
"break", "default", "func", "interface", "select", "case", "defer", "go", "map", "struct", "chan", "else", "goto", "package", "switch", "const", "fallthrough", "if", "range", "type", "continue", "for", "import", "return", "var",
/* predeclared */
"bool", "byte", "complex64", "complex128", "error", "float32", "float64", "int", "int8", "int16", "int32", "int64", "rune", "string", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "true", "false", "iota", "nil", "append", "cap", "close", "complex", "copy", "delete", "imag", "len", "make", "new", "panic", "print", "println", "real", "recover",
/* common variables */
"err",
}
func isReservedWord(alias string) bool {
for _, name := range reserved {
if alias == name {
return true
}
}
return false
}
func (f *File) isValidAlias(alias string) bool {
// multiple dot-imports are ok
if alias == "." {
return true
}
// the import alias is invalid if it's a reserved word
if isReservedWord(alias) {
return false
}
// the import alias is invalid if it's already been registered
for _, v := range f.imports {
if alias == v.name {
return false
}
}
return true
}
func (f *File) isDotImport(path string) bool {
if id, ok := f.hints[path]; ok {
return id.name == "." && id.alias
}
return false
}
func (f *File) register(path string) string {
if f.isLocal(path) {
// notest
// should never get here becasue in Qual the packageToken will be null,
// so render will never be called.
return ""
}
// if the path has been registered previously, simply return the name
def := f.imports[path]
if def.name != "" && def.name != "_" {
return def.name
}
// special case for "C" pseudo-package
if path == "C" {
f.imports["C"] = importdef{name: "C", alias: false}
return "C"
}
var name string
var alias bool
if hint := f.hints[path]; hint.name != "" {
// look up the path in the list of provided package names and aliases by ImportName / ImportAlias
name = hint.name
alias = hint.alias
} else if standardLibraryHints[path] != "" {
// look up the path in the list of standard library packages
name = standardLibraryHints[path]
alias = false
} else {
// if a hint is not found for the package, guess the alias from the package path
name = guessAlias(path)
alias = true
}
// If the name is invalid or has been registered already, make it unique by appending a number
unique := name
i := 0
for !f.isValidAlias(unique) {
i++
unique = fmt.Sprintf("%s%d", name, i)
}
// If we've changed the name to make it unique, it should definitely be an alias
if unique != name {
alias = true
}
// Only add a prefix if the name is an alias
if f.PackagePrefix != "" && alias {
unique = f.PackagePrefix + "_" + unique
}
// Register the eventual name
f.imports[path] = importdef{name: unique, alias: alias}
return unique
}
// GoString renders the File for testing. Any error will cause a panic.
func (f *File) GoString() string {
buf := &bytes.Buffer{}
if err := f.Render(buf); err != nil {
panic(err)
}
return buf.String()
}
func guessAlias(path string) string {
alias := path
if strings.HasSuffix(alias, "/") {
// training slashes are usually tolerated, so we can get rid of one if
// it exists
alias = alias[:len(alias)-1]
}
if strings.Contains(alias, "/") {
// if the path contains a "/", use the last part
alias = alias[strings.LastIndex(alias, "/")+1:]
}
// alias should be lower case
alias = strings.ToLower(alias)
// alias should now only contain alphanumerics
importsRegex := regexp.MustCompile(`[^a-z0-9]`)
alias = importsRegex.ReplaceAllString(alias, "")
return alias
}