storage.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. package app
  2. import (
  3. "encoding/json"
  4. "sync"
  5. "github.com/maxence-charriere/go-app/v9/pkg/errors"
  6. )
  7. // BrowserStorage is the interface that describes a web browser storage.
  8. type BrowserStorage interface {
  9. // Set sets the value to the given key. The value must be json convertible.
  10. Set(k string, v any) error
  11. // Get gets the item associated to the given key and store it in the given
  12. // value.
  13. // It returns an error if v is not a pointer.
  14. Get(k string, v any) error
  15. // Del deletes the item associated with the given key.
  16. Del(k string)
  17. // Len returns the number of items stored.
  18. Len() int
  19. // Key returns the key of the item associated to the given index.
  20. Key(i int) (string, error)
  21. // Clear deletes all items.
  22. Clear()
  23. }
  24. type memoryStorage struct {
  25. mu sync.RWMutex
  26. data map[string][]byte
  27. }
  28. func newMemoryStorage() *memoryStorage {
  29. return &memoryStorage{
  30. data: make(map[string][]byte),
  31. }
  32. }
  33. func (s *memoryStorage) Set(k string, v any) error {
  34. b, err := json.Marshal(v)
  35. if err != nil {
  36. return err
  37. }
  38. s.mu.Lock()
  39. s.data[k] = b
  40. s.mu.Unlock()
  41. return nil
  42. }
  43. func (s *memoryStorage) Get(k string, v any) error {
  44. s.mu.RLock()
  45. d, ok := s.data[k]
  46. if !ok {
  47. s.mu.RUnlock()
  48. return nil
  49. }
  50. s.mu.RUnlock()
  51. return json.Unmarshal(d, v)
  52. }
  53. func (s *memoryStorage) Del(k string) {
  54. s.mu.Lock()
  55. delete(s.data, k)
  56. s.mu.Unlock()
  57. }
  58. func (s *memoryStorage) Clear() {
  59. s.mu.Lock()
  60. for k := range s.data {
  61. delete(s.data, k)
  62. }
  63. s.mu.Unlock()
  64. }
  65. func (s *memoryStorage) Len() int {
  66. s.mu.RLock()
  67. l := len(s.data)
  68. s.mu.RUnlock()
  69. return l
  70. }
  71. func (s *memoryStorage) Key(i int) (string, error) {
  72. s.mu.RLock()
  73. defer s.mu.RUnlock()
  74. j := 0
  75. for k := range s.data {
  76. if i == j {
  77. return k, nil
  78. }
  79. j++
  80. }
  81. return "", errors.New("index out of range").
  82. Tag("index", i).
  83. Tag("len", s.Len())
  84. }
  85. type jsStorage struct {
  86. name string
  87. mutex sync.RWMutex
  88. }
  89. func newJSStorage(name string) *jsStorage {
  90. return &jsStorage{name: name}
  91. }
  92. func (s *jsStorage) Set(k string, v any) (err error) {
  93. defer func() {
  94. r := recover()
  95. if r != nil {
  96. err = errors.New("setting storage value failed").
  97. Tag("storage-type", s.name).
  98. Tag("key", k).
  99. Wrap(r.(error))
  100. }
  101. }()
  102. s.mutex.Lock()
  103. defer s.mutex.Unlock()
  104. b, err := json.Marshal(v)
  105. if err != nil {
  106. return err
  107. }
  108. Window().Get(s.name).Call("setItem", k, string(b))
  109. return nil
  110. }
  111. func (s *jsStorage) Get(k string, v any) error {
  112. s.mutex.RLock()
  113. defer s.mutex.RUnlock()
  114. item := Window().Get(s.name).Call("getItem", k)
  115. if !item.Truthy() {
  116. return nil
  117. }
  118. return json.Unmarshal([]byte(item.String()), v)
  119. }
  120. func (s *jsStorage) Del(k string) {
  121. s.mutex.Lock()
  122. defer s.mutex.Unlock()
  123. Window().Get(s.name).Call("removeItem", k)
  124. }
  125. func (s *jsStorage) Clear() {
  126. s.mutex.Lock()
  127. defer s.mutex.Unlock()
  128. Window().Get(s.name).Call("clear")
  129. }
  130. func (s *jsStorage) Len() int {
  131. s.mutex.Lock()
  132. defer s.mutex.Unlock()
  133. return s.len()
  134. }
  135. func (s *jsStorage) len() int {
  136. return Window().Get(s.name).Get("length").Int()
  137. }
  138. func (s *jsStorage) Key(i int) (string, error) {
  139. if l := s.len(); i < 0 || i >= l {
  140. return "", errors.New("index out of range").
  141. Tag("index", i).
  142. Tag("len", l)
  143. }
  144. return Window().Get(s.name).Call("key", i).String(), nil
  145. }