github.com/abdfnx/gh-api@v0.0.0-20210414084727-f5432eec23b8/pkg/cmd/api/template.go (about) 1 package api 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "math" 9 "strconv" 10 "strings" 11 "text/template" 12 "time" 13 14 "github.com/abdfnx/gh-api/utils" 15 "github.com/mgutz/ansi" 16 ) 17 18 func parseTemplate(tpl string, colorEnabled bool) (*template.Template, error) { 19 now := time.Now() 20 21 templateFuncs := map[string]interface{}{ 22 "color": templateColor, 23 "autocolor": templateColor, 24 25 "timefmt": func(format, input string) (string, error) { 26 t, err := time.Parse(time.RFC3339, input) 27 if err != nil { 28 return "", err 29 } 30 return t.Format(format), nil 31 }, 32 "timeago": func(input string) (string, error) { 33 t, err := time.Parse(time.RFC3339, input) 34 if err != nil { 35 return "", err 36 } 37 return timeAgo(now.Sub(t)), nil 38 }, 39 40 "pluck": templatePluck, 41 "join": templateJoin, 42 } 43 44 if !colorEnabled { 45 templateFuncs["autocolor"] = func(colorName string, input interface{}) (string, error) { 46 return jsonScalarToString(input) 47 } 48 } 49 50 return template.New("").Funcs(templateFuncs).Parse(tpl) 51 } 52 53 func executeTemplate(w io.Writer, input io.Reader, templateStr string, colorEnabled bool) error { 54 t, err := parseTemplate(templateStr, colorEnabled) 55 if err != nil { 56 return err 57 } 58 59 jsonData, err := ioutil.ReadAll(input) 60 if err != nil { 61 return err 62 } 63 64 var data interface{} 65 if err := json.Unmarshal(jsonData, &data); err != nil { 66 return err 67 } 68 69 return t.Execute(w, data) 70 } 71 72 func jsonScalarToString(input interface{}) (string, error) { 73 switch tt := input.(type) { 74 case string: 75 return tt, nil 76 case float64: 77 if math.Trunc(tt) == tt { 78 return strconv.FormatFloat(tt, 'f', 0, 64), nil 79 } else { 80 return strconv.FormatFloat(tt, 'f', 2, 64), nil 81 } 82 case nil: 83 return "", nil 84 case bool: 85 return fmt.Sprintf("%v", tt), nil 86 default: 87 return "", fmt.Errorf("cannot convert type to string: %v", tt) 88 } 89 } 90 91 func templateColor(colorName string, input interface{}) (string, error) { 92 text, err := jsonScalarToString(input) 93 if err != nil { 94 return "", err 95 } 96 return ansi.Color(text, colorName), nil 97 } 98 99 func templatePluck(field string, input []interface{}) []interface{} { 100 var results []interface{} 101 for _, item := range input { 102 obj := item.(map[string]interface{}) 103 results = append(results, obj[field]) 104 } 105 return results 106 } 107 108 func templateJoin(sep string, input []interface{}) (string, error) { 109 var results []string 110 for _, item := range input { 111 text, err := jsonScalarToString(item) 112 if err != nil { 113 return "", err 114 } 115 results = append(results, text) 116 } 117 return strings.Join(results, sep), nil 118 } 119 120 func timeAgo(ago time.Duration) string { 121 if ago < time.Minute { 122 return "just now" 123 } 124 if ago < time.Hour { 125 return utils.Pluralize(int(ago.Minutes()), "minute") + " ago" 126 } 127 if ago < 24*time.Hour { 128 return utils.Pluralize(int(ago.Hours()), "hour") + " ago" 129 } 130 if ago < 30*24*time.Hour { 131 return utils.Pluralize(int(ago.Hours())/24, "day") + " ago" 132 } 133 if ago < 365*24*time.Hour { 134 return utils.Pluralize(int(ago.Hours())/24/30, "month") + " ago" 135 } 136 return utils.Pluralize(int(ago.Hours()/24/365), "year") + " ago" 137 }