github.com/Desuuuu/genqlient@v0.5.3/generate/imports.go (about) 1 package generate 2 3 import ( 4 "fmt" 5 "go/types" 6 "regexp" 7 "strconv" 8 "strings" 9 ) 10 11 func (g *generator) addImportFor(pkgPath string) (alias string) { 12 pkgName := pkgPath[strings.LastIndex(pkgPath, "/")+1:] 13 alias = pkgName 14 suffix := 2 15 for g.usedAliases[alias] { 16 alias = pkgName + strconv.Itoa(suffix) 17 suffix++ 18 } 19 20 g.imports[pkgPath] = alias 21 g.usedAliases[alias] = true 22 return alias 23 } 24 25 var _sliceOrMapPrefixRegexp = regexp.MustCompile(`^(\*|\[\d*\]|map\[string\])*`) 26 27 // ref takes a Go fully-qualified name, ensures that any necessary symbols are 28 // imported, and returns an appropriate reference. 29 // 30 // Ideally, we want to allow a reference to basically an arbitrary symbol. 31 // But that's very hard, because it might be quite complicated, like 32 // struct{ F []map[mypkg.K]otherpkg.V } 33 // Now in practice, using an unnamed struct is not a great idea, but we do 34 // want to allow as much as we can that encoding/json knows how to work 35 // with, since you would reasonably expect us to accept, say, 36 // map[string][]interface{}. So we allow: 37 // - any named type (mypkg.T) 38 // - any predeclared basic type (string, int, etc.) 39 // - interface{} 40 // - for any allowed type T, *T, []T, [N]T, and map[string]T 41 // which effectively excludes: 42 // - unnamed struct types 43 // - map[K]V where K is a named type wrapping string 44 // - any nonstandard spelling of those (interface {/* hi */}, 45 // map[ string ]T) 46 // (This is documented in docs/genqlient.yaml) 47 func (g *generator) ref(fullyQualifiedName string) (qualifiedName string, err error) { 48 errorMsg := `invalid type-name "%v" (%v); expected a builtin, ` + 49 `path/to/package.Name, interface{}, or a slice, map, or pointer of those` 50 51 if strings.Contains(fullyQualifiedName, " ") { 52 return "", errorf(nil, errorMsg, fullyQualifiedName, "contains spaces") 53 } 54 55 prefix := _sliceOrMapPrefixRegexp.FindString(fullyQualifiedName) 56 nameToImport := fullyQualifiedName[len(prefix):] 57 58 i := strings.LastIndex(nameToImport, ".") 59 if i == -1 { 60 if nameToImport != "interface{}" && types.Universe.Lookup(nameToImport) == nil { 61 return "", errorf(nil, errorMsg, fullyQualifiedName, 62 fmt.Sprintf(`unknown type-name "%v"`, nameToImport)) 63 } 64 return fullyQualifiedName, nil 65 } 66 67 pkgPath := nameToImport[:i] 68 localName := nameToImport[i+1:] 69 alias, ok := g.imports[pkgPath] 70 if !ok { 71 if g.importsLocked { 72 return "", errorf(nil, 73 `genqlient internal error: imports locked but package "%v" has not been imported`, pkgPath) 74 } 75 alias = g.addImportFor(pkgPath) 76 } 77 return prefix + alias + "." + localName, nil 78 } 79 80 // Returns the import-clause to use in the generated code. 81 func (g *generator) Imports() string { 82 g.importsLocked = true 83 if len(g.imports) == 0 { 84 return "" 85 } 86 87 var builder strings.Builder 88 builder.WriteString("import (\n") 89 for path, alias := range g.imports { 90 if path == alias || strings.HasSuffix(path, "/"+alias) { 91 builder.WriteString("\t" + strconv.Quote(path) + "\n") 92 } else { 93 builder.WriteString("\t" + alias + " " + strconv.Quote(path) + "\n") 94 } 95 } 96 builder.WriteString(")\n\n") 97 return builder.String() 98 }