code.gitea.io/gitea@v1.19.3/modules/templates/vars/vars.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package vars 5 6 import ( 7 "fmt" 8 "strings" 9 "unicode" 10 "unicode/utf8" 11 ) 12 13 // ErrWrongSyntax represents a wrong syntax with a template 14 type ErrWrongSyntax struct { 15 Template string 16 } 17 18 func (err ErrWrongSyntax) Error() string { 19 return fmt.Sprintf("wrong syntax found in %s", err.Template) 20 } 21 22 // ErrVarMissing represents an error that no matched variable 23 type ErrVarMissing struct { 24 Template string 25 Var string 26 } 27 28 func (err ErrVarMissing) Error() string { 29 return fmt.Sprintf("the variable %s is missing for %s", err.Var, err.Template) 30 } 31 32 // Expand replaces all variables like {var} by `vars` map, it always returns the expanded string regardless of errors 33 // if error occurs, the error part doesn't change and is returned as it is. 34 func Expand(template string, vars map[string]string) (string, error) { 35 // in the future, if necessary, we can introduce some escape-char, 36 // for example: it will use `#' as a reversed char, templates will use `{#{}` to do escape and output char '{'. 37 var buf strings.Builder 38 var err error 39 40 posBegin := 0 41 strLen := len(template) 42 for posBegin < strLen { 43 // find the next `{` 44 pos := strings.IndexByte(template[posBegin:], '{') 45 if pos == -1 { 46 buf.WriteString(template[posBegin:]) 47 break 48 } 49 50 // copy texts between vars 51 buf.WriteString(template[posBegin : posBegin+pos]) 52 53 // find the var between `{` and `}`/end 54 posBegin += pos 55 posEnd := posBegin + 1 56 for posEnd < strLen { 57 if template[posEnd] == '}' { 58 posEnd++ 59 break 60 } // in the future, if we need to support escape chars, we can do: if (isEscapeChar) { posEnd+=2 } 61 posEnd++ 62 } 63 64 // the var part, it can be "{", "{}", "{..." or or "{...}" 65 part := template[posBegin:posEnd] 66 posBegin = posEnd 67 if part == "{}" || part[len(part)-1] != '}' { 68 // treat "{}" or "{..." as error 69 err = ErrWrongSyntax{Template: template} 70 buf.WriteString(part) 71 } else { 72 // now we get a valid key "{...}" 73 key := part[1 : len(part)-1] 74 keyFirst, _ := utf8.DecodeRuneInString(key) 75 if unicode.IsSpace(keyFirst) || unicode.IsPunct(keyFirst) || unicode.IsControl(keyFirst) { 76 // the if key doesn't start with a letter, then we do not treat it as a var now 77 buf.WriteString(part) 78 } else { 79 // look up in the map 80 if val, ok := vars[key]; ok { 81 buf.WriteString(val) 82 } else { 83 // write the non-existing var as it is 84 buf.WriteString(part) 85 err = ErrVarMissing{Template: template, Var: key} 86 } 87 } 88 } 89 } 90 91 return buf.String(), err 92 }