Skip to content
Snippets Groups Projects
Commit 22c46f9d authored by Jonah Husson's avatar Jonah Husson
Browse files

Put LocalStorage behind interface

parent e832bc49
No related branches found
No related tags found
2 merge requests!3Release,!2Project/base32768
This commit is part of merge request !2. Comments created here will be created in the context of that merge request.
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
) )
// localStorageWasmPrefix is prefixed to every keyName saved to local storage by // localStorageWasmPrefix is prefixed to every keyName saved to local storage by
// LocalStorage. It allows the identification and deletion of keys only created // localStorage. It allows the identification and deletion of keys only created
// by this WASM binary while ignoring keys made by other scripts on the same // by this WASM binary while ignoring keys made by other scripts on the same
// page. // page.
// //
...@@ -31,8 +31,22 @@ const localStorageWasmPrefix = "xxdkWasmStorage/" ...@@ -31,8 +31,22 @@ const localStorageWasmPrefix = "xxdkWasmStorage/"
// const localStorageWasmPrefix = "🞮🞮" // const localStorageWasmPrefix = "🞮🞮"
// LocalStorage contains the js.Value representation of localStorage. // LocalStorage defines an interface for setting persistent state in a KV format
type LocalStorage struct { // specifically for web-based implementations.
type LocalStorage interface {
Get(key string) ([]byte, error)
Set(key string, value []byte) error
RemoveItem(keyName string)
Clear() int
ClearPrefix(prefix string) int
Key(n int) (string, error)
Keys() []string
Length() int
LocalStorageUNSAFE() *LocalStorageJS
}
// localStorage contains the js.Value representation of localStorage.
type localStorage struct {
// The Javascript value containing the localStorage object // The Javascript value containing the localStorage object
v *LocalStorageJS v *LocalStorageJS
...@@ -45,24 +59,24 @@ type LocalStorage struct { ...@@ -45,24 +59,24 @@ type LocalStorage struct {
// jsStorage is the global that stores Javascript as window.localStorage. // jsStorage is the global that stores Javascript as window.localStorage.
// //
// Doc: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage // Doc: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
var jsStorage = newLocalStorage(localStorageWasmPrefix) var jsStorage LocalStorage = newLocalStorage(localStorageWasmPrefix)
// newLocalStorage creates a new LocalStorage object with the specified prefix. // newLocalStorage creates a new localStorage object with the specified prefix.
func newLocalStorage(prefix string) *LocalStorage { func newLocalStorage(prefix string) *localStorage {
return &LocalStorage{ return &localStorage{
v: &LocalStorageJS{js.Global().Get("localStorage")}, v: &LocalStorageJS{js.Global().Get("localStorage")},
prefix: prefix, prefix: prefix,
} }
} }
// GetLocalStorage returns Javascript's local storage. // GetLocalStorage returns Javascript's local storage.
func GetLocalStorage() *LocalStorage { func GetLocalStorage() LocalStorage {
return jsStorage return jsStorage
} }
// Get decodes and returns the value from the local storage given its key // Get decodes and returns the value from the local storage given its key
// name. Returns os.ErrNotExist if the key does not exist. // name. Returns os.ErrNotExist if the key does not exist.
func (ls *LocalStorage) Get(keyName string) ([]byte, error) { func (ls *localStorage) Get(keyName string) ([]byte, error) {
value, err := ls.v.GetItem(ls.prefix + keyName) value, err := ls.v.GetItem(ls.prefix + keyName)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -73,19 +87,19 @@ func (ls *LocalStorage) Get(keyName string) ([]byte, error) { ...@@ -73,19 +87,19 @@ func (ls *LocalStorage) Get(keyName string) ([]byte, error) {
// Set encodes the bytes to a string and adds them to local storage at the // Set encodes the bytes to a string and adds them to local storage at the
// given key name. Returns an error if local storage quota has been reached. // given key name. Returns an error if local storage quota has been reached.
func (ls *LocalStorage) Set(keyName string, keyValue []byte) error { func (ls *localStorage) Set(keyName string, keyValue []byte) error {
encoded := base32768.SafeEncoding.EncodeToString(keyValue) encoded := base32768.SafeEncoding.EncodeToString(keyValue)
return ls.v.SetItem(ls.prefix+keyName, encoded) return ls.v.SetItem(ls.prefix+keyName, encoded)
} }
// RemoveItem removes a key's value from local storage given its name. If there // RemoveItem removes a key's value from local storage given its name. If there
// is no item with the given key, this function does nothing. // is no item with the given key, this function does nothing.
func (ls *LocalStorage) RemoveItem(keyName string) { func (ls *localStorage) RemoveItem(keyName string) {
ls.v.RemoveItem(ls.prefix + keyName) ls.v.RemoveItem(ls.prefix + keyName)
} }
// Clear clears all the keys in storage. Returns the number of keys cleared. // Clear clears all the keys in storage. Returns the number of keys cleared.
func (ls *LocalStorage) Clear() int { func (ls *localStorage) Clear() int {
// Get a copy of all key names at once // Get a copy of all key names at once
keys := ls.v.KeysPrefix(ls.prefix) keys := ls.v.KeysPrefix(ls.prefix)
...@@ -99,7 +113,7 @@ func (ls *LocalStorage) Clear() int { ...@@ -99,7 +113,7 @@ func (ls *LocalStorage) Clear() int {
// ClearPrefix clears all keys with the given prefix. Returns the number of // ClearPrefix clears all keys with the given prefix. Returns the number of
// keys cleared. // keys cleared.
func (ls *LocalStorage) ClearPrefix(prefix string) int { func (ls *localStorage) ClearPrefix(prefix string) int {
// Get a copy of all key names at once // Get a copy of all key names at once
keys := ls.v.KeysPrefix(ls.prefix + prefix) keys := ls.v.KeysPrefix(ls.prefix + prefix)
...@@ -113,7 +127,7 @@ func (ls *LocalStorage) ClearPrefix(prefix string) int { ...@@ -113,7 +127,7 @@ func (ls *LocalStorage) ClearPrefix(prefix string) int {
// Key returns the name of the nth key in localStorage. Return [os.ErrNotExist] // Key returns the name of the nth key in localStorage. Return [os.ErrNotExist]
// if the key does not exist. The order of keys is not defined. // if the key does not exist. The order of keys is not defined.
func (ls *LocalStorage) Key(n int) (string, error) { func (ls *localStorage) Key(n int) (string, error) {
keyName, err := ls.v.Key(n) keyName, err := ls.v.Key(n)
if err != nil { if err != nil {
return "", err return "", err
...@@ -122,12 +136,12 @@ func (ls *LocalStorage) Key(n int) (string, error) { ...@@ -122,12 +136,12 @@ func (ls *LocalStorage) Key(n int) (string, error) {
} }
// Keys returns a list of all key names in local storage. // Keys returns a list of all key names in local storage.
func (ls *LocalStorage) Keys() []string { func (ls *localStorage) Keys() []string {
return ls.v.KeysPrefix(ls.prefix) return ls.v.KeysPrefix(ls.prefix)
} }
// Length returns the number of keys in localStorage. // Length returns the number of keys in localStorage.
func (ls *LocalStorage) Length() int { func (ls *localStorage) Length() int {
return ls.v.Length() return ls.v.Length()
} }
...@@ -139,7 +153,7 @@ func (ls *LocalStorage) Length() int { ...@@ -139,7 +153,7 @@ func (ls *LocalStorage) Length() int {
// decode/sanitize the inputs/outputs or track entries using the prefix system. // decode/sanitize the inputs/outputs or track entries using the prefix system.
// If using it, make sure all key names and values can be converted to valid // If using it, make sure all key names and values can be converted to valid
// UCS-2 strings. // UCS-2 strings.
func (ls *LocalStorage) LocalStorageUNSAFE() *LocalStorageJS { func (ls *localStorage) LocalStorageUNSAFE() *LocalStorageJS {
return ls.v return ls.v
} }
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
// Unit test of GetLocalStorage. // Unit test of GetLocalStorage.
func TestGetLocalStorage(t *testing.T) { func TestGetLocalStorage(t *testing.T) {
expected := &LocalStorage{ expected := &localStorage{
v: &LocalStorageJS{js.Global().Get("localStorage")}, v: &LocalStorageJS{js.Global().Get("localStorage")},
prefix: localStorageWasmPrefix, prefix: localStorageWasmPrefix,
} }
...@@ -29,13 +29,13 @@ func TestGetLocalStorage(t *testing.T) { ...@@ -29,13 +29,13 @@ func TestGetLocalStorage(t *testing.T) {
ls := GetLocalStorage() ls := GetLocalStorage()
if !reflect.DeepEqual(expected, ls) { if !reflect.DeepEqual(expected, ls) {
t.Errorf("Did not receive expected LocalStorage."+ t.Errorf("Did not receive expected localStorage."+
"\nexpected: %+v\nreceived: %+v", expected, ls) "\nexpected: %+v\nreceived: %+v", expected, ls)
} }
} }
// Tests that a value set with LocalStorage.Set and retrieved with // Tests that a value set with localStorage.Set and retrieved with
// LocalStorage.Get matches the original. // localStorage.Get matches the original.
func TestLocalStorage_Get_Set(t *testing.T) { func TestLocalStorage_Get_Set(t *testing.T) {
values := map[string][]byte{ values := map[string][]byte{
"key1": []byte("key value"), "key1": []byte("key value"),
...@@ -64,7 +64,7 @@ func TestLocalStorage_Get_Set(t *testing.T) { ...@@ -64,7 +64,7 @@ func TestLocalStorage_Get_Set(t *testing.T) {
} }
} }
// Tests that LocalStorage.Get returns the error os.ErrNotExist when the key // Tests that localStorage.Get returns the error os.ErrNotExist when the key
// does not exist in storage. // does not exist in storage.
func TestLocalStorage_Get_NotExistError(t *testing.T) { func TestLocalStorage_Get_NotExistError(t *testing.T) {
_, err := jsStorage.Get("someKey") _, err := jsStorage.Get("someKey")
...@@ -74,7 +74,7 @@ func TestLocalStorage_Get_NotExistError(t *testing.T) { ...@@ -74,7 +74,7 @@ func TestLocalStorage_Get_NotExistError(t *testing.T) {
} }
} }
// Tests that LocalStorage.RemoveItem deletes a key from the store and that it // Tests that localStorage.RemoveItem deletes a key from the store and that it
// cannot be retrieved. // cannot be retrieved.
func TestLocalStorage_RemoveItem(t *testing.T) { func TestLocalStorage_RemoveItem(t *testing.T) {
keyName := "key" keyName := "key"
...@@ -89,10 +89,10 @@ func TestLocalStorage_RemoveItem(t *testing.T) { ...@@ -89,10 +89,10 @@ func TestLocalStorage_RemoveItem(t *testing.T) {
} }
} }
// Tests that LocalStorage.Clear deletes all the WASM keys from storage and // Tests that localStorage.Clear deletes all the WASM keys from storage and
// does not remove any others // does not remove any others
func TestLocalStorage_Clear(t *testing.T) { func TestLocalStorage_Clear(t *testing.T) {
jsStorage.v.Clear() jsStorage.LocalStorageUNSAFE().Clear()
const numKeys = 10 const numKeys = 10
var yesPrefix, noPrefix []string var yesPrefix, noPrefix []string
...@@ -106,7 +106,7 @@ func TestLocalStorage_Clear(t *testing.T) { ...@@ -106,7 +106,7 @@ func TestLocalStorage_Clear(t *testing.T) {
} }
} else { } else {
noPrefix = append(noPrefix, keyName) noPrefix = append(noPrefix, keyName)
err := jsStorage.v.SetItem(keyName, strconv.Itoa(i)) err := jsStorage.LocalStorageUNSAFE().SetItem(keyName, strconv.Itoa(i))
if err != nil { if err != nil {
t.Errorf("Failed to set with no prefix %q: %+v", keyName, err) t.Errorf("Failed to set with no prefix %q: %+v", keyName, err)
} }
...@@ -120,7 +120,7 @@ func TestLocalStorage_Clear(t *testing.T) { ...@@ -120,7 +120,7 @@ func TestLocalStorage_Clear(t *testing.T) {
} }
for _, keyName := range noPrefix { for _, keyName := range noPrefix {
if _, err := jsStorage.v.GetItem(keyName); err != nil { if _, err := jsStorage.LocalStorageUNSAFE().GetItem(keyName); err != nil {
t.Errorf("Could not get keyName %q: %+v", keyName, err) t.Errorf("Could not get keyName %q: %+v", keyName, err)
} }
} }
...@@ -132,10 +132,10 @@ func TestLocalStorage_Clear(t *testing.T) { ...@@ -132,10 +132,10 @@ func TestLocalStorage_Clear(t *testing.T) {
} }
} }
// Tests that LocalStorage.ClearPrefix deletes only the keys with the given // Tests that localStorage.ClearPrefix deletes only the keys with the given
// prefix. // prefix.
func TestLocalStorage_ClearPrefix(t *testing.T) { func TestLocalStorage_ClearPrefix(t *testing.T) {
jsStorage.v.Clear() jsStorage.LocalStorageUNSAFE().Clear()
const numKeys = 10 const numKeys = 10
var yesPrefix, noPrefix []string var yesPrefix, noPrefix []string
prefix := "keyNamePrefix/" prefix := "keyNamePrefix/"
...@@ -173,10 +173,10 @@ func TestLocalStorage_ClearPrefix(t *testing.T) { ...@@ -173,10 +173,10 @@ func TestLocalStorage_ClearPrefix(t *testing.T) {
} }
} }
// Tests that LocalStorage.Key return all added keys when looping through all // Tests that localStorage.Key return all added keys when looping through all
// indexes. // indexes.
func TestLocalStorage_Key(t *testing.T) { func TestLocalStorage_Key(t *testing.T) {
jsStorage.v.Clear() jsStorage.LocalStorageUNSAFE().Clear()
values := map[string][]byte{ values := map[string][]byte{
"key1": []byte("key value"), "key1": []byte("key value"),
"key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, "key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
...@@ -207,10 +207,10 @@ func TestLocalStorage_Key(t *testing.T) { ...@@ -207,10 +207,10 @@ func TestLocalStorage_Key(t *testing.T) {
} }
} }
// Tests that LocalStorage.Key returns the error os.ErrNotExist when the index // Tests that localStorage.Key returns the error os.ErrNotExist when the index
// is greater than or equal to the number of keys. // is greater than or equal to the number of keys.
func TestLocalStorage_Key_NotExistError(t *testing.T) { func TestLocalStorage_Key_NotExistError(t *testing.T) {
jsStorage.v.Clear() jsStorage.LocalStorageUNSAFE().Clear()
if err := jsStorage.Set("key", []byte("value")); err != nil { if err := jsStorage.Set("key", []byte("value")); err != nil {
t.Errorf("Failed to set: %+v", err) t.Errorf("Failed to set: %+v", err)
} }
...@@ -228,10 +228,10 @@ func TestLocalStorage_Key_NotExistError(t *testing.T) { ...@@ -228,10 +228,10 @@ func TestLocalStorage_Key_NotExistError(t *testing.T) {
} }
} }
// Tests that LocalStorage.Length returns the correct Length when adding and // Tests that localStorage.Length returns the correct Length when adding and
// removing various keys. // removing various keys.
func TestLocalStorage_Length(t *testing.T) { func TestLocalStorage_Length(t *testing.T) {
jsStorage.v.Clear() jsStorage.LocalStorageUNSAFE().Clear()
values := map[string][]byte{ values := map[string][]byte{
"key1": []byte("key value"), "key1": []byte("key value"),
"key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, "key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
...@@ -263,9 +263,9 @@ func TestLocalStorage_Length(t *testing.T) { ...@@ -263,9 +263,9 @@ func TestLocalStorage_Length(t *testing.T) {
} }
} }
// Tests that LocalStorage.Keys return a list that contains all the added keys. // Tests that localStorage.Keys return a list that contains all the added keys.
func TestLocalStorage_Keys(t *testing.T) { func TestLocalStorage_Keys(t *testing.T) {
jsStorage.v.Clear() jsStorage.LocalStorageUNSAFE().Clear()
values := map[string][]byte{ values := map[string][]byte{
"key1": []byte("key value"), "key1": []byte("key value"),
"key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, "key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment