github.com/docker-library/go-dockerlibrary@v0.0.0-20200821205225-669fbe5c1d52/pkg/templatelib/lib.go (about)

     1  package templatelib
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"reflect"
     8  	"strings"
     9  	"text/template"
    10  )
    11  
    12  func swapStringsFuncBoolArgsOrder(a func(string, string) bool) func(string, string) bool {
    13  	return func(str1 string, str2 string) bool {
    14  		return a(str2, str1)
    15  	}
    16  }
    17  
    18  func thingsActionFactory(name string, actOnFirst bool, action func([]interface{}, interface{}) interface{}) func(args ...interface{}) interface{} {
    19  	return func(args ...interface{}) interface{} {
    20  		if len(args) < 1 {
    21  			panic(fmt.Sprintf(`%q requires at least one argument`, name))
    22  		}
    23  
    24  		actArgs := []interface{}{}
    25  		for _, val := range args {
    26  			v := reflect.ValueOf(val)
    27  
    28  			switch v.Kind() {
    29  			case reflect.Slice, reflect.Array:
    30  				for i := 0; i < v.Len(); i++ {
    31  					actArgs = append(actArgs, v.Index(i).Interface())
    32  				}
    33  			default:
    34  				actArgs = append(actArgs, v.Interface())
    35  			}
    36  		}
    37  
    38  		var arg interface{}
    39  		if actOnFirst {
    40  			arg = actArgs[0]
    41  			actArgs = actArgs[1:]
    42  		} else {
    43  			arg = actArgs[len(actArgs)-1]
    44  			actArgs = actArgs[:len(actArgs)-1]
    45  		}
    46  
    47  		return action(actArgs, arg)
    48  	}
    49  }
    50  
    51  func stringsActionFactory(name string, actOnFirst bool, action func([]string, string) string) func(args ...interface{}) interface{} {
    52  	return thingsActionFactory(name, actOnFirst, func(args []interface{}, arg interface{}) interface{} {
    53  		str := arg.(string)
    54  		strs := []string{}
    55  		for _, val := range args {
    56  			strs = append(strs, val.(string))
    57  		}
    58  		return action(strs, str)
    59  	})
    60  }
    61  
    62  func stringsModifierActionFactory(a func(string, string) string) func([]string, string) string {
    63  	return func(strs []string, str string) string {
    64  		for _, mod := range strs {
    65  			str = a(str, mod)
    66  		}
    67  		return str
    68  	}
    69  }
    70  
    71  var FuncMap = template.FuncMap{
    72  	// {{- $isGitHub := hasPrefix "https://github.com/" $url -}}
    73  	// {{- $isHtml := hasSuffix ".html" $url -}}
    74  	"hasPrefix": swapStringsFuncBoolArgsOrder(strings.HasPrefix),
    75  	"hasSuffix": swapStringsFuncBoolArgsOrder(strings.HasSuffix),
    76  
    77  	// {{- $hugeIfTrue := .SomeValue | ternary "HUGE" "not so huge" -}}
    78  	// if .SomeValue is truthy, $hugeIfTrue will be "HUGE"
    79  	// (otherwise, "not so huge")
    80  	"ternary": func(truthy interface{}, falsey interface{}, val interface{}) interface{} {
    81  		if t, ok := template.IsTrue(val); !ok {
    82  			panic(fmt.Sprintf(`template.IsTrue(%+v) says things are NOT OK`, val))
    83  		} else if t {
    84  			return truthy
    85  		} else {
    86  			return falsey
    87  		}
    88  	},
    89  
    90  	// First Tag: {{- .Tags | first -}}
    91  	// Last Tag:  {{- .Tags | last -}}
    92  	"first": thingsActionFactory("first", true, func(args []interface{}, arg interface{}) interface{} { return arg }),
    93  	"last":  thingsActionFactory("last", false, func(args []interface{}, arg interface{}) interface{} { return arg }),
    94  
    95  	// JSON data dump: {{ json . }}
    96  	// (especially nice for taking data and piping it to "jq")
    97  	// (ie "some-tool inspect --format '{{ json . }}' some-things | jq .")
    98  	"json": func(v interface{}) (string, error) {
    99  		j, err := json.Marshal(v)
   100  		return string(j), err
   101  	},
   102  
   103  	// Everybody: {{- join ", " .Names -}}
   104  	// Concat: {{- join "/" "https://github.com" "jsmith" "some-repo" -}}
   105  	"join": stringsActionFactory("join", true, strings.Join),
   106  
   107  	// {{- $mungedUrl := $url | replace "git://" "https://" | trimSuffixes ".git" -}}
   108  	// turns: git://github.com/jsmith/some-repo.git
   109  	// into: https://github.com/jsmith/some-repo
   110  	"trimPrefixes": stringsActionFactory("trimPrefixes", false, stringsModifierActionFactory(strings.TrimPrefix)),
   111  	"trimSuffixes": stringsActionFactory("trimSuffixes", false, stringsModifierActionFactory(strings.TrimSuffix)),
   112  	"replace": stringsActionFactory("replace", false, func(strs []string, str string) string {
   113  		return strings.NewReplacer(strs...).Replace(str)
   114  	}),
   115  
   116  	// {{- getenv "PATH" -}}
   117  	// {{- getenv "HOME" "no HOME set" -}}
   118  	// {{- getenv "HOME" "is set" "is NOT set (or is empty)" -}}
   119  	"getenv": thingsActionFactory("getenv", true, func(args []interface{}, arg interface{}) interface{} {
   120  		var (
   121  			val                  = os.Getenv(arg.(string))
   122  			setVal   interface{} = val
   123  			unsetVal interface{} = ""
   124  		)
   125  		if len(args) == 2 {
   126  			setVal, unsetVal = args[0], args[1]
   127  		} else if len(args) == 1 {
   128  			unsetVal = args[0]
   129  		} else if len(args) != 0 {
   130  			panic(fmt.Sprintf(`expected between 1 and 3 arguments to "getenv", got %d`, len(args)+1))
   131  		}
   132  		if val != "" {
   133  			return setVal
   134  		} else {
   135  			return unsetVal
   136  		}
   137  	}),
   138  }