344 lines
9.2 KiB
Go
344 lines
9.2 KiB
Go
|
|
// myMapList provides functions for managing user map lists
|
|
// Map lists are concurrency safe, and are stored in-memory and on disk.
|
|
// Examples of map lists include saved contacts, my chat messages, and my chat conversations
|
|
|
|
package myMapList
|
|
|
|
import "seekia/internal/localFilesystem"
|
|
import "seekia/internal/helpers"
|
|
import "seekia/internal/appMemory"
|
|
|
|
import "encoding/json"
|
|
import "path/filepath"
|
|
import "errors"
|
|
import "sync"
|
|
import "maps"
|
|
|
|
|
|
func InitializeMyMapListsFolder()error{
|
|
|
|
userDirectory, err := localFilesystem.GetAppUserFolderPath()
|
|
if (err != nil) { return err }
|
|
|
|
myMapListsFolderpath := filepath.Join(userDirectory, "MyMapLists")
|
|
|
|
_, err = localFilesystem.CreateFolder(myMapListsFolderpath)
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
|
|
type MyMapList struct{
|
|
|
|
// Name of map list. The file name is derived from this name.
|
|
mapListName string
|
|
|
|
// Folderpath of the mapList file
|
|
mapListFolderpath string
|
|
|
|
// We lock this whenever we are editing the memory map and map file
|
|
updatingMapListMutex sync.Mutex
|
|
|
|
// We lock this whenever we edit the memoryMapList
|
|
// memoryMutex only needs to be RLock/RUnlocked by GetMapList/GetMapListItems, because updatingMapListMutex is locked for all other reads
|
|
memoryMutex sync.RWMutex
|
|
|
|
// The map list stored in memory
|
|
memoryMapList []map[string]string
|
|
}
|
|
|
|
func CreateNewMapList(newMapListName string)(*MyMapList, error){
|
|
|
|
if (newMapListName == ""){
|
|
return nil, errors.New("CreateNewMapList called with empty myMapList name")
|
|
}
|
|
|
|
var newMapList MyMapList
|
|
|
|
newMapList.mapListName = newMapListName
|
|
|
|
userDirectory, err := localFilesystem.GetAppUserFolderPath()
|
|
if (err != nil) { return nil, err }
|
|
|
|
mapListFolderpath := filepath.Join(userDirectory, "MyMapLists")
|
|
|
|
newMapList.mapListFolderpath = mapListFolderpath
|
|
|
|
getMapListFromFile := func()([]map[string]string, error){
|
|
|
|
mapListFilepath := filepath.Join(mapListFolderpath, newMapListName + "MapList.json")
|
|
|
|
fileExists, fileBytes, err := localFilesystem.GetFileContents(mapListFilepath)
|
|
if (err != nil) { return nil, err }
|
|
if (fileExists == false){
|
|
emptyMapList := make([]map[string]string, 0)
|
|
return emptyMapList, nil
|
|
}
|
|
|
|
currentMapList := make([]map[string]string, 0)
|
|
|
|
err = json.Unmarshal(fileBytes, ¤tMapList)
|
|
if (err != nil) {
|
|
return nil, errors.New("My Map list is corrupted: " + newMapListName)
|
|
}
|
|
|
|
return currentMapList, nil
|
|
}
|
|
|
|
mapListFromFile, err := getMapListFromFile()
|
|
if (err != nil) { return nil, err }
|
|
|
|
newMapList.memoryMapList = mapListFromFile
|
|
|
|
return &newMapList, nil
|
|
}
|
|
|
|
|
|
//Outputs:
|
|
// -[]map[string]string
|
|
// -error
|
|
func (inputMapListObject *MyMapList) GetMapList()([]map[string]string, error){
|
|
|
|
if (inputMapListObject == nil){
|
|
return nil, errors.New("GetMapList called when MyMapList object is not initialized.")
|
|
}
|
|
|
|
exists, _ := appMemory.GetMemoryEntry("AppUser")
|
|
if (exists == false){
|
|
return nil, errors.New("GetMapList called when no user is signed in.")
|
|
}
|
|
|
|
inputMapListObject.memoryMutex.RLock()
|
|
|
|
currentMapList := inputMapListObject.memoryMapList
|
|
|
|
mapListCopy := helpers.DeepCopyStringToStringMapList(currentMapList)
|
|
|
|
inputMapListObject.memoryMutex.RUnlock()
|
|
|
|
return mapListCopy, nil
|
|
}
|
|
|
|
// Note: Added items are not copied
|
|
func (inputMapListObject *MyMapList) AddMapListItem(newItem map[string]string)error{
|
|
|
|
if (inputMapListObject == nil){
|
|
return errors.New("AddMapListItem called when MyMapList object is not initialized.")
|
|
}
|
|
|
|
exists, _ := appMemory.GetMemoryEntry("AppUser")
|
|
if (exists == false){
|
|
return errors.New("AddMapListItem called when no user is signed in.")
|
|
}
|
|
|
|
inputMapListObject.updatingMapListMutex.Lock()
|
|
defer inputMapListObject.updatingMapListMutex.Unlock()
|
|
|
|
inputMapListObject.memoryMutex.Lock()
|
|
|
|
currentMapList := inputMapListObject.memoryMapList
|
|
currentMapList = append(currentMapList, newItem)
|
|
inputMapListObject.memoryMapList = currentMapList
|
|
|
|
inputMapListObject.memoryMutex.Unlock()
|
|
|
|
mapListFolderpath := inputMapListObject.mapListFolderpath
|
|
mapListName := inputMapListObject.mapListName
|
|
|
|
err := overwriteMapListFileWithMapList(mapListFolderpath, mapListName, currentMapList)
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
|
|
// This can be used to delete multiple entries at a time.
|
|
// Any maps that contain the same entries as the input map will be deleted
|
|
func (inputMapListObject *MyMapList) DeleteMapListItems(mapItem map[string]string)error{
|
|
|
|
if (inputMapListObject == nil){
|
|
return errors.New("DeleteMapListItems called when MyMapList object is not initialized.")
|
|
}
|
|
|
|
exists, _ := appMemory.GetMemoryEntry("AppUser")
|
|
if (exists == false){
|
|
return errors.New("DeleteMapListItems called when no user is signed in.")
|
|
}
|
|
|
|
inputMapListObject.updatingMapListMutex.Lock()
|
|
defer inputMapListObject.updatingMapListMutex.Unlock()
|
|
|
|
currentMapList := inputMapListObject.memoryMapList
|
|
|
|
if (len(currentMapList) == 0){
|
|
return nil
|
|
}
|
|
|
|
newMapList := make([]map[string]string, 0)
|
|
|
|
anyItemDeleted := false
|
|
|
|
for _, element := range currentMapList{
|
|
|
|
areEqual := checkIfMapContainsMapEntries(mapItem, element)
|
|
if (areEqual == false){
|
|
newMapList = append(newMapList, element)
|
|
continue
|
|
}
|
|
|
|
anyItemDeleted = true
|
|
}
|
|
|
|
if (anyItemDeleted == false){
|
|
// No items were deleted. Nothing left to do.
|
|
return nil
|
|
}
|
|
|
|
inputMapListObject.memoryMutex.Lock()
|
|
inputMapListObject.memoryMapList = newMapList
|
|
inputMapListObject.memoryMutex.Unlock()
|
|
|
|
mapListFolderpath := inputMapListObject.mapListFolderpath
|
|
mapListName := inputMapListObject.mapListName
|
|
|
|
err := overwriteMapListFileWithMapList(mapListFolderpath, mapListName, newMapList)
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
|
|
|
|
//Outputs:
|
|
// bool: Any item was found
|
|
// []map[string]string: list of matching items
|
|
// error
|
|
func (inputMapListObject *MyMapList) GetMapListItems(lookupItem map[string]string)(bool, []map[string]string, error){
|
|
|
|
if (inputMapListObject == nil){
|
|
return false, nil, errors.New("GetMapListItems called when MyMapList object is not initialized.")
|
|
}
|
|
|
|
exists, _ := appMemory.GetMemoryEntry("AppUser")
|
|
if (exists == false){
|
|
return false, nil, errors.New("GetMapListItems called when no user is signed in.")
|
|
}
|
|
|
|
inputMapListObject.memoryMutex.RLock()
|
|
currentMapList := inputMapListObject.memoryMapList
|
|
|
|
matchingItemsMapList := make([]map[string]string, 0)
|
|
|
|
for _, element := range currentMapList{
|
|
|
|
areEqual := checkIfMapContainsMapEntries(lookupItem, element)
|
|
if (areEqual == true){
|
|
|
|
matchingItemCopy := maps.Clone(element)
|
|
|
|
matchingItemsMapList = append(matchingItemsMapList, matchingItemCopy)
|
|
}
|
|
|
|
}
|
|
inputMapListObject.memoryMutex.RUnlock()
|
|
|
|
if (len(matchingItemsMapList) == 0) {
|
|
|
|
return false, matchingItemsMapList, nil
|
|
}
|
|
|
|
return true, matchingItemsMapList, nil
|
|
}
|
|
|
|
// Note: Input map list is not copied
|
|
func (inputMapListObject *MyMapList) OverwriteMapList(newMapList []map[string]string) error{
|
|
|
|
if (inputMapListObject == nil){
|
|
return errors.New("OverwriteMapList called when MyMapList object is not initialized.")
|
|
}
|
|
|
|
exists, _ := appMemory.GetMemoryEntry("AppUser")
|
|
if (exists == false){
|
|
return errors.New("OverwriteMapList called when no user is signed in.")
|
|
}
|
|
|
|
inputMapListObject.updatingMapListMutex.Lock()
|
|
defer inputMapListObject.updatingMapListMutex.Unlock()
|
|
|
|
inputMapListObject.memoryMutex.Lock()
|
|
inputMapListObject.memoryMapList = newMapList
|
|
inputMapListObject.memoryMutex.Unlock()
|
|
|
|
mapListFolderpath := inputMapListObject.mapListFolderpath
|
|
mapListName := inputMapListObject.mapListName
|
|
|
|
err := overwriteMapListFileWithMapList(mapListFolderpath, mapListName, newMapList)
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
|
|
func (inputMapListObject *MyMapList) DeleteMapList() error{
|
|
|
|
if (inputMapListObject == nil){
|
|
return errors.New("DeleteMapList called when MyMapList object is not initialized.")
|
|
}
|
|
|
|
exists, _ := appMemory.GetMemoryEntry("AppUser")
|
|
if (exists == false){
|
|
return errors.New("DeleteMapList called when no user is signed in.")
|
|
}
|
|
|
|
inputMapListObject.updatingMapListMutex.Lock()
|
|
defer inputMapListObject.updatingMapListMutex.Unlock()
|
|
|
|
emptyMapList := make([]map[string]string, 0)
|
|
|
|
inputMapListObject.memoryMutex.Lock()
|
|
inputMapListObject.memoryMapList = emptyMapList
|
|
inputMapListObject.memoryMutex.Unlock()
|
|
|
|
mapListFolderpath := inputMapListObject.mapListFolderpath
|
|
mapListName := inputMapListObject.mapListName
|
|
|
|
err := overwriteMapListFileWithMapList(mapListFolderpath, mapListName, emptyMapList)
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
|
|
// We use this function to find matching items to delete/retrieve
|
|
// Map2 must contain all of map1's entries.
|
|
// If any map2 entries do not match match1 entries, return false
|
|
// If map1 contains more entries than map2, return false
|
|
// Map2 is allowed to contain more entries than map1
|
|
func checkIfMapContainsMapEntries(map1 map[string]string, map2 map[string]string) bool {
|
|
|
|
for key, value := range map1 {
|
|
|
|
currentValue, exists := map2[key]
|
|
if (exists == false) {
|
|
return false
|
|
}
|
|
|
|
if (value != currentValue) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func overwriteMapListFileWithMapList(mapListFolderpath string, mapListName string, inputMapList []map[string]string)error{
|
|
|
|
fileContents, err := json.MarshalIndent(inputMapList, "", "\t")
|
|
if (err != nil) { return err }
|
|
|
|
mapListFileName := mapListName + "MapList.json"
|
|
|
|
err = localFilesystem.CreateOrOverwriteFile(fileContents, mapListFolderpath, mapListFileName)
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
|
|
|