gitea源码

vars.go 2.5KB

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