github.com/jepp2078/gqlgen@v0.7.2/codegen/templates/templates.go (about)

     1  //go:generate go run ./inliner/inliner.go
     2  
     3  package templates
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"sort"
    12  	"strconv"
    13  	"strings"
    14  	"text/template"
    15  	"unicode"
    16  
    17  	"github.com/99designs/gqlgen/internal/imports"
    18  
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  // this is done with a global because subtemplates currently get called in functions. Lets aim to remove this eventually.
    23  var CurrentImports *Imports
    24  
    25  func Run(name string, tpldata interface{}) (*bytes.Buffer, error) {
    26  	t := template.New("").Funcs(template.FuncMap{
    27  		"ucFirst":       ucFirst,
    28  		"lcFirst":       lcFirst,
    29  		"quote":         strconv.Quote,
    30  		"rawQuote":      rawQuote,
    31  		"toCamel":       ToCamel,
    32  		"dump":          dump,
    33  		"prefixLines":   prefixLines,
    34  		"reserveImport": CurrentImports.Reserve,
    35  		"lookupImport":  CurrentImports.Lookup,
    36  	})
    37  
    38  	for filename, data := range data {
    39  		_, err := t.New(filename).Parse(data)
    40  		if err != nil {
    41  			panic(err)
    42  		}
    43  	}
    44  
    45  	buf := &bytes.Buffer{}
    46  	err := t.Lookup(name).Execute(buf, tpldata)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	return buf, nil
    52  }
    53  
    54  func ucFirst(s string) string {
    55  	if s == "" {
    56  		return ""
    57  	}
    58  	r := []rune(s)
    59  	r[0] = unicode.ToUpper(r[0])
    60  	return string(r)
    61  }
    62  
    63  func lcFirst(s string) string {
    64  	if s == "" {
    65  		return ""
    66  	}
    67  
    68  	r := []rune(s)
    69  	r[0] = unicode.ToLower(r[0])
    70  	return string(r)
    71  }
    72  
    73  func isDelimiter(c rune) bool {
    74  	return c == '-' || c == '_' || unicode.IsSpace(c)
    75  }
    76  
    77  func ToCamel(s string) string {
    78  	buffer := make([]rune, 0, len(s))
    79  	upper := true
    80  	lastWasUpper := false
    81  
    82  	for _, c := range s {
    83  		if isDelimiter(c) {
    84  			upper = true
    85  			continue
    86  		}
    87  		if !lastWasUpper && unicode.IsUpper(c) {
    88  			upper = true
    89  		}
    90  
    91  		if upper {
    92  			buffer = append(buffer, unicode.ToUpper(c))
    93  		} else {
    94  			buffer = append(buffer, unicode.ToLower(c))
    95  		}
    96  		upper = false
    97  		lastWasUpper = unicode.IsUpper(c)
    98  	}
    99  
   100  	return string(buffer)
   101  }
   102  
   103  func rawQuote(s string) string {
   104  	return "`" + strings.Replace(s, "`", "`+\"`\"+`", -1) + "`"
   105  }
   106  
   107  func dump(val interface{}) string {
   108  	switch val := val.(type) {
   109  	case int:
   110  		return strconv.Itoa(val)
   111  	case int64:
   112  		return fmt.Sprintf("%d", val)
   113  	case float64:
   114  		return fmt.Sprintf("%f", val)
   115  	case string:
   116  		return strconv.Quote(val)
   117  	case bool:
   118  		return strconv.FormatBool(val)
   119  	case nil:
   120  		return "nil"
   121  	case []interface{}:
   122  		var parts []string
   123  		for _, part := range val {
   124  			parts = append(parts, dump(part))
   125  		}
   126  		return "[]interface{}{" + strings.Join(parts, ",") + "}"
   127  	case map[string]interface{}:
   128  		buf := bytes.Buffer{}
   129  		buf.WriteString("map[string]interface{}{")
   130  		var keys []string
   131  		for key := range val {
   132  			keys = append(keys, key)
   133  		}
   134  		sort.Strings(keys)
   135  
   136  		for _, key := range keys {
   137  			data := val[key]
   138  
   139  			buf.WriteString(strconv.Quote(key))
   140  			buf.WriteString(":")
   141  			buf.WriteString(dump(data))
   142  			buf.WriteString(",")
   143  		}
   144  		buf.WriteString("}")
   145  		return buf.String()
   146  	default:
   147  		panic(fmt.Errorf("unsupported type %T", val))
   148  	}
   149  }
   150  
   151  func prefixLines(prefix, s string) string {
   152  	return prefix + strings.Replace(s, "\n", "\n"+prefix, -1)
   153  }
   154  
   155  func RenderToFile(tpl string, filename string, data interface{}) error {
   156  	if CurrentImports != nil {
   157  		panic(fmt.Errorf("recursive or concurrent call to RenderToFile detected"))
   158  	}
   159  	CurrentImports = &Imports{destDir: filepath.Dir(filename)}
   160  
   161  	var buf *bytes.Buffer
   162  	buf, err := Run(tpl, data)
   163  	if err != nil {
   164  		return errors.Wrap(err, filename+" generation failed")
   165  	}
   166  
   167  	b := bytes.Replace(buf.Bytes(), []byte("%%%IMPORTS%%%"), []byte(CurrentImports.String()), -1)
   168  	CurrentImports = nil
   169  
   170  	return write(filename, b)
   171  }
   172  
   173  func write(filename string, b []byte) error {
   174  	err := os.MkdirAll(filepath.Dir(filename), 0755)
   175  	if err != nil {
   176  		return errors.Wrap(err, "failed to create directory")
   177  	}
   178  
   179  	formatted, err := imports.Prune(filename, b)
   180  	if err != nil {
   181  		fmt.Fprintf(os.Stderr, "gofmt failed on %s: %s\n", filepath.Base(filename), err.Error())
   182  		formatted = b
   183  	}
   184  
   185  	err = ioutil.WriteFile(filename, formatted, 0644)
   186  	if err != nil {
   187  		return errors.Wrapf(err, "failed to write %s", filename)
   188  	}
   189  
   190  	return nil
   191  }