From caedc4b2da3c679a231c31e12f58a4b0fb961064 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Wed, 8 Oct 2025 13:04:36 +0200 Subject: [PATCH 01/30] Get the first draft up there --- README.md | 19 ++++- src/json_storage.vala | 179 ++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 3 +- 3 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 src/json_storage.vala diff --git a/README.md b/README.md index 0968cf7..4ce7cc1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,23 @@ # libfridge -Simple Vala App Storage Library +A simple, hassle-free storage library to keep your data fresh. +Ready out of the box, and comprehensive for vala newcomers. This library primarily contains the three following objects: + +- json_storage +- string_storage +- byte_storage + +all three are meant to represent a storage file saved on the disk +Simply declare: + +``` +var mystorage = new Fridge.json_storage(); +``` + +Or any variant depending what you want to store... And you are good to go! +Access mystorage.content, or assign it, to load and save. There are more options for more control, but the idea here is to set and forget. + + ## Build Instructions diff --git a/src/json_storage.vala b/src/json_storage.vala new file mode 100644 index 0000000..6998c2c --- /dev/null +++ b/src/json_storage.vala @@ -0,0 +1,179 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 Stella & Charlie (teamcons.carrd.co) + */ + +/** +* A library intended for basic storage, for beginner and whoever needs only hassle-free basics +* +* Json_storage represents a json file in the app's data folder +* You can use this class to store a Json.Array containing your objects (As Json.Node) +* You can optionally initialize it with with a file name +* +*/ +public class Fridge.json_storage : Object { + + /** + * This signal gets emitted when the content of the storage has been changed + ' This allows you to connect to your storage instance and trigger a function whenever there has been changes + */ + public signal void changed (); + + /** + * This signal gets emitted when there is an error while loading or saving, along with said error + * Connect to this if you wish to handle errors + */ + public signal void error (Error e); + + /** + * Whether to keep a duplicate of the storage content to access storage very quickly + * By default this is set to true. Cache is regenerated when saving, and when loading if empty and enabled + * You can force it to be reloaded by using empty_cache, then accessing the storage content + * This feature can be disabled anytime, but make sure to call empty_cache after disabling it to avoid keeping a stale cache in memory + */ + public bool keep_cache = true; + + /** + * A copy of the storage file content. + */ + private Json.Array? cache; + + /** + * The name of the file saved on disk. This can be set only upon creation + */ + string filename { public get; private set;}; + + /** + * The path of the directory where the storage file is saved + * This variable is not meant to be changed, and only as aid if the location is uncertain + */ + string data_directory { public get; private set;}; + + /** + * The full path of the storage file + * This variable is not meant to be changed, and only as aid if the location is uncertain + */ + string storage_path { public get; private set;}; + + /** + * Create a representation of a storage file. If there is no file, the storage is considered empty + * There is one optional parameters: + * + * name: the name of the file to save to and load from. By default it is simply "storage.json" + * + * the storage emits a changed() signal whenever + */ + public json_storage (string? name = "storage.json") { + Object (filename: name); + } + + /** + * Property representing the content of storage on disk + * You can save a Json.Array by invoking: + * + * yourstorageinstance.content = array; + * + * And load the content of the file by doing + * + * var array = yourstorageinstance.content; + * + * You can disable the cache via keep_cache = false + * You can connect handlers to the storage via the changed() signal + * If you expect errors to happen, connect a handler to error() signal + */ + public Json.Array content { + owned get { return load ();} + set { save (value);} + } + + /*************************************************/ + construct { + data_directory = Environment.get_user_data_dir (); + storage_path = data_directory + "/" + filename; + check_if_stash (); + } + + /*************************************************/ + /** + * Persistently check for the data directory and create if there is none + * Without this, we risk creating our storage in the void + */ + private void check_if_datadir () { + debug ("[STORAGE] do we have a data directory?"); + var dir = File.new_for_path (data_directory); + + try { + if (!dir.query_exists ()) { + dir.make_directory (); + debug ("[STORAGE] yes we do now"); + } + } catch (Error e) { + warning ("[STORAGE] Failed to prepare target data directory: %s\n", e.message); + } + } + + /*************************************************/ + /** + * Converts a Json.Node into a string and take care of saving it + */ + private void save (Json.Array? json_data) { + debug("[STORAGE] Writing..."); + check_if_datadir (); + + try { + var generator = new Json.Generator (); + var node = new Json.Node (Json.NodeType.ARRAY); + node.set_array (json_data); + generator.set_root (node); + generator.to_file (storage_path); + if (keep_cache) { cache = json_data;}; + changed (); + + } catch (Error e) { + warning ("[STORAGE] Failed to save to storage: %s", e.message); + error (e); + } + } + + /*************************************************/ + /** + * Grab from storage, into a Json.Node we can parse. Insist if necessary + * We simply return a copy of the cache in the event we track one and it isn't empty + * Should the storage be empty, and thus the cache as well, we still check on-disk + */ + private Json.Array? load () { + debug("[STORAGE] Loading from storage letsgo"); + check_if_datadir (); + + if (keep_cache && (cache != null)) { + return cache.copy (); + } + + var parser = new Json.Parser (); + var array? = new Json.Array (); + + try { + parser.load_from_mapped_file (storage_path); + var node = parser.get_root (); + array = node.get_array (); + if (keep_cache) { cache = json_data;}; + + } catch (Error e) { + warning ("[STORAGE] Failed to load from storage: " + e.message.to_string()); + error (e); + } + + return array; + } + + + /*************************************************/ + /** + * Drop everything. The next time the content property is accessed, it will be read from disk + * If keep_cache is set to true, a new cache will be generated + */ + private void empty_cache () { + debug("[STORAGE] Emptying cache"); + cache = null; + } +} diff --git a/src/meson.build b/src/meson.build index a7e95a3..46a9364 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,6 @@ sources = files([ - 'library.vala' + 'library.vala', + 'json_storage.vala' ]) libfridge = library( From d05b665f4dfe072dfde6ee817dc4cea4b9678409 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Wed, 8 Oct 2025 13:06:42 +0200 Subject: [PATCH 02/30] forgor meson dependency --- meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6bc626e..c76ae17 100644 --- a/meson.build +++ b/meson.build @@ -23,7 +23,8 @@ g_ir_compiler = find_program('g-ir-compiler', required: false) libfridge_deps = [ dependency('glib-2.0'), - dependency('gobject-2.0') + dependency('gobject-2.0'), + dependency('json-glib-1.0') ] subdir('src') From 77012e6592e583fd383584f55188b02befcb7dc1 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Wed, 8 Oct 2025 13:14:35 +0200 Subject: [PATCH 03/30] Add CI --- .github/workflows/ci.yml | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..75a5757 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + pull_request: + types: + - opened + - reopened + - synchronize + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + version: [stable, unstable, development-target] + container: + image: ghcr.io/elementary/docker:${{ matrix.version }} + + steps: + - uses: actions/checkout@v5 + - name: Install Dependencies + run: | + apt update + apt install -y meson valac libjson-glib-dev + - name: Build + run: | + meson setup build --prefix=/usr + ninja -C build + ninja -C build install + + lint: + runs-on: ubuntu-latest + + container: + image: valalang/lint + + steps: + - uses: actions/checkout@v5 + - name: Lint + run: io.elementary.vala-lint -d . \ No newline at end of file From 64b6e96b2b729ce9c1f5c07c6b4443f4bf89d274 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Wed, 8 Oct 2025 13:37:01 +0200 Subject: [PATCH 04/30] Make the json optional --- README.md | 28 ++++++++++++++++++++++++++++ meson.build | 2 +- meson_options.txt | 7 ++----- src/meson.build | 7 +++++-- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4ce7cc1..fe3b524 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,34 @@ To install libfridge: ninja install ``` + +## Add to your flatpak project + +Simply copy the in your manifest before your app sources: + +``` + - name: libfridge + buildsystem: meson + sources: + - type: git + url: https://github.com/vala-community/libfridge.git + tag: 0.0.1 + commit: [NOT DONE YET] + x-checker-data: + type: git + tag-pattern: '^([\d.]+)$' +``` + +By default json_storage is included. If you do not use it, and wish to skip having a json dependency, you can add immediately after the buildsystem line: + +``` + config-opts: + - -Denable_json=false +``` + + + + ## Documentation By default, documentation is built by default using [`valadoc`](https://docs.vala.dev/developer-guides/documentation/valadoc-guide.html) diff --git a/meson.build b/meson.build index c76ae17..166bad0 100644 --- a/meson.build +++ b/meson.build @@ -24,7 +24,7 @@ g_ir_compiler = find_program('g-ir-compiler', required: false) libfridge_deps = [ dependency('glib-2.0'), dependency('gobject-2.0'), - dependency('json-glib-1.0') + dependency('json-glib-1.0', required : get_option('enable_json')) ] subdir('src') diff --git a/meson_options.txt b/meson_options.txt index 3849cf5..22c692b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,5 +1,2 @@ -option( - 'enable_valadoc', - type: 'boolean', - value: true -) +option('enable_valadoc', type: 'boolean', value: true) +option('enable_json', type : 'boolean', value : true) \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 46a9364..9bdda59 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,8 +1,11 @@ sources = files([ - 'library.vala', - 'json_storage.vala' + 'library.vala' ]) +if get_option('enable_json') + sources += files(['json_storage']) +endif + libfridge = library( LIBRARY_NAME + '-' + API_VERSION, sources, From c7833f251989d44d870ccf58891f4a78134f0c91 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Wed, 8 Oct 2025 14:02:00 +0200 Subject: [PATCH 05/30] Comprehensive readme --- README.md | 56 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index fe3b524..f47a465 100644 --- a/README.md +++ b/README.md @@ -7,39 +7,34 @@ Ready out of the box, and comprehensive for vala newcomers. This library primari - string_storage - byte_storage -all three are meant to represent a storage file saved on the disk -Simply declare: +all three are meant to represent a storage file saved on the disk, and intended to contain a type of data. Simply declare: ``` var mystorage = new Fridge.json_storage(); ``` -Or any variant depending what you want to store... And you are good to go! -Access mystorage.content, or assign it, to load and save. There are more options for more control, but the idea here is to set and forget. - - - -## Build Instructions - -First, setup the build directory by running the following command in the project root: +Or any variant depending what you want to store... And you are good to go! +Save by doing: ``` -meson setup build --prefix=/usr +mystorage.content = thing_to_save; ``` -Now change to the `build` directory (`cd build`) for the following commands: - -Build libfridge: +Access it by doing: ``` -ninja +var thing_to_load = mystorage.content; ``` -To install libfridge: +Features: +- [x] Smart instancing: Each instance is its own distinct file +- [x] Cache: By default a cache lets you access content faster +- [x] Optional error handling: You can connect to the error() signal + + +Wishlist of features: +- [] More agressive trying to load/save in case of errors -``` -ninja install -``` ## Add to your flatpak project @@ -69,6 +64,29 @@ By default json_storage is included. If you do not use it, and wish to skip havi +## Build Instructions + +First, setup the build directory by running the following command in the project root: + +``` +meson setup build --prefix=/usr +``` + +Now change to the `build` directory (`cd build`) for the following commands: + +Build libfridge: + +``` +ninja +``` + +To install libfridge: + +``` +ninja install +``` + + ## Documentation By default, documentation is built by default using [`valadoc`](https://docs.vala.dev/developer-guides/documentation/valadoc-guide.html) From ca35a8d46daf957e09a9094b927de403b388da82 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Wed, 8 Oct 2025 16:18:03 +0200 Subject: [PATCH 06/30] I think this has to be an unowned, right? --- src/json_storage.vala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/json_storage.vala b/src/json_storage.vala index 6998c2c..3cf7e91 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -82,7 +82,7 @@ public class Fridge.json_storage : Object { * If you expect errors to happen, connect a handler to error() signal */ public Json.Array content { - owned get { return load ();} + get { return load ();} set { save (value);} } @@ -141,7 +141,7 @@ public class Fridge.json_storage : Object { * We simply return a copy of the cache in the event we track one and it isn't empty * Should the storage be empty, and thus the cache as well, we still check on-disk */ - private Json.Array? load () { + private unowned Json.Array? load () { debug("[STORAGE] Loading from storage letsgo"); check_if_datadir (); From 1ec86db0df8f613531c0015279e0d8c4bcec8a39 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Wed, 8 Oct 2025 16:31:04 +0200 Subject: [PATCH 07/30] Add a demo for upcoming tostring --- README.md | 3 +-- demo.vala | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 demo.vala diff --git a/README.md b/README.md index f47a465..d830a41 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Features: Wishlist of features: - [] More agressive trying to load/save in case of errors - +- [] Optional SQL storage i think? ## Add to your flatpak project @@ -86,7 +86,6 @@ To install libfridge: ninja install ``` - ## Documentation By default, documentation is built by default using [`valadoc`](https://docs.vala.dev/developer-guides/documentation/valadoc-guide.html) diff --git a/demo.vala b/demo.vala new file mode 100644 index 0000000..0443bbc --- /dev/null +++ b/demo.vala @@ -0,0 +1,16 @@ +// Testing and demo purposes +// valac --pkg libfridge demo.vala + + public static int main (string[] args) { + + sto = new Fridge.string_storage (); + print ("\n" + "Location: " sto.storage_path + "\n") + + switch (args[1]) { + "load": print(sto.content);break; + "save": sto.content = args[2];break; + default: ;break; + } + + return 0; + } From fbd5cbf8a565e2485ff69d9fe31ce087f6a4fcc9 Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 8 Oct 2025 19:04:34 +0200 Subject: [PATCH 08/30] add the dynamical internal unique storage thingy --- src/json_storage.vala | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/json_storage.vala b/src/json_storage.vala index 3cf7e91..ac0ce56 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -13,9 +13,15 @@ */ public class Fridge.json_storage : Object { + /** + * Used to give a unique name for each new instance without file name + * Incremented during object creation, only when no argument has been passed + */ + private static uint8 unnamed_storage_count = 0; + /** * This signal gets emitted when the content of the storage has been changed - ' This allows you to connect to your storage instance and trigger a function whenever there has been changes + * This allows you to connect to your storage instance and trigger a function whenever there has been changes */ public signal void changed (); @@ -40,20 +46,21 @@ public class Fridge.json_storage : Object { /** * The name of the file saved on disk. This can be set only upon creation + * Should there be no name, a default one will be assigned */ - string filename { public get; private set;}; + string filename { public get; private set;} /** * The path of the directory where the storage file is saved * This variable is not meant to be changed, and only as aid if the location is uncertain */ - string data_directory { public get; private set;}; + string data_directory { public get; private set;} /** * The full path of the storage file * This variable is not meant to be changed, and only as aid if the location is uncertain */ - string storage_path { public get; private set;}; + string storage_path { public get; private set;} /** * Create a representation of a storage file. If there is no file, the storage is considered empty @@ -63,7 +70,7 @@ public class Fridge.json_storage : Object { * * the storage emits a changed() signal whenever */ - public json_storage (string? name = "storage.json") { + public json_storage (string? name = "") { Object (filename: name); } @@ -81,16 +88,25 @@ public class Fridge.json_storage : Object { * You can connect handlers to the storage via the changed() signal * If you expect errors to happen, connect a handler to error() signal */ - public Json.Array content { - get { return load ();} + public Json.Array? content { + owned get { return load ();} set { save (value);} } /*************************************************/ construct { + + // Allow having several storage files without declaring a single name + // Each get a unique number depending on the order it is declared + // If the order is the same each time, there wouldnt be any storage clash + if (filename == "") { + filename = "storage_%i.json".printf (unnamed_storage_count); + unnamed_storage_count += 1; + } + data_directory = Environment.get_user_data_dir (); storage_path = data_directory + "/" + filename; - check_if_stash (); + check_if_datadir (); } /*************************************************/ From 61059c273c67dba6e46c79df0d9a0cfcb32db387 Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 8 Oct 2025 19:32:36 +0200 Subject: [PATCH 09/30] create string counterpart --- demo.vala | 8 +- meson.build | 1 + src/json_storage.vala | 10 +-- src/meson.build | 5 +- src/string_storage.vala | 195 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 11 deletions(-) create mode 100644 src/string_storage.vala diff --git a/demo.vala b/demo.vala index 0443bbc..7ebcf85 100644 --- a/demo.vala +++ b/demo.vala @@ -3,12 +3,12 @@ public static int main (string[] args) { - sto = new Fridge.string_storage (); - print ("\n" + "Location: " sto.storage_path + "\n") + var sto = new Fridge.string_storage (); + print ("\n" + "Location: " + sto.storage_path + "\n"); switch (args[1]) { - "load": print(sto.content);break; - "save": sto.content = args[2];break; + case "load": print (sto.content);break; + case "save": sto.content = args[2];break; default: ;break; } diff --git a/meson.build b/meson.build index 166bad0..b6e0648 100644 --- a/meson.build +++ b/meson.build @@ -24,6 +24,7 @@ g_ir_compiler = find_program('g-ir-compiler', required: false) libfridge_deps = [ dependency('glib-2.0'), dependency('gobject-2.0'), + dependency('gio-2.0'), dependency('json-glib-1.0', required : get_option('enable_json')) ] diff --git a/src/json_storage.vala b/src/json_storage.vala index ac0ce56..3fa90ef 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -157,22 +157,22 @@ public class Fridge.json_storage : Object { * We simply return a copy of the cache in the event we track one and it isn't empty * Should the storage be empty, and thus the cache as well, we still check on-disk */ - private unowned Json.Array? load () { + private Json.Array? load () { debug("[STORAGE] Loading from storage letsgo"); check_if_datadir (); if (keep_cache && (cache != null)) { - return cache.copy (); + return cache; } var parser = new Json.Parser (); - var array? = new Json.Array (); + var array = new Json.Array (); try { parser.load_from_mapped_file (storage_path); var node = parser.get_root (); array = node.get_array (); - if (keep_cache) { cache = json_data;}; + if (keep_cache) { cache = array;}; } catch (Error e) { warning ("[STORAGE] Failed to load from storage: " + e.message.to_string()); @@ -185,7 +185,7 @@ public class Fridge.json_storage : Object { /*************************************************/ /** - * Drop everything. The next time the content property is accessed, it will be read from disk + * Drop everything. The next time "content" is accessed, it will be read from disk * If keep_cache is set to true, a new cache will be generated */ private void empty_cache () { diff --git a/src/meson.build b/src/meson.build index 9bdda59..71397a3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,9 +1,10 @@ sources = files([ - 'library.vala' + 'library.vala', + 'string_storage.vala' ]) if get_option('enable_json') - sources += files(['json_storage']) + sources += files(['json_storage.vala']) endif libfridge = library( diff --git a/src/string_storage.vala b/src/string_storage.vala new file mode 100644 index 0000000..25265dc --- /dev/null +++ b/src/string_storage.vala @@ -0,0 +1,195 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 Stella & Charlie (teamcons.carrd.co) + */ + +/** +* A library intended for basic storage, for beginner and whoever needs only hassle-free basics +* +* Json_storage represents a json file in the app's data folder +* You can use this class to store a Json.Array containing your objects (As Json.Node) +* You can optionally initialize it with with a file name +* +*/ +public class Fridge.string_storage : Object { + + /** + * Used to give a unique name for each new instance without file name + * Incremented during object creation, only when no argument has been passed + */ + private static uint8 unnamed_storage_count = 0; + + /** + * This signal gets emitted when the content of the storage has been changed + * This allows you to connect to your storage instance and trigger a function whenever there has been changes + */ + public signal void changed (); + + /** + * This signal gets emitted when there is an error while loading or saving, along with said error + * Connect to this if you wish to handle errors + */ + public signal void error (Error e); + + /** + * Whether to keep a duplicate of the storage content to access storage very quickly + * By default this is set to true. Cache is regenerated when saving, and when loading if empty and enabled + * You can force it to be reloaded by using empty_cache, then accessing the storage content + * This feature can be disabled anytime, but make sure to call empty_cache after disabling it to avoid keeping a stale cache in memory + */ + public bool keep_cache = true; + + /** + * A copy of the storage file content. + */ + private string? cache; + + /** + * The name of the file saved on disk. This can be set only upon creation + * Should there be no name, a default one will be assigned + */ + string filename { public get; private set;} + + /** + * The path of the directory where the storage file is saved + * This variable is not meant to be changed, and only as aid if the location is uncertain + */ + string data_directory { public get; private set;} + + /** + * The full path of the storage file + * This variable is not meant to be changed, and only as aid if the location is uncertain + */ + string storage_path { public get; private set;} + + /** + * Create a representation of a storage file. If there is no file, the storage is considered empty + * There is one optional parameters: + * + * name: the name of the file to save to and load from. By default it is simply "storage.json" + * + * the storage emits a changed() signal whenever + */ + public string_storage (string? name = "") { + Object (filename: name); + } + + /** + * Property representing the content of storage on disk + * You can save a Json.Array by invoking: + * + * yourstorageinstance.content = array; + * + * And load the content of the file by doing + * + * var array = yourstorageinstance.content; + * + * You can disable the cache via keep_cache = false + * You can connect handlers to the storage via the changed() signal + * If you expect errors to happen, connect a handler to error() signal + */ + public string? content { + owned get { return load ();} + set { save (value);} + } + + /*************************************************/ + construct { + + // Allow having several storage files without declaring a single name + // Each get a unique number depending on the order it is declared + // If the order is the same each time, there wouldnt be any storage clash + if (filename == "") { + filename = "storage_%i.json".printf (unnamed_storage_count); + unnamed_storage_count += 1; + } + + data_directory = Environment.get_user_data_dir (); + storage_path = data_directory + "/" + filename; + check_if_datadir (); + } + + /*************************************************/ + /** + * Persistently check for the data directory and create if there is none + * Without this, we risk creating our storage in the void + */ + private void check_if_datadir () { + debug ("[STORAGE] do we have a data directory?"); + var dir = File.new_for_path (data_directory); + + try { + if (!dir.query_exists ()) { + dir.make_directory (); + debug ("[STORAGE] yes we do now"); + } + } catch (Error e) { + warning ("[STORAGE] Failed to prepare target data directory: %s\n", e.message); + } + } + + /*************************************************/ + /** + * Converts a Json.Node into a string and take care of saving it + */ + private void save (string? string_data) { + debug("[STORAGE] Writing..."); + check_if_datadir (); + + try { + var storage_file = File.new_for_path (storage_path); + var dostream = new DataOutputStream ( + storage_file.replace (null, false, GLib.FileCreateFlags.REPLACE_DESTINATION) + ); + + dostream.put_string (string_data); + if (keep_cache) { cache = string_data;}; + changed (); + + } catch (Error e) { + warning ("[STORAGE] Failed to save to storage: %s", e.message); + error (e); + } + } + + /*************************************************/ + /** + * Grab from storage, into a Json.Node we can parse. Insist if necessary + * We simply return a copy of the cache in the event we track one and it isn't empty + * Should the storage be empty, and thus the cache as well, we still check on-disk + */ + private string? load () { + debug("[STORAGE] Loading from storage letsgo"); + check_if_datadir (); + + if (keep_cache && (cache != null)) { + return cache; + } + + string string_data = null; + + try { + var storage_file = File.new_for_path (storage_path); + var distream = new DataInputStream (storage_file.read (null)); + string_data = distream.read_upto ("", -1, null); + if (keep_cache) { cache = string_data;}; + + } catch (Error e) { + warning ("[STORAGE] Failed to load from storage: " + e.message.to_string()); + error (e); + } + + return string_data; + } + + + /*************************************************/ + /** + * Drop everything. The next time "content" is accessed, it will be read from disk + * If keep_cache is set to true, a new cache will be generated + */ + private void empty_cache () { + debug("[STORAGE] Emptying cache"); + cache = null; + } +} From 05bf41af699529a3624ceb9215710f1dfb6c462c Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 8 Oct 2025 20:46:16 +0200 Subject: [PATCH 10/30] add in the demo to be able to test without installing --- demo.vala => src/demo.vala | 0 src/json_storage.vala | 6 +++--- src/meson.build | 3 ++- src/string_storage.vala | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) rename demo.vala => src/demo.vala (100%) diff --git a/demo.vala b/src/demo.vala similarity index 100% rename from demo.vala rename to src/demo.vala diff --git a/src/json_storage.vala b/src/json_storage.vala index 3fa90ef..c920e45 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -48,19 +48,19 @@ public class Fridge.json_storage : Object { * The name of the file saved on disk. This can be set only upon creation * Should there be no name, a default one will be assigned */ - string filename { public get; private set;} + public string filename { public get; private set;} /** * The path of the directory where the storage file is saved * This variable is not meant to be changed, and only as aid if the location is uncertain */ - string data_directory { public get; private set;} + public string data_directory { public get; private set;} /** * The full path of the storage file * This variable is not meant to be changed, and only as aid if the location is uncertain */ - string storage_path { public get; private set;} + public string storage_path { public get; private set;} /** * Create a representation of a storage file. If there is no file, the storage is considered empty diff --git a/src/meson.build b/src/meson.build index 71397a3..5d0ca83 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,7 @@ sources = files([ 'library.vala', - 'string_storage.vala' + 'string_storage.vala', + 'demo.vala' ]) if get_option('enable_json') diff --git a/src/string_storage.vala b/src/string_storage.vala index 25265dc..8bb5a96 100644 --- a/src/string_storage.vala +++ b/src/string_storage.vala @@ -48,19 +48,19 @@ public class Fridge.string_storage : Object { * The name of the file saved on disk. This can be set only upon creation * Should there be no name, a default one will be assigned */ - string filename { public get; private set;} + public string filename { public get; private set;} /** * The path of the directory where the storage file is saved * This variable is not meant to be changed, and only as aid if the location is uncertain */ - string data_directory { public get; private set;} + public string data_directory { public get; private set;} /** * The full path of the storage file * This variable is not meant to be changed, and only as aid if the location is uncertain */ - string storage_path { public get; private set;} + public string storage_path { public get; private set;} /** * Create a representation of a storage file. If there is no file, the storage is considered empty From b4979a8230221458dba9514907dcd911b1a88945 Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 8 Oct 2025 20:48:46 +0200 Subject: [PATCH 11/30] more info --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d830a41..d58eb5f 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,14 @@ Features: - [x] Smart instancing: Each instance is its own distinct file - [x] Cache: By default a cache lets you access content faster - [x] Optional error handling: You can connect to the error() signal - +- [x] Optional Json: You can rely on pretty pure vala here if you want to keep it light Wishlist of features: - [] More agressive trying to load/save in case of errors - [] Optional SQL storage i think? +- [] Save to binary? +- [] Integrated debounce saving? +- [] Make shit async for cases with heavy objects? ## Add to your flatpak project From 591cccc14e0c9408a5c19a8a0ce50c815798c1ed Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 8 Oct 2025 20:56:04 +0200 Subject: [PATCH 12/30] Allow building for windows --- meson.build | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index b6e0648..098a807 100644 --- a/meson.build +++ b/meson.build @@ -21,10 +21,38 @@ add_project_arguments(['--enable-experimental'], language: 'vala') g_ir_compiler = find_program('g-ir-compiler', required: false) +if build_machine.system() == 'linux' + vala_os_arg = ['--define=LINUX'] +elif build_machine.system() == 'dragonfly' + vala_os_arg = ['--define=DRAGON_FLY'] +elif build_machine.system() == 'freebsd' + vala_os_arg = ['--define=FREE_BSD'] +elif build_machine.system() == 'netbsd' + vala_os_arg = ['--define=NET_BSD'] +elif build_machine.system() == 'windows' + vala_os_arg = ['--define=WINDOWS'] +else + vala_os_arg = [] +endif + +glib_min_version = '2.50' +if build_machine.system() == 'windows' + gio_os_dep = dependency('gio-windows-2.0', version: '>=' + glib_min_version) +else + gio_os_dep = dependency('gio-unix-2.0', version: '>=' + glib_min_version) +endif + +add_project_arguments( + vala_os_arg, + '--target-glib=' + glib_min_version, + language: ['vala'] +) + libfridge_deps = [ - dependency('glib-2.0'), - dependency('gobject-2.0'), - dependency('gio-2.0'), + dependency('gio-2.0', version: '>=' + glib_min_version), + gio_os_dep, + dependency('glib-2.0', version: '>=' + glib_min_version), + dependency('gobject-2.0', version: '>=' + glib_min_version), dependency('json-glib-1.0', required : get_option('enable_json')) ] From 5ded2da2af92421c98da218aab4707578d963eb0 Mon Sep 17 00:00:00 2001 From: Stella and Charlie <147658063+teamcons@users.noreply.github.com> Date: Tue, 14 Oct 2025 22:15:31 +0200 Subject: [PATCH 13/30] Update README.md --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d58eb5f..7b505f7 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,12 @@ Features: - [x] Optional Json: You can rely on pretty pure vala here if you want to keep it light Wishlist of features: -- [] More agressive trying to load/save in case of errors -- [] Optional SQL storage i think? -- [] Save to binary? -- [] Integrated debounce saving? -- [] Make shit async for cases with heavy objects? +- [ ] More agressive trying to load/save in case of errors +- [ ] Optional SQL storage i think? +- [ ] Save to binary? +- [ ] Integrated debounce saving? +- [ ] Make shit async for cases with heavy objects? +- [ ] encrypted storage ? using some combined magic of libsecret and some traditional encrypt library? maybe a salt and a randomly generated password? so user only need unlock their keyring, and the random gen gets pulled out invisibly. ## Add to your flatpak project From 2be275fa49a6fbd02f39f037aaf2eaec28db7bdd Mon Sep 17 00:00:00 2001 From: Stella and Charlie <147658063+teamcons@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:46:56 +0200 Subject: [PATCH 14/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b505f7..bcc0354 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Features: Wishlist of features: - [ ] More agressive trying to load/save in case of errors -- [ ] Optional SQL storage i think? +- [ ] Optional SQL storage i think? Something stupid: One key one value. Optional like the json one - [ ] Save to binary? - [ ] Integrated debounce saving? - [ ] Make shit async for cases with heavy objects? From 678681c3bd5a9d68fcca188cc41b085b94627742 Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 15 Oct 2025 21:35:32 +0200 Subject: [PATCH 15/30] add is_empty() --- src/json_storage.vala | 9 ++++++++- src/string_storage.vala | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/json_storage.vala b/src/json_storage.vala index c920e45..46178e9 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -182,7 +182,6 @@ public class Fridge.json_storage : Object { return array; } - /*************************************************/ /** * Drop everything. The next time "content" is accessed, it will be read from disk @@ -192,4 +191,12 @@ public class Fridge.json_storage : Object { debug("[STORAGE] Emptying cache"); cache = null; } + + /*************************************************/ + /** + * Return whether storage is empty + */ + private bool? is_empty () { + return (load() == null); + } } diff --git a/src/string_storage.vala b/src/string_storage.vala index 8bb5a96..2157d1c 100644 --- a/src/string_storage.vala +++ b/src/string_storage.vala @@ -192,4 +192,12 @@ public class Fridge.string_storage : Object { debug("[STORAGE] Emptying cache"); cache = null; } + + /*************************************************/ + /** + * Return whether storage is empty + */ + private bool? is_empty () { + return (load() == null); + } } From 5ea9698f5893c2975f1b9e332f8df2e3d2a342b8 Mon Sep 17 00:00:00 2001 From: Stella and Charlie <147658063+teamcons@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:52:41 +0200 Subject: [PATCH 16/30] add in method to add later Removed duplicate wishlist item for integrated debounce saving. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bcc0354..5890aa5 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,11 @@ Features: - [x] Optional Json: You can rely on pretty pure vala here if you want to keep it light Wishlist of features: +- [ ] Add a contains() method, so people can do if (var in storageinstance) +- [ ] Integrated debounce saving? - [ ] More agressive trying to load/save in case of errors - [ ] Optional SQL storage i think? Something stupid: One key one value. Optional like the json one - [ ] Save to binary? -- [ ] Integrated debounce saving? - [ ] Make shit async for cases with heavy objects? - [ ] encrypted storage ? using some combined magic of libsecret and some traditional encrypt library? maybe a salt and a randomly generated password? so user only need unlock their keyring, and the random gen gets pulled out invisibly. From ea06dd41c8fa68d729c6324b3c8dca0519602f58 Mon Sep 17 00:00:00 2001 From: teamcons Date: Sun, 19 Oct 2025 20:09:05 +0200 Subject: [PATCH 17/30] add contains() and do better is_empty() --- src/json_storage.vala | 28 ++++++++++++++++++++++++++-- src/string_storage.vala | 21 +++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/json_storage.vala b/src/json_storage.vala index 46178e9..5ba39d5 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -196,7 +196,31 @@ public class Fridge.json_storage : Object { /** * Return whether storage is empty */ - private bool? is_empty () { - return (load() == null); + private bool is_empty () { + var currently_stored = load (); + return ((currently_stored == null) || (currently_stored.get_elements ().length () == 0)); } + + /*************************************************/ + /** + * Return whether storage contains input + */ + private bool contains (Json.Node? some_node) { + debug("[STORAGE] Checking if storage contains element"); + var currently_stored = load (); + + // True if storage is null, else false. + if (some_node == null) { + return (currently_stored == null); + } + + foreach (var node in currently_stored.get_elements ()) { + if (some_node == node) { + return true; + } + } + + return false; + } + } diff --git a/src/string_storage.vala b/src/string_storage.vala index 2157d1c..dcfd24a 100644 --- a/src/string_storage.vala +++ b/src/string_storage.vala @@ -197,7 +197,24 @@ public class Fridge.string_storage : Object { /** * Return whether storage is empty */ - private bool? is_empty () { - return (load() == null); + private bool is_empty () { + var currently_stored = load (); + return ((currently_stored == null) || (currently_stored == "")); } + + /*************************************************/ + /** + * Return whether storage contains input + */ + private bool contains (string? some_string) { + var currently_stored = load (); + + // True if storage is null, else false. + if (some_string == null) { + return (currently_stored == null); + } + + return (currently_stored.contains (some_string)); + } + } From f92ea9907eae8200d62308824b5a2c23801ef46e Mon Sep 17 00:00:00 2001 From: teamcons Date: Sun, 19 Oct 2025 20:12:16 +0200 Subject: [PATCH 18/30] Explicit getter setter default --- README.md | 2 +- src/json_storage.vala | 2 +- src/string_storage.vala | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5890aa5..44501ac 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,9 @@ Features: - [x] Cache: By default a cache lets you access content faster - [x] Optional error handling: You can connect to the error() signal - [x] Optional Json: You can rely on pretty pure vala here if you want to keep it light +- [x] Add a contains() method, so people can do if (var in storageinstance) Wishlist of features: -- [ ] Add a contains() method, so people can do if (var in storageinstance) - [ ] Integrated debounce saving? - [ ] More agressive trying to load/save in case of errors - [ ] Optional SQL storage i think? Something stupid: One key one value. Optional like the json one diff --git a/src/json_storage.vala b/src/json_storage.vala index 5ba39d5..db53649 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -37,7 +37,7 @@ public class Fridge.json_storage : Object { * You can force it to be reloaded by using empty_cache, then accessing the storage content * This feature can be disabled anytime, but make sure to call empty_cache after disabling it to avoid keeping a stale cache in memory */ - public bool keep_cache = true; + public bool keep_cache { get; set; default = true;} /** * A copy of the storage file content. diff --git a/src/string_storage.vala b/src/string_storage.vala index dcfd24a..6c61b70 100644 --- a/src/string_storage.vala +++ b/src/string_storage.vala @@ -32,12 +32,12 @@ public class Fridge.string_storage : Object { public signal void error (Error e); /** - * Whether to keep a duplicate of the storage content to access storage very quickly + * Whether to keep a duplicate of the storage content to access storage very quickly * By default this is set to true. Cache is regenerated when saving, and when loading if empty and enabled * You can force it to be reloaded by using empty_cache, then accessing the storage content * This feature can be disabled anytime, but make sure to call empty_cache after disabling it to avoid keeping a stale cache in memory */ - public bool keep_cache = true; + public bool keep_cache { get; set; default = true;} /** * A copy of the storage file content. From 0b9f87f5dded724bb88a356a7bb08aa40a12d928 Mon Sep 17 00:00:00 2001 From: teamcons Date: Sun, 19 Oct 2025 20:15:29 +0200 Subject: [PATCH 19/30] code style, debug --- src/json_storage.vala | 6 +++--- src/string_storage.vala | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/json_storage.vala b/src/json_storage.vala index db53649..1e859c5 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -197,6 +197,7 @@ public class Fridge.json_storage : Object { * Return whether storage is empty */ private bool is_empty () { + debug("[STORAGE] Checking if storage is empty"); var currently_stored = load (); return ((currently_stored == null) || (currently_stored.get_elements ().length () == 0)); } @@ -209,9 +210,8 @@ public class Fridge.json_storage : Object { debug("[STORAGE] Checking if storage contains element"); var currently_stored = load (); - // True if storage is null, else false. - if (some_node == null) { - return (currently_stored == null); + if (currently_stored == null) { + return false; } foreach (var node in currently_stored.get_elements ()) { diff --git a/src/string_storage.vala b/src/string_storage.vala index 6c61b70..6e7276f 100644 --- a/src/string_storage.vala +++ b/src/string_storage.vala @@ -198,6 +198,7 @@ public class Fridge.string_storage : Object { * Return whether storage is empty */ private bool is_empty () { + debug("[STORAGE] Checking if storage is empty"); var currently_stored = load (); return ((currently_stored == null) || (currently_stored == "")); } @@ -207,6 +208,7 @@ public class Fridge.string_storage : Object { * Return whether storage contains input */ private bool contains (string? some_string) { + debug("[STORAGE] Checking if storage contains element"); var currently_stored = load (); // True if storage is null, else false. From e0323e9582c8fc2841b29c00f92448f694570e29 Mon Sep 17 00:00:00 2001 From: teamcons Date: Thu, 30 Oct 2025 19:21:35 +0100 Subject: [PATCH 20/30] Add read-only --- src/json_storage.vala | 11 +++++++++++ src/string_storage.vala | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/json_storage.vala b/src/json_storage.vala index 1e859c5..184a53f 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -31,6 +31,12 @@ public class Fridge.json_storage : Object { */ public signal void error (Error e); + /** + * Whether the storage is read_only. False by default. + * If true, you cannot set content to anything. It does nothing. + */ + public bool read_only { get; set; default = false;} + /** * Whether to keep a duplicate of the storage content to access storage very quickly * By default this is set to true. Cache is regenerated when saving, and when loading if empty and enabled @@ -136,6 +142,11 @@ public class Fridge.json_storage : Object { debug("[STORAGE] Writing..."); check_if_datadir (); + if (read_only) { + warning ("Storage is read_only"); + return; + } + try { var generator = new Json.Generator (); var node = new Json.Node (Json.NodeType.ARRAY); diff --git a/src/string_storage.vala b/src/string_storage.vala index 6e7276f..64af94d 100644 --- a/src/string_storage.vala +++ b/src/string_storage.vala @@ -31,6 +31,12 @@ public class Fridge.string_storage : Object { */ public signal void error (Error e); + /** + * Whether the storage is read_only. False by default. + * If true, you cannot set content to anything. It does nothing. + */ + public bool read_only { get; set; default = false;} + /** * Whether to keep a duplicate of the storage content to access storage very quickly * By default this is set to true. Cache is regenerated when saving, and when loading if empty and enabled @@ -136,6 +142,11 @@ public class Fridge.string_storage : Object { debug("[STORAGE] Writing..."); check_if_datadir (); + if (read_only) { + warning ("Storage is read_only"); + return; + } + try { var storage_file = File.new_for_path (storage_path); var dostream = new DataOutputStream ( From 5d17210647a9d53d20d92abaa71bd946cae01c45 Mon Sep 17 00:00:00 2001 From: teamcons Date: Thu, 30 Oct 2025 20:55:42 +0100 Subject: [PATCH 21/30] Keep a Glib.File ref and add delete, but theres simplification doable here --- src/json_storage.vala | 21 +++++++++++++++++++++ src/string_storage.vala | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/json_storage.vala b/src/json_storage.vala index 184a53f..3f7b983 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -68,6 +68,11 @@ public class Fridge.json_storage : Object { */ public string storage_path { public get; private set;} + /** + * The File object this storage represents + */ + public File file { public get; private set;} + /** * Create a representation of a storage file. If there is no file, the storage is considered empty * There is one optional parameters: @@ -112,6 +117,7 @@ public class Fridge.json_storage : Object { data_directory = Environment.get_user_data_dir (); storage_path = data_directory + "/" + filename; + file = File.new_for_path (storage_path); check_if_datadir (); } @@ -234,4 +240,19 @@ public class Fridge.json_storage : Object { return false; } + /*************************************************/ + /** + * Call this to delete the file storage is connected to + */ + public void delete () { + file.delete_async.begin (GLib.Priority.DEFAULT, null, (obj, res) => { + try { + file.trash_async.end (res); + + } catch (Error e) { + this.error (e); + warning (e.message); + } + }); + } } diff --git a/src/string_storage.vala b/src/string_storage.vala index 64af94d..663dc86 100644 --- a/src/string_storage.vala +++ b/src/string_storage.vala @@ -68,6 +68,11 @@ public class Fridge.string_storage : Object { */ public string storage_path { public get; private set;} + /** + * The File object this storage represents + */ + public File file { public get; private set;} + /** * Create a representation of a storage file. If there is no file, the storage is considered empty * There is one optional parameters: @@ -112,6 +117,7 @@ public class Fridge.string_storage : Object { data_directory = Environment.get_user_data_dir (); storage_path = data_directory + "/" + filename; + file = File.new_for_path (storage_path); check_if_datadir (); } @@ -230,4 +236,19 @@ public class Fridge.string_storage : Object { return (currently_stored.contains (some_string)); } + /*************************************************/ + /** + * Call this to delete the file storage is connected to + */ + public void delete () { + file.delete_async.begin (GLib.Priority.DEFAULT, null, (obj, res) => { + try { + file.trash_async.end (res); + + } catch (Error e) { + this.error (e); + warning (e.message); + } + }); + } } From a429e4c266c13a8bb4de3b274ef2c6ca8c659e8d Mon Sep 17 00:00:00 2001 From: teamcons Date: Thu, 30 Oct 2025 21:58:12 +0100 Subject: [PATCH 22/30] make datadir check static, make linter happy, follow camelcase conventions for classes --- src/demo.vala | 2 +- src/json_storage.vala | 37 +++++++++---------------------------- src/library.vala | 19 +++++++++++++++++++ src/string_storage.vala | 34 +++++++--------------------------- 4 files changed, 36 insertions(+), 56 deletions(-) diff --git a/src/demo.vala b/src/demo.vala index 7ebcf85..8818524 100644 --- a/src/demo.vala +++ b/src/demo.vala @@ -3,7 +3,7 @@ public static int main (string[] args) { - var sto = new Fridge.string_storage (); + var sto = new Fridge.StringStorage (); print ("\n" + "Location: " + sto.storage_path + "\n"); switch (args[1]) { diff --git a/src/json_storage.vala b/src/json_storage.vala index 3f7b983..1a6c276 100644 --- a/src/json_storage.vala +++ b/src/json_storage.vala @@ -11,7 +11,7 @@ * You can optionally initialize it with with a file name * */ -public class Fridge.json_storage : Object { +public class Fridge.JsonStorage : Object { /** * Used to give a unique name for each new instance without file name @@ -81,7 +81,7 @@ public class Fridge.json_storage : Object { * * the storage emits a changed() signal whenever */ - public json_storage (string? name = "") { + public JsonStorage (string? name = "") { Object (filename: name); } @@ -121,31 +121,12 @@ public class Fridge.json_storage : Object { check_if_datadir (); } - /*************************************************/ - /** - * Persistently check for the data directory and create if there is none - * Without this, we risk creating our storage in the void - */ - private void check_if_datadir () { - debug ("[STORAGE] do we have a data directory?"); - var dir = File.new_for_path (data_directory); - - try { - if (!dir.query_exists ()) { - dir.make_directory (); - debug ("[STORAGE] yes we do now"); - } - } catch (Error e) { - warning ("[STORAGE] Failed to prepare target data directory: %s\n", e.message); - } - } - /*************************************************/ /** * Converts a Json.Node into a string and take care of saving it */ private void save (Json.Array? json_data) { - debug("[STORAGE] Writing..."); + debug ("[STORAGE] Writing..."); check_if_datadir (); if (read_only) { @@ -175,7 +156,7 @@ public class Fridge.json_storage : Object { * Should the storage be empty, and thus the cache as well, we still check on-disk */ private Json.Array? load () { - debug("[STORAGE] Loading from storage letsgo"); + debug ("[STORAGE] Loading from storage letsgo"); check_if_datadir (); if (keep_cache && (cache != null)) { @@ -192,20 +173,20 @@ public class Fridge.json_storage : Object { if (keep_cache) { cache = array;}; } catch (Error e) { - warning ("[STORAGE] Failed to load from storage: " + e.message.to_string()); + warning ("[STORAGE] Failed to load from storage: " + e.message.to_string ()); error (e); } return array; } - + /*************************************************/ /** * Drop everything. The next time "content" is accessed, it will be read from disk * If keep_cache is set to true, a new cache will be generated */ private void empty_cache () { - debug("[STORAGE] Emptying cache"); + debug ("[STORAGE] Emptying cache"); cache = null; } @@ -214,7 +195,7 @@ public class Fridge.json_storage : Object { * Return whether storage is empty */ private bool is_empty () { - debug("[STORAGE] Checking if storage is empty"); + debug ("[STORAGE] Checking if storage is empty"); var currently_stored = load (); return ((currently_stored == null) || (currently_stored.get_elements ().length () == 0)); } @@ -224,7 +205,7 @@ public class Fridge.json_storage : Object { * Return whether storage contains input */ private bool contains (Json.Node? some_node) { - debug("[STORAGE] Checking if storage contains element"); + debug ("[STORAGE] Checking if storage contains element"); var currently_stored = load (); if (currently_stored == null) { diff --git a/src/library.vala b/src/library.vala index 01320d3..fe42a26 100644 --- a/src/library.vala +++ b/src/library.vala @@ -2,4 +2,23 @@ namespace Fridge { public static void say_hello () { stdout.printf ("say_hello () called\n"); } + + /*************************************************/ + /** + * Persistently check for the data directory and create if there is none + * Without this, we risk creating our storage in the void + */ + private static void check_if_datadir () { + debug ("[STORAGE] do we have a data directory?"); + var dir = File.new_for_path ( Environment.get_user_data_dir ()); + + try { + if (!dir.query_exists ()) { + dir.make_directory (); + debug ("[STORAGE] yes we do now"); + } + } catch (Error e) { + warning ("[STORAGE] Failed to prepare target data directory: %s\n", e.message); + } + } } diff --git a/src/string_storage.vala b/src/string_storage.vala index 663dc86..c414d2c 100644 --- a/src/string_storage.vala +++ b/src/string_storage.vala @@ -11,7 +11,7 @@ * You can optionally initialize it with with a file name * */ -public class Fridge.string_storage : Object { +public class Fridge.StringStorage : Object { /** * Used to give a unique name for each new instance without file name @@ -81,7 +81,7 @@ public class Fridge.string_storage : Object { * * the storage emits a changed() signal whenever */ - public string_storage (string? name = "") { + public StringStorage (string? name = "") { Object (filename: name); } @@ -121,31 +121,12 @@ public class Fridge.string_storage : Object { check_if_datadir (); } - /*************************************************/ - /** - * Persistently check for the data directory and create if there is none - * Without this, we risk creating our storage in the void - */ - private void check_if_datadir () { - debug ("[STORAGE] do we have a data directory?"); - var dir = File.new_for_path (data_directory); - - try { - if (!dir.query_exists ()) { - dir.make_directory (); - debug ("[STORAGE] yes we do now"); - } - } catch (Error e) { - warning ("[STORAGE] Failed to prepare target data directory: %s\n", e.message); - } - } - /*************************************************/ /** * Converts a Json.Node into a string and take care of saving it */ private void save (string? string_data) { - debug("[STORAGE] Writing..."); + debug ("[STORAGE] Writing..."); check_if_datadir (); if (read_only) { @@ -176,7 +157,7 @@ public class Fridge.string_storage : Object { * Should the storage be empty, and thus the cache as well, we still check on-disk */ private string? load () { - debug("[STORAGE] Loading from storage letsgo"); + debug ("[STORAGE] Loading from storage letsgo"); check_if_datadir (); if (keep_cache && (cache != null)) { @@ -198,15 +179,14 @@ public class Fridge.string_storage : Object { return string_data; } - - + /*************************************************/ /** * Drop everything. The next time "content" is accessed, it will be read from disk * If keep_cache is set to true, a new cache will be generated */ private void empty_cache () { - debug("[STORAGE] Emptying cache"); + debug ("[STORAGE] Emptying cache"); cache = null; } @@ -225,7 +205,7 @@ public class Fridge.string_storage : Object { * Return whether storage contains input */ private bool contains (string? some_string) { - debug("[STORAGE] Checking if storage contains element"); + debug ("[STORAGE] Checking if storage contains element"); var currently_stored = load (); // True if storage is null, else false. From 80ffdee1733d8b6ab2cb82fa6d384b7fe4a73dc9 Mon Sep 17 00:00:00 2001 From: teamcons Date: Thu, 30 Oct 2025 23:01:25 +0100 Subject: [PATCH 23/30] Explicit GLib to silence warnings, and disable valadoc making stuff fail --- meson.build | 4 ++-- src/library.vala | 2 +- src/string_storage.vala | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/meson.build b/meson.build index 098a807..f5c9c6c 100644 --- a/meson.build +++ b/meson.build @@ -53,7 +53,7 @@ libfridge_deps = [ gio_os_dep, dependency('glib-2.0', version: '>=' + glib_min_version), dependency('gobject-2.0', version: '>=' + glib_min_version), - dependency('json-glib-1.0', required : get_option('enable_json')) + dependency('json-glib-1.0', required: get_option('enable_json')) ] subdir('src') @@ -74,7 +74,7 @@ if get_option('enable_valadoc') '--force' ], output: meson.project_name(), - build_by_default: true + build_by_default: false ) install_subdir( diff --git a/src/library.vala b/src/library.vala index fe42a26..f0790ba 100644 --- a/src/library.vala +++ b/src/library.vala @@ -10,7 +10,7 @@ namespace Fridge { */ private static void check_if_datadir () { debug ("[STORAGE] do we have a data directory?"); - var dir = File.new_for_path ( Environment.get_user_data_dir ()); + var dir = GLib.File.new_for_path ( Environment.get_user_data_dir ()); try { if (!dir.query_exists ()) { diff --git a/src/string_storage.vala b/src/string_storage.vala index c414d2c..a093506 100644 --- a/src/string_storage.vala +++ b/src/string_storage.vala @@ -11,7 +11,7 @@ * You can optionally initialize it with with a file name * */ -public class Fridge.StringStorage : Object { +public class Fridge.StringStorage : GLib.Object { /** * Used to give a unique name for each new instance without file name @@ -71,7 +71,7 @@ public class Fridge.StringStorage : Object { /** * The File object this storage represents */ - public File file { public get; private set;} + public GLib.File file { public get; private set;} /** * Create a representation of a storage file. If there is no file, the storage is considered empty @@ -82,7 +82,7 @@ public class Fridge.StringStorage : Object { * the storage emits a changed() signal whenever */ public StringStorage (string? name = "") { - Object (filename: name); + GLib.Object (filename: name); } /** @@ -117,7 +117,7 @@ public class Fridge.StringStorage : Object { data_directory = Environment.get_user_data_dir (); storage_path = data_directory + "/" + filename; - file = File.new_for_path (storage_path); + file = GLib.File.new_for_path (storage_path); check_if_datadir (); } @@ -135,7 +135,7 @@ public class Fridge.StringStorage : Object { } try { - var storage_file = File.new_for_path (storage_path); + var storage_file = GLib.File.new_for_path (storage_path); var dostream = new DataOutputStream ( storage_file.replace (null, false, GLib.FileCreateFlags.REPLACE_DESTINATION) ); From 871cf8ee1ebbf1d576e0e969bab1dfa910dac83e Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Fri, 31 Oct 2025 10:36:37 +0100 Subject: [PATCH 24/30] Some renaming --- src/json_storage.vala => lib/JsonStorage.vala | 0 src/library.vala => lib/Static.vala | 0 src/string_storage.vala => lib/StringStorage.vala | 0 {src => lib}/demo.vala | 0 {src => lib}/meson.build | 6 +++--- meson.build | 2 +- 6 files changed, 4 insertions(+), 4 deletions(-) rename src/json_storage.vala => lib/JsonStorage.vala (100%) rename src/library.vala => lib/Static.vala (100%) rename src/string_storage.vala => lib/StringStorage.vala (100%) rename {src => lib}/demo.vala (100%) rename {src => lib}/meson.build (94%) diff --git a/src/json_storage.vala b/lib/JsonStorage.vala similarity index 100% rename from src/json_storage.vala rename to lib/JsonStorage.vala diff --git a/src/library.vala b/lib/Static.vala similarity index 100% rename from src/library.vala rename to lib/Static.vala diff --git a/src/string_storage.vala b/lib/StringStorage.vala similarity index 100% rename from src/string_storage.vala rename to lib/StringStorage.vala diff --git a/src/demo.vala b/lib/demo.vala similarity index 100% rename from src/demo.vala rename to lib/demo.vala diff --git a/src/meson.build b/lib/meson.build similarity index 94% rename from src/meson.build rename to lib/meson.build index 5d0ca83..298a341 100644 --- a/src/meson.build +++ b/lib/meson.build @@ -1,11 +1,11 @@ sources = files([ - 'library.vala', - 'string_storage.vala', + 'Static.vala', + 'StringStorage.vala', 'demo.vala' ]) if get_option('enable_json') - sources += files(['json_storage.vala']) + sources += files(['JsonStorage.vala']) endif libfridge = library( diff --git a/meson.build b/meson.build index f5c9c6c..5d8c0c3 100644 --- a/meson.build +++ b/meson.build @@ -56,7 +56,7 @@ libfridge_deps = [ dependency('json-glib-1.0', required: get_option('enable_json')) ] -subdir('src') +subdir('lib') if get_option('enable_valadoc') valadoc = find_program('valadoc') From b468bd08b0f7c6ddf1088b148f1776240dabae72 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Fri, 31 Oct 2025 10:59:38 +0100 Subject: [PATCH 25/30] Add an init() and a couple static values --- lib/Static.vala | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/lib/Static.vala b/lib/Static.vala index f0790ba..8be5554 100644 --- a/lib/Static.vala +++ b/lib/Static.vala @@ -1,4 +1,36 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 Stella & Charlie (teamcons.carrd.co) + */ + namespace Fridge { + private static bool initialized = false; + + /** + * The number of files detected in the data directory + * You need to call Fridge.init () or else this will not be initialized + * + * Access to files will pollute the data directory with temporary files and + * pollute the count if it is done after declaring a couple Storage objects, + * So we can obtain a reliable count only early on. + */ + public static uint8? file_count { get; private set; default = null;} + + + /** + * The name of all files detected in the data directory + * You need to call Fridge.init () or else this will not be initialized + * + * Access to files will pollute the data directory with temporary files and + * pollute the count if it is done after declaring a couple Storage objects, + * So we can obtain a reliable count only early on. + */ + public static string[]? file_list { get; private set; default = null;} + + + /** + * Hi bud + */ public static void say_hello () { stdout.printf ("say_hello () called\n"); } @@ -21,4 +53,36 @@ namespace Fridge { warning ("[STORAGE] Failed to prepare target data directory: %s\n", e.message); } } + + /** + * Initializes Fridge. If Fridge has already been initialized, the function will return. + * Retrieves how many files we have in the data directory + */ + public static void init () { + if (initialized) { + return; + } + + check_if_datadir (); + + try { + var data_dir = Dir.open (Environment.get_user_data_dir ()); + string? filename = null; + Fridge.file_count = 0; + Fridge.file_list = {} + + while ((filename = data_dir.read_name ()) != null) { + print (filename); + + Fridge.file_count++; + Fridge.file_list += filename; + } + + } catch (Error e) { + warning ("Cannot read datadir! Is the disk okay? %s\n", e.message); + } + } + + + } From 29ccbeaebd554013a9cdc0355789f20cdb394116 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Fri, 31 Oct 2025 11:09:36 +0100 Subject: [PATCH 26/30] Niceify README --- README.md | 100 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 44501ac..fc98281 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,21 @@ # libfridge A simple, hassle-free storage library to keep your data fresh. -Ready out of the box, and comprehensive for vala newcomers. This library primarily contains the three following objects: +Ready out of the box, comprehensive for vala newcomers, and built to be reliable and out of your way. -- json_storage -- string_storage -- byte_storage +This library primarily contains: -all three are meant to represent a storage file saved on the disk, and intended to contain a type of data. Simply declare: +- StringStorage: Store a string, any size. +- JsonStorage: Store an array of Json.Node or Json.Objects +- Static variables to keep track of your data files -``` -var mystorage = new Fridge.json_storage(); -``` - -Or any variant depending what you want to store... And you are good to go! -Save by doing: - -``` -mystorage.content = thing_to_save; -``` - -Access it by doing: - -``` -var thing_to_load = mystorage.content; -``` - -Features: -- [x] Smart instancing: Each instance is its own distinct file +## Features: +- [x] Smart instancing: Each instance is its own distinct file, and you can let Fridge deal with that. - [x] Cache: By default a cache lets you access content faster -- [x] Optional error handling: You can connect to the error() signal +- [x] Optional error handling: You can connect to the error() signal. If you want to. - [x] Optional Json: You can rely on pretty pure vala here if you want to keep it light - [x] Add a contains() method, so people can do if (var in storageinstance) +- [x] Handy: Static values to help you manage your datadir Wishlist of features: - [ ] Integrated debounce saving? @@ -42,31 +26,32 @@ Wishlist of features: - [ ] encrypted storage ? using some combined magic of libsecret and some traditional encrypt library? maybe a salt and a randomly generated password? so user only need unlock their keyring, and the random gen gets pulled out invisibly. -## Add to your flatpak project -Simply copy the in your manifest before your app sources: +## How to use: Crash course + +All instances are meant to represent a file on disk. Simply declare: ``` - - name: libfridge - buildsystem: meson - sources: - - type: git - url: https://github.com/vala-community/libfridge.git - tag: 0.0.1 - commit: [NOT DONE YET] - x-checker-data: - type: git - tag-pattern: '^([\d.]+)$' +var mystorage = new Fridge.json_storage(); ``` -By default json_storage is included. If you do not use it, and wish to skip having a json dependency, you can add immediately after the buildsystem line: +Or any variant depending what you want to store... And you are good to go! +Save by doing: ``` - config-opts: - - -Denable_json=false +mystorage.content = thing_to_save; ``` +Access it by doing: +``` +var thing_to_load = mystorage.content; +``` + +## Apps using Fridge: + +- [ ] Jorts (https://github.com/ellie-commons/Jorts) +- [ ] You can expand this list! ## Build Instructions @@ -91,6 +76,41 @@ To install libfridge: ninja install ``` + +## Add to your meson + +With Fridge installed on your puter, simply add to your dependencies: + +``` + dependency('libfridge-0.1'), +``` + + +## Add to your flatpak project + +Simply copy the in your manifest before your app sources: + +``` + - name: libfridge + buildsystem: meson + sources: + - type: git + url: https://github.com/vala-community/libfridge.git + tag: 0.0.1 + commit: [NOT DONE YET] + x-checker-data: + type: git + tag-pattern: '^([\d.]+)$' +``` + +By default JsonStorage is included. If you do not use it, and wish to skip having a json dependency, you can add immediately after the buildsystem line: + +``` + config-opts: + - -Denable_json=false +``` + + ## Documentation By default, documentation is built by default using [`valadoc`](https://docs.vala.dev/developer-guides/documentation/valadoc-guide.html) From 047390b023bb60970fbd18233d3c82fc2a2b7ebe Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Fri, 31 Oct 2025 13:44:01 +0100 Subject: [PATCH 27/30] Add demo executable --- {lib => demo}/demo.vala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {lib => demo}/demo.vala (100%) diff --git a/lib/demo.vala b/demo/demo.vala similarity index 100% rename from lib/demo.vala rename to demo/demo.vala From 5dee41298ff8c5e37230b15efea381907431e790 Mon Sep 17 00:00:00 2001 From: teamcons_atwork Date: Fri, 31 Oct 2025 13:44:09 +0100 Subject: [PATCH 28/30] meson for demo --- demo/meson.build | 11 +++++++++++ lib/meson.build | 3 +-- meson.build | 4 ++++ meson_options.txt | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 demo/meson.build diff --git a/demo/meson.build b/demo/meson.build new file mode 100644 index 0000000..14dbe3e --- /dev/null +++ b/demo/meson.build @@ -0,0 +1,11 @@ + + executable( + 'libfridge-demo', + 'demo.vala', + + dependencies: [ + libfridge-0.1 + ], + + install: true, + ) \ No newline at end of file diff --git a/lib/meson.build b/lib/meson.build index 298a341..d019f22 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -1,7 +1,6 @@ sources = files([ 'Static.vala', - 'StringStorage.vala', - 'demo.vala' + 'StringStorage.vala' ]) if get_option('enable_json') diff --git a/meson.build b/meson.build index 5d8c0c3..17bbc95 100644 --- a/meson.build +++ b/meson.build @@ -82,3 +82,7 @@ if get_option('enable_valadoc') install_dir: get_option('datadir') / 'devhelp' / 'books' ) endif + +if get_option('build_demo') + subdir('demo') +endif diff --git a/meson_options.txt b/meson_options.txt index 22c692b..36523e6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,2 +1,3 @@ option('enable_valadoc', type: 'boolean', value: true) -option('enable_json', type : 'boolean', value : true) \ No newline at end of file +option('enable_json', type : 'boolean', value : true) +option('build_demo', type : 'boolean', value : true) \ No newline at end of file From b56f57d4f3dff75ed1022db32397ee6f081dc31f Mon Sep 17 00:00:00 2001 From: teamcons Date: Fri, 31 Oct 2025 19:17:43 +0100 Subject: [PATCH 29/30] fix minimistake --- demo/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/meson.build b/demo/meson.build index 14dbe3e..b7bd3ec 100644 --- a/demo/meson.build +++ b/demo/meson.build @@ -4,7 +4,7 @@ 'demo.vala', dependencies: [ - libfridge-0.1 + dependency (libfridge-0.1) ], install: true, From 556026e12a4ef105eb0b2ac07751f344b3749277 Mon Sep 17 00:00:00 2001 From: teamcons Date: Fri, 31 Oct 2025 19:18:56 +0100 Subject: [PATCH 30/30] fix minimistake - dependency should be a string --- demo/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/meson.build b/demo/meson.build index b7bd3ec..cf477f1 100644 --- a/demo/meson.build +++ b/demo/meson.build @@ -4,7 +4,7 @@ 'demo.vala', dependencies: [ - dependency (libfridge-0.1) + dependency ('libfridge-0.1') ], install: true,