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  }