github.com/coveo/gotemplate@v2.7.7+incompatible/template/func_table.go (about) 1 package template 2 3 import ( 4 "fmt" 5 "reflect" 6 "sort" 7 "strings" 8 "text/template" 9 10 "github.com/coveo/gotemplate/collections" 11 "github.com/fatih/color" 12 ) 13 14 // FuncInfo contains the information related to a function made available to go template. 15 type FuncInfo struct { 16 name string 17 function interface{} 18 group string 19 aliases []string 20 arguments []string 21 description string 22 in, out string 23 alias *FuncInfo 24 } 25 26 // Aliases returns the aliases related to the entry. 27 func (fi FuncInfo) Aliases() []string { return ifUndef(&fi, fi.alias).(*FuncInfo).aliases } 28 29 // Arguments returns the list of arguments that must be supplied to the function. 30 func (fi FuncInfo) Arguments() string { return fi.getArguments(false) } 31 32 // Description returns the description related to the entry. 33 func (fi FuncInfo) Description() string { return ifUndef(&fi, fi.alias).(*FuncInfo).description } 34 35 // Group returns the group name associated to the entry. 36 func (fi FuncInfo) Group() string { return ifUndef(&fi, fi.alias).(*FuncInfo).group } 37 38 // Name returns the name related to the entry. 39 func (fi FuncInfo) Name() string { return fi.name } 40 41 // Signature returns the function signature 42 func (fi FuncInfo) Signature() string { return fi.getSignature(false) } 43 44 // IsAlias indicates if the current function is an alias. 45 func (fi FuncInfo) IsAlias() bool { return fi.alias != nil } 46 47 // String returns the presentation of the FuncInfo entry. 48 func (fi FuncInfo) String() (result string) { 49 signature := fi.Signature() 50 if fi.alias != nil { 51 fi = *fi.alias 52 } 53 54 if fi.group != "" { 55 result += fmt.Sprintf(color.GreenString("Group: %s\n"), fi.group) 56 } 57 if fi.description != "" { 58 result += fmt.Sprintf(color.GreenString("%s\n"), fi.description) 59 } 60 return result + signature 61 } 62 63 // Signature returns the function signature 64 func (fi FuncInfo) getSignature(isMethod bool) string { 65 col := color.HiBlueString 66 name := fi.name 67 if fi.alias != nil { 68 fi = *fi.alias 69 col = color.HiBlackString 70 } 71 72 return fmt.Sprintf("%s(%s) %s", col(name), fi.getArguments(isMethod), color.HiBlackString(fi.Result())) 73 } 74 75 func (fi FuncInfo) getArguments(isMethod bool) string { 76 if fi.in != "" || fi.function == nil { 77 return fi.in 78 } 79 80 if fi.alias != nil { 81 fi = *fi.alias 82 } 83 84 signature := reflect.ValueOf(fi.function).Type() 85 var parameters []string 86 for i := iif(isMethod, 1, 0).(int); i < signature.NumIn(); i++ { 87 arg := strings.Replace(fmt.Sprint(signature.In(i)), "interface {}", "interface{}", -1) 88 arg = strings.Replace(arg, "collections.", "", -1) 89 var argName string 90 if i < len(fi.arguments) { 91 argName = fi.arguments[i] 92 } else { 93 if signature.IsVariadic() && i == signature.NumIn()-1 { 94 argName = "args" 95 } else { 96 argName = fmt.Sprintf("arg%d", i+1) 97 } 98 } 99 if signature.IsVariadic() && i == signature.NumIn()-1 { 100 arg = "..." + arg[2:] 101 } 102 if isMethod { 103 parameters = append(parameters, color.CyanString(arg)) 104 } else { 105 parameters = append(parameters, fmt.Sprintf("%s %s", argName, color.CyanString(arg))) 106 } 107 } 108 return strings.Join(parameters, ", ") 109 } 110 111 // Result returns the list of output produced by the function. 112 func (fi FuncInfo) Result() string { 113 if fi.out != "" || fi.function == nil { 114 return fi.out 115 } 116 signature := reflect.ValueOf(fi.function).Type() 117 var outputs []string 118 for i := 0; i < signature.NumOut(); i++ { 119 r := strings.Replace(fmt.Sprint(signature.Out(i)), "interface {}", "interface{}", -1) 120 r = strings.Replace(r, "collections.", "", -1) 121 outputs = append(outputs, r) 122 } 123 return strings.Join(outputs, ", ") 124 } 125 126 type funcTableMap map[string]FuncInfo 127 128 func (ftm funcTableMap) convert() template.FuncMap { 129 result := collections.CreateDictionary(len(ftm)) 130 for key, val := range ftm { 131 if val.function == nil { 132 continue 133 } 134 result.Set(key, val.function) 135 } 136 return result.AsMap() 137 } 138 139 // FuncOptionsSet defines categories that could be used to define elaborated help context when adding functions to go template. 140 type FuncOptionsSet int8 141 142 const ( 143 // FuncHelp is used to associate help to functions added to go templates. 144 FuncHelp FuncOptionsSet = iota 145 // FuncArgs is used to associate arguments name to functions added to go templates. 146 FuncArgs 147 // FuncAliases is used to associate aliases to functions added to go templates. 148 FuncAliases 149 // FuncGroup is used to associate a group to functions added to go templates. 150 FuncGroup 151 ) 152 153 // FuncOptions is a map of FuncOptionsSet that is used to associates help, aliases, arguments and groups to functions added to go template. 154 type FuncOptions map[FuncOptionsSet]interface{} 155 156 type aliases = map[string][]string 157 type arguments = map[string][]string 158 type descriptions = map[string]string 159 type dictionary = map[string]interface{} 160 type groups = map[string]string 161 162 // AddFunctions add functions to the template, but keep a detailled definition of the function added for helping purpose 163 func (t *Template) AddFunctions(funcs dictionary, group string, options FuncOptions) *Template { 164 ft := make(funcTableMap, len(funcs)) 165 help := defval(options[FuncHelp], descriptions{}).(descriptions) 166 aliases := defval(options[FuncAliases], aliases{}).(aliases) 167 arguments := defval(options[FuncArgs], arguments{}).(arguments) 168 groups := defval(options[FuncGroup], groups{}).(groups) 169 for key, val := range funcs { 170 ft[key] = FuncInfo{ 171 function: val, 172 group: defval(group, groups[key]).(string), 173 aliases: aliases[key], 174 arguments: arguments[key], 175 description: help[key], 176 } 177 } 178 179 return t.addFunctions(ft) 180 } 181 182 func (t *Template) addFunctions(funcMap funcTableMap) *Template { 183 templateMutex.Lock() 184 defer templateMutex.Unlock() 185 186 if t.functions == nil { 187 t.functions = make(funcTableMap) 188 } 189 for key, value := range funcMap { 190 value.name = key 191 t.functions[key] = value 192 for i := range value.aliases { 193 // It is necessary here to take a distinct copy of the variable since 194 // val will change over the iteration and we cannot rely on its address 195 copy := value 196 funcMap[value.aliases[i]] = FuncInfo{alias: ©, function: value.function, name: value.aliases[i]} 197 t.functions[value.aliases[i]] = funcMap[value.aliases[i]] 198 } 199 } 200 t.Funcs(funcMap.convert()) 201 return t 202 } 203 204 // List the available functions in the template 205 func (t Template) getFunctionsInternal(original, alias bool) (result []string) { 206 for name := range t.functions { 207 fi := t.functions[name] 208 if original && fi.alias == nil { 209 result = append(result, name) 210 } 211 if alias && fi.alias != nil { 212 result = append(result, name) 213 } 214 } 215 sort.Strings(result) 216 return 217 } 218 219 func (t Template) getAliases() []string { return t.getFunctionsInternal(false, true) } 220 func (t Template) getAllFunctions() []string { return t.getFunctionsInternal(true, true) } 221 func (t Template) getFunctions() []string { return t.getFunctionsInternal(true, false) } 222 223 // List the available functions in the template 224 func (t Template) getFunction(name string) FuncInfo { 225 return t.functions[name] 226 }