matchdef.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package nonnative
  2. import (
  3. "bytes"
  4. "embed"
  5. "encoding/json"
  6. "io/fs"
  7. "strings"
  8. "text/template"
  9. )
  10. //go:embed definitions/*
  11. var embeddedDefinitions embed.FS
  12. func NewDefMatcher() *DefMatcher {
  13. d := &DefMatcher{}
  14. d.init()
  15. return d
  16. }
  17. type DefMatcher struct {
  18. templates *template.Template
  19. }
  20. type ExecutionEnvironment struct {
  21. link AbstractNonNativeLink
  22. }
  23. func (d *DefMatcher) createFuncMap() template.FuncMap {
  24. return map[string]any{
  25. "assertExists": func(env *ExecutionEnvironment, names ...string) (bool, error) {
  26. link := env.link
  27. for _, v := range names {
  28. _, ok := link.Values[v]
  29. if !ok {
  30. return false, newError("failed assertExists of ", v)
  31. }
  32. }
  33. return true, nil
  34. },
  35. "assertIsOneOf": func(env *ExecutionEnvironment, name string, values ...string) (bool, error) {
  36. link := env.link
  37. actualValue, ok := link.Values[name]
  38. if !ok {
  39. return false, newError("failed assertIs of non-exist ", name)
  40. }
  41. found := false
  42. for _, currentValue := range values {
  43. if currentValue == actualValue {
  44. found = true
  45. break
  46. }
  47. }
  48. if !found {
  49. return false, newError("failed assertIsOneOf name = ", actualValue, "is not one of ", values)
  50. }
  51. return true, nil
  52. },
  53. "assertValueIsOneOf": func(value string, values ...string) (bool, error) {
  54. actualValue := value
  55. found := false
  56. for _, currentValue := range values {
  57. if currentValue == actualValue {
  58. found = true
  59. break
  60. }
  61. }
  62. if !found {
  63. return false, newError("failed assertIsOneOf name = ", actualValue, "is not one of ", values)
  64. }
  65. return true, nil
  66. },
  67. "tryGet": func(env *ExecutionEnvironment, names ...string) (string, error) {
  68. link := env.link
  69. for _, currentName := range names {
  70. value, ok := link.Values[currentName]
  71. if ok {
  72. return value, nil
  73. } else if currentName == "<default>" {
  74. return "", nil
  75. }
  76. }
  77. return "", newError("failed tryGet exists none of ", names)
  78. },
  79. "splitAndGetNth": func(sep string, n int, content string) (string, error) {
  80. result := strings.Split(content, sep)
  81. if n > len(result)-1 {
  82. return "", newError("failed splitAndGetNth exists too short content:", content, "n = ", n, "sep =", sep)
  83. }
  84. if n < 0 {
  85. n = len(result) + n
  86. if n < 0 {
  87. return "", newError("failed splitAndGetNth exists too short content:", content, "n = ", n, "sep =", sep)
  88. }
  89. }
  90. return result[n], nil
  91. },
  92. "splitAndGetAfterNth": func(sep string, n int, content string) ([]string, error) {
  93. result := strings.Split(content, sep)
  94. if n < 0 {
  95. n = len(result) + n
  96. }
  97. if n > len(result)-1 {
  98. return []string{}, newError("failed splitAndGetNth exists too short content:", content)
  99. }
  100. return result[n:], nil
  101. },
  102. "splitAndGetBeforeNth": func(sep string, n int, content string) ([]string, error) {
  103. result := strings.Split(content, sep)
  104. if n < 0 {
  105. n = len(result) + n
  106. }
  107. if n > len(result)-1 {
  108. return []string{}, newError("failed splitAndGetNth exists too short content:", content)
  109. }
  110. return result[:n], nil
  111. },
  112. "jsonEncode": func(content any) (string, error) {
  113. buf := bytes.NewBuffer(nil)
  114. err := json.NewEncoder(buf).Encode(content)
  115. if err != nil {
  116. return "", newError("unable to jsonQuote ", content).Base(err)
  117. }
  118. return buf.String(), nil
  119. },
  120. "stringCutSuffix": func(suffix, content string) (string, error) {
  121. remaining, found := strings.CutSuffix(content, suffix)
  122. if !found {
  123. return "", newError("suffix not found in content =", suffix, " suffix =", suffix)
  124. }
  125. return remaining, nil
  126. },
  127. "unalias": func(standardName string, names ...string) (string, error) {
  128. if len(names) == 0 {
  129. return "", newError("no input value specified")
  130. }
  131. actualInput := names[len(names)-1]
  132. alias := names[:len(names)-1]
  133. for _, v := range alias {
  134. if v == actualInput {
  135. return standardName, nil
  136. }
  137. }
  138. return actualInput, nil
  139. },
  140. }
  141. }
  142. func (d *DefMatcher) init() {
  143. d.templates = template.New("root").Funcs(d.createFuncMap())
  144. }
  145. func (d *DefMatcher) LoadEmbeddedDefinitions() error {
  146. return d.LoadDefinitions(embeddedDefinitions)
  147. }
  148. func (d *DefMatcher) LoadDefinitions(fs fs.FS) error {
  149. var err error
  150. d.templates, err = d.templates.ParseFS(fs, "definitions/*.jsont")
  151. if err != nil {
  152. return err
  153. }
  154. return nil
  155. }
  156. func (d *DefMatcher) ExecuteNamed(link AbstractNonNativeLink, name string) ([]byte, error) {
  157. outputBuffer := bytes.NewBuffer(nil)
  158. env := &ExecutionEnvironment{link: link}
  159. err := d.templates.ExecuteTemplate(outputBuffer, name, env)
  160. if err != nil {
  161. return nil, newError("failed to execute template").Base(err)
  162. }
  163. return outputBuffer.Bytes(), nil
  164. }
  165. func (d *DefMatcher) ExecuteAll(link AbstractNonNativeLink) ([]byte, error) {
  166. outputBuffer := bytes.NewBuffer(nil)
  167. for _, loadedTemplates := range d.templates.Templates() {
  168. env := &ExecutionEnvironment{link: link}
  169. err := loadedTemplates.Execute(outputBuffer, env)
  170. if err != nil {
  171. outputBuffer.Reset()
  172. } else {
  173. break
  174. }
  175. }
  176. if outputBuffer.Len() == 0 {
  177. return nil, newError("failed to find a working template")
  178. }
  179. return outputBuffer.Bytes(), nil
  180. }