From 26b5343e2160de172969e6834074cf8482ceb845 Mon Sep 17 00:00:00 2001 From: Amine Hilaly Date: Tue, 9 Apr 2019 21:45:24 +0200 Subject: Update Gopkg.* --- .../gqlgen/codegen/templates/templates.go | 452 ++++++++++++++++++--- 1 file changed, 402 insertions(+), 50 deletions(-) (limited to 'vendor/github.com/99designs/gqlgen/codegen/templates/templates.go') diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go b/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go index 22e5d739..4c292732 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go @@ -1,13 +1,14 @@ -//go:generate go run ./inliner/inliner.go - package templates import ( "bytes" "fmt" + "go/types" "io/ioutil" "os" "path/filepath" + "reflect" + "runtime" "sort" "strconv" "strings" @@ -15,40 +16,141 @@ import ( "unicode" "github.com/99designs/gqlgen/internal/imports" - "github.com/pkg/errors" ) // this is done with a global because subtemplates currently get called in functions. Lets aim to remove this eventually. var CurrentImports *Imports -func Run(name string, tpldata interface{}) (*bytes.Buffer, error) { - t := template.New("").Funcs(template.FuncMap{ - "ucFirst": ucFirst, - "lcFirst": lcFirst, - "quote": strconv.Quote, - "rawQuote": rawQuote, - "toCamel": ToCamel, - "dump": dump, - "prefixLines": prefixLines, - "reserveImport": CurrentImports.Reserve, - "lookupImport": CurrentImports.Lookup, +type Options struct { + PackageName string + Filename string + RegionTags bool + GeneratedHeader bool + Data interface{} + Funcs template.FuncMap +} + +func Render(cfg Options) error { + if CurrentImports != nil { + panic(fmt.Errorf("recursive or concurrent call to RenderToFile detected")) + } + CurrentImports = &Imports{destDir: filepath.Dir(cfg.Filename)} + + // load path relative to calling source file + _, callerFile, _, _ := runtime.Caller(1) + rootDir := filepath.Dir(callerFile) + + funcs := Funcs() + for n, f := range cfg.Funcs { + funcs[n] = f + } + t := template.New("").Funcs(funcs) + + var roots []string + // load all the templates in the directory + err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + name := filepath.ToSlash(strings.TrimPrefix(path, rootDir+string(os.PathSeparator))) + if !strings.HasSuffix(info.Name(), ".gotpl") { + return nil + } + b, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + t, err = t.New(name).Parse(string(b)) + if err != nil { + return errors.Wrap(err, cfg.Filename) + } + + roots = append(roots, name) + + return nil }) + if err != nil { + return errors.Wrap(err, "locating templates") + } - for filename, data := range data { - _, err := t.New(filename).Parse(data) + // then execute all the important looking ones in order, adding them to the same file + sort.Slice(roots, func(i, j int) bool { + // important files go first + if strings.HasSuffix(roots[i], "!.gotpl") { + return true + } + if strings.HasSuffix(roots[j], "!.gotpl") { + return false + } + return roots[i] < roots[j] + }) + var buf bytes.Buffer + for _, root := range roots { + if cfg.RegionTags { + buf.WriteString("\n// region " + center(70, "*", " "+root+" ") + "\n") + } + err = t.Lookup(root).Execute(&buf, cfg.Data) if err != nil { - panic(err) + return errors.Wrap(err, root) + } + if cfg.RegionTags { + buf.WriteString("\n// endregion " + center(70, "*", " "+root+" ") + "\n") } } - buf := &bytes.Buffer{} - err := t.Lookup(name).Execute(buf, tpldata) + var result bytes.Buffer + if cfg.GeneratedHeader { + result.WriteString("// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\n") + } + result.WriteString("package ") + result.WriteString(cfg.PackageName) + result.WriteString("\n\n") + result.WriteString("import (\n") + result.WriteString(CurrentImports.String()) + result.WriteString(")\n") + _, err = buf.WriteTo(&result) if err != nil { - return nil, err + return err } + CurrentImports = nil - return buf, nil + return write(cfg.Filename, result.Bytes()) +} + +func center(width int, pad string, s string) string { + if len(s)+2 > width { + return s + } + lpad := (width - len(s)) / 2 + rpad := width - (lpad + len(s)) + return strings.Repeat(pad, lpad) + s + strings.Repeat(pad, rpad) +} + +func Funcs() template.FuncMap { + return template.FuncMap{ + "ucFirst": ucFirst, + "lcFirst": lcFirst, + "quote": strconv.Quote, + "rawQuote": rawQuote, + "dump": Dump, + "ref": ref, + "ts": TypeIdentifier, + "call": Call, + "prefixLines": prefixLines, + "notNil": notNil, + "reserveImport": CurrentImports.Reserve, + "lookupImport": CurrentImports.Lookup, + "go": ToGo, + "goPrivate": ToGoPrivate, + "add": func(a, b int) int { + return a + b + }, + "render": func(filename string, tpldata interface{}) (*bytes.Buffer, error) { + return render(resolveName(filename, 0), tpldata) + }, + } } func ucFirst(s string) string { @@ -74,37 +176,276 @@ func isDelimiter(c rune) bool { return c == '-' || c == '_' || unicode.IsSpace(c) } -func ToCamel(s string) string { - buffer := make([]rune, 0, len(s)) - upper := true - lastWasUpper := false +func ref(p types.Type) string { + return CurrentImports.LookupType(p) +} - for _, c := range s { - if isDelimiter(c) { - upper = true +var pkgReplacer = strings.NewReplacer( + "/", "ᚋ", + ".", "ᚗ", + "-", "ᚑ", +) + +func TypeIdentifier(t types.Type) string { + res := "" + for { + switch it := t.(type) { + case *types.Pointer: + t.Underlying() + res += "ᚖ" + t = it.Elem() + case *types.Slice: + res += "ᚕ" + t = it.Elem() + case *types.Named: + res += pkgReplacer.Replace(it.Obj().Pkg().Path()) + res += "ᚐ" + res += it.Obj().Name() + return res + case *types.Basic: + res += it.Name() + return res + case *types.Map: + res += "map" + return res + case *types.Interface: + res += "interface" + return res + default: + panic(fmt.Errorf("unexpected type %T", it)) + } + } +} + +func Call(p *types.Func) string { + pkg := CurrentImports.Lookup(p.Pkg().Path()) + + if pkg != "" { + pkg += "." + } + + if p.Type() != nil { + // make sure the returned type is listed in our imports. + ref(p.Type().(*types.Signature).Results().At(0).Type()) + } + + return pkg + p.Name() +} + +func ToGo(name string) string { + runes := make([]rune, 0, len(name)) + + wordWalker(name, func(info *wordInfo) { + word := info.Word + if info.MatchCommonInitial { + word = strings.ToUpper(word) + } else if !info.HasCommonInitial { + if strings.ToUpper(word) == word || strings.ToLower(word) == word { + // FOO or foo → Foo + // FOo → FOo + word = ucFirst(strings.ToLower(word)) + } + } + runes = append(runes, []rune(word)...) + }) + + return string(runes) +} + +func ToGoPrivate(name string) string { + runes := make([]rune, 0, len(name)) + + first := true + wordWalker(name, func(info *wordInfo) { + word := info.Word + if first { + if strings.ToUpper(word) == word || strings.ToLower(word) == word { + // ID → id, CAMEL → camel + word = strings.ToLower(info.Word) + } else { + // ITicket → iTicket + word = lcFirst(info.Word) + } + first = false + } else if info.MatchCommonInitial { + word = strings.ToUpper(word) + } else if !info.HasCommonInitial { + word = ucFirst(strings.ToLower(word)) + } + runes = append(runes, []rune(word)...) + }) + + return sanitizeKeywords(string(runes)) +} + +type wordInfo struct { + Word string + MatchCommonInitial bool + HasCommonInitial bool +} + +// This function is based on the following code. +// https://github.com/golang/lint/blob/06c8688daad7faa9da5a0c2f163a3d14aac986ca/lint.go#L679 +func wordWalker(str string, f func(*wordInfo)) { + runes := []rune(str) + w, i := 0, 0 // index of start of word, scan + hasCommonInitial := false + for i+1 <= len(runes) { + eow := false // whether we hit the end of a word + if i+1 == len(runes) { + eow = true + } else if isDelimiter(runes[i+1]) { + // underscore; shift the remainder forward over any run of underscores + eow = true + n := 1 + for i+n+1 < len(runes) && isDelimiter(runes[i+n+1]) { + n++ + } + + // Leave at most one underscore if the underscore is between two digits + if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) { + n-- + } + + copy(runes[i+1:], runes[i+n+1:]) + runes = runes[:len(runes)-n] + } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) { + // lower->non-lower + eow = true + } + i++ + + // [w,i) is a word. + word := string(runes[w:i]) + if !eow && commonInitialisms[word] && !unicode.IsLower(runes[i]) { + // through + // split IDFoo → ID, Foo + // but URLs → URLs + } else if !eow { + if commonInitialisms[word] { + hasCommonInitial = true + } continue } - if !lastWasUpper && unicode.IsUpper(c) { - upper = true + + matchCommonInitial := false + if commonInitialisms[strings.ToUpper(word)] { + hasCommonInitial = true + matchCommonInitial = true } - if upper { - buffer = append(buffer, unicode.ToUpper(c)) - } else { - buffer = append(buffer, unicode.ToLower(c)) + f(&wordInfo{ + Word: word, + MatchCommonInitial: matchCommonInitial, + HasCommonInitial: hasCommonInitial, + }) + hasCommonInitial = false + w = i + } +} + +var keywords = []string{ + "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", + "_", +} + +// sanitizeKeywords prevents collisions with go keywords for arguments to resolver functions +func sanitizeKeywords(name string) string { + for _, k := range keywords { + if name == k { + return name + "Arg" } - upper = false - lastWasUpper = unicode.IsUpper(c) } + return name +} - return string(buffer) +// commonInitialisms is a set of common initialisms. +// Only add entries that are highly unlikely to be non-initialisms. +// For instance, "ID" is fine (Freudian code is rare), but "AND" is not. +var commonInitialisms = map[string]bool{ + "ACL": true, + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SQL": true, + "SSH": true, + "TCP": true, + "TLS": true, + "TTL": true, + "UDP": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XMPP": true, + "XSRF": true, + "XSS": true, } func rawQuote(s string) string { return "`" + strings.Replace(s, "`", "`+\"`\"+`", -1) + "`" } -func dump(val interface{}) string { +func notNil(field string, data interface{}) bool { + v := reflect.ValueOf(data) + + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return false + } + val := v.FieldByName(field) + + return val.IsValid() && !val.IsNil() +} + +func Dump(val interface{}) string { switch val := val.(type) { case int: return strconv.Itoa(val) @@ -121,7 +462,7 @@ func dump(val interface{}) string { case []interface{}: var parts []string for _, part := range val { - parts = append(parts, dump(part)) + parts = append(parts, Dump(part)) } return "[]interface{}{" + strings.Join(parts, ",") + "}" case map[string]interface{}: @@ -138,7 +479,7 @@ func dump(val interface{}) string { buf.WriteString(strconv.Quote(key)) buf.WriteString(":") - buf.WriteString(dump(data)) + buf.WriteString(Dump(data)) buf.WriteString(",") } buf.WriteString("}") @@ -152,22 +493,33 @@ func prefixLines(prefix, s string) string { return prefix + strings.Replace(s, "\n", "\n"+prefix, -1) } -func RenderToFile(tpl string, filename string, data interface{}) error { - if CurrentImports != nil { - panic(fmt.Errorf("recursive or concurrent call to RenderToFile detected")) +func resolveName(name string, skip int) string { + if name[0] == '.' { + // load path relative to calling source file + _, callerFile, _, _ := runtime.Caller(skip + 1) + return filepath.Join(filepath.Dir(callerFile), name[1:]) } - CurrentImports = &Imports{destDir: filepath.Dir(filename)} - var buf *bytes.Buffer - buf, err := Run(tpl, data) + // load path relative to this directory + _, callerFile, _, _ := runtime.Caller(0) + return filepath.Join(filepath.Dir(callerFile), name) +} + +func render(filename string, tpldata interface{}) (*bytes.Buffer, error) { + t := template.New("").Funcs(Funcs()) + + b, err := ioutil.ReadFile(filename) if err != nil { - return errors.Wrap(err, filename+" generation failed") + return nil, err } - b := bytes.Replace(buf.Bytes(), []byte("%%%IMPORTS%%%"), []byte(CurrentImports.String()), -1) - CurrentImports = nil + t, err = t.New(filepath.Base(filename)).Parse(string(b)) + if err != nil { + panic(err) + } - return write(filename, b) + buf := &bytes.Buffer{} + return buf, t.Execute(buf, tpldata) } func write(filename string, b []byte) error { -- cgit