expand.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package csstring
  2. func seekClosingBracket(s string, i int) int {
  3. for ; i < len(s); i++ {
  4. if s[i] == '}' {
  5. return i
  6. }
  7. }
  8. return -1
  9. }
  10. func seekEndVarname(s string, i int) int {
  11. // envvar names are more strict but this is good enough
  12. for ; i < len(s); i++ {
  13. if (s[i] < 'a' || s[i] > 'z') && (s[i] < 'A' || s[i] > 'Z') && (s[i] < '0' || s[i] > '9') && s[i] != '_' {
  14. break
  15. }
  16. }
  17. return i
  18. }
  19. func replaceVarBracket(s string, i int, mapping func(string) (string, bool)) string {
  20. j := seekClosingBracket(s, i+2)
  21. if j < 0 {
  22. return s
  23. }
  24. if j < len(s) {
  25. varName := s[i+2 : j]
  26. if val, ok := mapping(varName); ok {
  27. s = s[:i] + val + s[j+1:]
  28. }
  29. }
  30. return s
  31. }
  32. func replaceVar(s string, i int, mapping func(string) (string, bool)) string {
  33. if s[i+1] == '{' {
  34. return replaceVarBracket(s, i, mapping)
  35. }
  36. j := seekEndVarname(s, i+1)
  37. if j < 0 {
  38. return s
  39. }
  40. if j > i+1 {
  41. varName := s[i+1 : j]
  42. if val, ok := mapping(varName); ok {
  43. s = s[:i] + val + s[j:]
  44. }
  45. }
  46. return s
  47. }
  48. // StrictExpand replaces ${var} or $var in the string according to the mapping
  49. // function, like os.Expand. The difference is that the mapping function
  50. // returns a boolean indicating whether the variable was found.
  51. // If the variable was not found, the string is not modified.
  52. //
  53. // Whereas os.ExpandEnv uses os.Getenv, here we can use os.LookupEnv
  54. // to distinguish between an empty variable and an undefined one.
  55. func StrictExpand(s string, mapping func(string) (string, bool)) string {
  56. for i := 0; i < len(s); i++ {
  57. if s[i] == '$' {
  58. s = replaceVar(s, i, mapping)
  59. }
  60. }
  61. return s
  62. }