2024-03-18 03:23:27 +01:00
# CORE FRAMEWORK SOURCE FILE
# Copyright (c) 2024 The StarOpenSource Project & Contributors
# Licensed under the GNU Affero General Public License v3
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
extends CoreBaseModule
2024-04-08 20:14:38 +02:00
## Easy config management.
##
## Allows you to read and write configuration files with ease, without any headaches.
## Indicates if a storage file is currently open.
## Danger: Don't modify this.
2024-03-18 03:23:27 +01:00
var is_open : bool = false
2024-04-08 20:14:38 +02:00
## The parsed data inside the storage file.
## Danger: Don't modify this.
2024-03-18 03:23:27 +01:00
var storage : Dictionary = { }
2024-04-08 20:14:38 +02:00
## The location of the storage file.
## Danger: Don't modify this.
2024-03-18 03:23:27 +01:00
var storage_location : String = " "
2024-04-08 20:14:38 +02:00
# +++ file management +++
## Opens a storage file into memory.
2024-03-18 03:23:27 +01:00
func open_storage ( location : String , create_new : bool = true , sanity_check : bool = true , fail_on_sanity_check : bool = false ) - > bool :
if is_open :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to open storage: A storage file is already open " )
2024-03-18 03:23:27 +01:00
return false
2024-03-31 18:01:57 +02:00
logger . verbf ( " storage " , " Opening storage file at \" " + location + " \" " )
2024-03-18 03:23:27 +01:00
var file : FileAccess
if ! FileAccess . file_exists ( location ) :
if create_new :
file = FileAccess . open ( location , FileAccess . WRITE )
if file == null :
2024-03-31 18:01:57 +02:00
await logger . crashf ( " storage " , " Could not open storage file at \" " + location + " \" : Failed with code " + str ( FileAccess . get_open_error ( ) ) )
2024-03-18 03:23:27 +01:00
return false
file . store_string ( " {} " )
file . close ( )
else :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to open storage: create_new is set to false " )
2024-03-18 03:23:27 +01:00
return false
file = FileAccess . open ( location , FileAccess . READ )
var storage_temp : Variant = file . get_as_text ( )
file . close ( )
storage_temp = JSON . parse_string ( storage_temp )
if typeof ( storage_temp ) != TYPE_DICTIONARY :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to open storage: Parsed storage file is of type " + str ( typeof ( storage_temp ) ) )
2024-03-18 03:23:27 +01:00
return false
if sanity_check :
var check_result : Array [ String ] = perform_sanity_check ( storage_temp )
if check_result . size ( ) != 0 :
if fail_on_sanity_check :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Sanity check failed (stopping): " )
2024-03-18 03:23:27 +01:00
for error in check_result :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " -> " + error )
2024-03-18 03:23:27 +01:00
return false
else :
2024-03-31 18:01:57 +02:00
logger . warnf ( " storage " , " Sanity check failed (continuing anyway): " )
2024-03-18 03:23:27 +01:00
for error in check_result :
2024-03-31 18:01:57 +02:00
logger . warnf ( " storage " , " -> " + error )
2024-03-18 03:23:27 +01:00
storage = storage_temp
storage_location = location
is_open = true
return true
2024-04-08 20:14:38 +02:00
## Closes the active storage file.
2024-03-18 03:23:27 +01:00
func close_storage ( ) - > bool :
if ! is_open :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to close storage: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
2024-03-31 18:01:57 +02:00
logger . verbf ( " storage " , " Closing storage file " )
2024-03-18 03:23:27 +01:00
storage = { }
is_open = false
return true
2024-04-08 20:14:38 +02:00
## Saves the active storage file to disk.
2024-03-18 03:23:27 +01:00
func save_storage ( ) - > bool :
if ! is_open :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to save storage: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
var file : FileAccess = FileAccess . open ( storage_location , FileAccess . WRITE )
if file == null :
2024-03-31 18:01:57 +02:00
await logger . crashf ( " storage " , " Could not open storage file at \" " + storage_location + " \" : Failed with code " + str ( FileAccess . get_open_error ( ) ) )
2024-03-18 03:23:27 +01:00
return false
2024-03-31 18:01:57 +02:00
logger . diagf ( " storage " , " Writing storage file to disk " )
2024-03-18 03:23:27 +01:00
file . store_string ( JSON . stringify ( storage ) )
file . close ( )
return true
2024-04-08 20:14:38 +02:00
# +++ config manipulation +++
## Removes all keys from the active storage file. The nuclear option basically.
2024-03-18 03:23:27 +01:00
func nuke_storage ( autosave : bool = true ) - > bool :
if ! is_open :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to nuke storage: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
2024-03-31 18:01:57 +02:00
logger . warnf ( " storage " , " Nuking storage " )
2024-03-18 03:23:27 +01:00
storage = { }
if autosave : save_storage ( )
return true
2024-04-08 20:14:38 +02:00
## Returns a storage key. Can also return a default value if unset.
2024-04-06 13:12:49 +02:00
func get_key ( key : String , default : Variant = null ) - > Variant :
2024-03-18 03:23:27 +01:00
if ! is_open :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to get key: No storage file is open " )
2024-03-18 03:23:27 +01:00
return NAN
2024-04-06 13:12:49 +02:00
logger . diagf ( " storage " , " Returning storage key \" " + key + " \" (default= ' " + str ( default ) + " ' ) " )
2024-03-18 03:23:27 +01:00
return storage . get ( key , default )
2024-04-08 20:14:38 +02:00
## Updates a storage key with the specified value.
2024-03-18 03:23:27 +01:00
func set_key ( key : String , value : Variant , overwrite : bool = true , autosave : bool = true ) - > bool :
if ! is_open :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to set key: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
2024-03-31 18:01:57 +02:00
logger . diagf ( " storage " , " Updating storage key \" " + key + " \" with value ' " + str ( value ) + " ' (overwrite= ' " + str ( overwrite ) + " ' autosave= ' " + str ( autosave ) + " ' " )
2024-03-18 03:23:27 +01:00
storage . merge ( { key : value } , overwrite )
if autosave : save_storage ( )
return true
2024-04-08 20:14:38 +02:00
## Deletes a storage key.
2024-03-18 03:23:27 +01:00
func del_key ( key : String , autosave : bool = true ) - > bool :
if ! is_open :
2024-03-31 18:01:57 +02:00
logger . errof ( " storage " , " Failed to delete key: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
2024-03-31 18:01:57 +02:00
logger . diagf ( " storage " , " Deleting storage key \" " + key + " \" (autosave= ' " + str ( autosave ) + " ' ) " )
2024-03-18 03:23:27 +01:00
storage . erase ( key )
if autosave : save_storage ( )
return true
2024-04-08 20:14:38 +02:00
## Returns the [param storage] [class Dictionary], useful for more advanced operations.[br]
## Changes are not reflected onto the [param storage] though. To save changes through this module,[br]
## pass your modified [class Dictionary to [method safe_dict].
2024-03-18 03:23:27 +01:00
func get_dict ( ) - > Dictionary :
if ! is_open :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to get dictionary: No storage file is open " )
2024-03-18 03:23:27 +01:00
return { }
2024-03-31 18:01:57 +02:00
logger . verbf ( " storage " , " Returning storage dictionary " )
2024-03-18 03:23:27 +01:00
return storage
2024-04-08 20:14:38 +02:00
# +++ raw manipulation +++
## Saves a arbitrary dictionary as a [param storage] [class Dictionary] with sanity checking ([code]sanity_check[/code] and [code]fail_on_sanity_check[/code]).
2024-03-18 03:23:27 +01:00
func save_dict ( dict : Dictionary , sanity_check : bool = true , fail_on_sanity_check : bool = false , autosave : bool = true ) - > bool :
if ! is_open :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Failed to save dictionary: No storage file is open " )
2024-03-18 03:23:27 +01:00
return false
2024-03-31 18:01:57 +02:00
logger . verbf ( " storage " , " Saving custom dictionary as storage " )
2024-03-18 03:23:27 +01:00
if sanity_check :
var check_result : Array [ String ] = perform_sanity_check ( dict )
if check_result . size ( ) != 0 :
if fail_on_sanity_check :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " Sanity check failed (stopping): " )
2024-03-18 03:23:27 +01:00
for error in check_result :
2024-03-31 18:01:57 +02:00
logger . errorf ( " storage " , " -> " + error )
2024-03-18 03:23:27 +01:00
return false
else :
2024-03-31 18:01:57 +02:00
logger . warnf ( " storage " , " Sanity check failed (continuing anyway): " )
2024-03-18 03:23:27 +01:00
for error in check_result :
2024-03-31 18:01:57 +02:00
logger . warnf ( " storage " , " -> " + error )
2024-03-18 03:23:27 +01:00
storage = dict
if autosave : save_storage ( )
return true
2024-04-08 20:14:38 +02:00
# +++ etc +++
## Performs sanity checks on a [class Dictionary] to determine if it can be saved and loaded using this module.
2024-03-18 03:23:27 +01:00
func perform_sanity_check ( storage_check : Dictionary ) - > Array [ String ] :
2024-03-31 18:01:57 +02:00
logger . verbf ( " storage " , " Performing a sanity check on some storage dictionary " )
2024-03-18 03:23:27 +01:00
var errors : Array [ String ] = [ ]
for key in storage_check :
if typeof ( key ) != TYPE_STRING :
errors . append ( " Key \" " + str ( key ) + " \" is not of type String (type ' " + type_string ( typeof ( key ) ) + " ' ) " )
continue
if typeof ( storage_check [ key ] ) != TYPE_NIL and typeof ( storage_check [ key ] ) != TYPE_STRING and typeof ( storage_check [ key ] ) != TYPE_INT and typeof ( storage_check [ key ] ) != TYPE_FLOAT and typeof ( storage_check [ key ] ) != TYPE_BOOL and typeof ( storage_check [ key ] ) != TYPE_ARRAY and typeof ( storage_check [ key ] ) != TYPE_DICTIONARY :
errors . append ( " The value of \" " + key + " \" is not null, a string, an integer, a float, boolean, array or dictionary (type ' " + type_string ( typeof ( key ) ) + " ' ) " )
2024-03-31 18:01:57 +02:00
logger . verbf ( " storage " , " Completed sanity check with " + str ( errors . size ( ) ) + " errors " )
2024-03-18 03:23:27 +01:00
return errors