entityfiles.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package entityfiles
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. config "github.com/OliveTin/OliveTin/internal/config"
  7. "github.com/OliveTin/OliveTin/internal/filehelper"
  8. sv "github.com/OliveTin/OliveTin/internal/stringvariables"
  9. log "github.com/sirupsen/logrus"
  10. "gopkg.in/yaml.v3"
  11. "math"
  12. "os"
  13. "path/filepath"
  14. "strings"
  15. )
  16. var (
  17. EntityChangedSender chan bool
  18. listeners []func()
  19. )
  20. func AddListener(l func()) {
  21. listeners = append(listeners, l)
  22. }
  23. func SetupEntityFileWatchers(cfg *config.Config) {
  24. configDir := cfg.GetDir()
  25. configDirVar := filepath.Join(configDir, "var") // for development purposes
  26. if _, err := os.Stat(configDirVar); err == nil {
  27. configDir = configDirVar
  28. }
  29. for entityIndex := range cfg.Entities { // #337 - iterate by key, not by value
  30. ef := cfg.Entities[entityIndex]
  31. p := ef.File
  32. if !filepath.IsAbs(p) {
  33. p = filepath.Join(configDir, p)
  34. log.WithFields(log.Fields{
  35. "entityFile": p,
  36. }).Debugf("Adding config dir to entity file path")
  37. }
  38. go filehelper.WatchFileWrite(p, func(filename string) {
  39. loadEntityFile(p, ef.Name)
  40. })
  41. loadEntityFile(p, ef.Name)
  42. }
  43. }
  44. func loadEntityFile(filename string, entityname string) {
  45. if strings.HasSuffix(filename, ".json") {
  46. loadEntityFileJson(filename, entityname)
  47. } else {
  48. loadEntityFileYaml(filename, entityname)
  49. }
  50. }
  51. func loadEntityFileJson(filename string, entityname string) {
  52. log.WithFields(log.Fields{
  53. "file": filename,
  54. "name": entityname,
  55. }).Infof("Loading entity file with JSON format")
  56. jfile, err := os.ReadFile(filename)
  57. if err != nil {
  58. log.Errorf("ReadIn: %v", err)
  59. return
  60. }
  61. data := make([]map[string]any, 0)
  62. decoder := json.NewDecoder(bytes.NewReader(jfile))
  63. for decoder.More() {
  64. d := make(map[string]any)
  65. err := decoder.Decode(&d)
  66. if err != nil {
  67. log.Errorf("%v", err)
  68. return
  69. }
  70. data = append(data, d)
  71. }
  72. updateSvFromFile(entityname, data)
  73. }
  74. func loadEntityFileYaml(filename string, entityname string) {
  75. log.WithFields(log.Fields{
  76. "file": filename,
  77. "name": entityname,
  78. }).Infof("Loading entity file with YAML format")
  79. yfile, err := os.ReadFile(filename)
  80. if err != nil {
  81. log.Errorf("ReadIn: %v", err)
  82. return
  83. }
  84. data := make([]map[string]any, 1)
  85. err = yaml.Unmarshal(yfile, &data)
  86. if err != nil {
  87. log.Errorf("Unmarshal: %v", err)
  88. }
  89. updateSvFromFile(entityname, data)
  90. }
  91. func updateSvFromFile(entityname string, data []map[string]any) {
  92. log.Debugf("updateSvFromFile: %+v", data)
  93. count := len(data)
  94. sv.RemoveKeysThatStartWith("entities." + entityname)
  95. // If the file is empty, it will be an array with one nil item.
  96. if count == 1 && data[0] == nil {
  97. sv.SetEntityCount(entityname, 0)
  98. } else {
  99. sv.SetEntityCount(entityname, count)
  100. for i, mapp := range data {
  101. prefix := "entities." + entityname + "." + fmt.Sprintf("%v", i)
  102. serializeValueToSv(prefix, mapp)
  103. }
  104. }
  105. for _, l := range listeners {
  106. l()
  107. }
  108. }
  109. //gocyclo:ignore
  110. func serializeValueToSv(prefix string, value any) {
  111. if m, ok := value.(map[string]any); ok { // if value is a map we need to flatten it
  112. serializeMapToSv(prefix, m)
  113. } else if s, ok := value.([]any); ok { // if value is a slice we need to flatten it
  114. serializeSliceToSv(prefix, s)
  115. } else if f, ok := value.(float64); ok {
  116. if canConvertToInt64(f) {
  117. s := int64(f)
  118. sv.Set(prefix, fmt.Sprintf("%d", s))
  119. } else {
  120. sv.Set(prefix, fmt.Sprintf("%f", f))
  121. }
  122. } else {
  123. sv.Set(prefix, fmt.Sprintf("%v", value))
  124. }
  125. }
  126. func canConvertToInt64(f float64) bool {
  127. return f >= math.MinInt64 && f <= math.MaxInt64 && f == math.Trunc(f)
  128. }
  129. func serializeMapToSv(prefix string, m map[string]any) {
  130. for k, v := range m {
  131. serializeValueToSv(prefix+"."+k, v)
  132. }
  133. }
  134. func serializeSliceToSv(prefix string, s []any) {
  135. sv.Set(prefix+".count", fmt.Sprintf("%v", len(s)))
  136. for i, v := range s {
  137. serializeValueToSv(prefix+"."+fmt.Sprintf("%v", i), v)
  138. }
  139. }