Crate hashmap_settings

source ·
Expand description

§HashMap wrapper for layered settings

This crate facilitates the management of settings, with the goal of allowing developers to turn previously needlessly set in stone values into a setting a user can change, as well as making it easier for a developer to create multiple priority levels of settings, allowing users to have greater control and customization including across devices.

This crate allows a developer to store and access all program settings on a Account, a wrapper around a HashMap.

This crate is intended to be used with some sort of type abstraction so that settings of distinct types can be stored in a single Account. This crate provides the Stg type abstraction for this.

An Account can also hold other Accounts, allowing the existence of layered settings, that permit the creation complex systems that have the:

§Benefits

  1. Having multiple places changing the same setting with the value being taken from the place that is deemed to have the most importance. (eg: Default, Themes, Extensions, Global Settings, OS Settings, Device Settings, Temporary Settings )

  2. Organization of Settings. Given that an Account can hold accounts, and they can hold accounts of they own, its possible for small groups of settings to be organized in an Account, making it more convenient to locate a setting, or display a group of settings. Important to notice that this organization doesn’t need to be (but could be) enforced in all held accounts equally.

  3. Accounts can be individually deactivated allowing for a developer (or a user) to group settings in an Account and easily ignore them under certain conditions.

§Drawbacks

  1. Each Account holds a copy of the settings present in it’s child Accounts, so there is a memory cost, but its planned for it to be changed to a reference to the value instead.

  2. Having to internally do a HashMap’s .get() will most likely be slower than alternatives.

§Example

// imports
use hashmap_settings::prelude::*;
use std::collections::HashMap;

//creating the Parent Account
let mut account = Account::<
   String, //Account's name
   &str, //HashMap<K,V> K key
   Stg  //HashMap<K,v> V value
   >::default();

// inserting child Accounts
account.push(
   Account::new(
       "Default".to_string(), //Name of the Account
       true,//is the account active
       HashMap::from([
           ("lines", 3.stg()), // .stg() turns a type into the type abstraction Stg
           ("word_repetition", 10.stg()),
           ("word", "default".to_string().stg()),
       ]), //settings
       vec![], //child Accounts
   ),
   Valid::new_true(), // not relevant for this example and can be ignored.
);

account.push(
   Account::new(
       "Global Settings".to_string(),
       true,
       HashMap::from([
           ("word_repetition", 2.stg()),
           ("word", "global".to_string().stg()),
       ]), //this account is in a layer above the "Default" Account, so it's values will have priority
       vec![],
   ),
   Valid::new_true(),
);// we could be getting this from a database

account.push(
   Account::new(
       "Local Settings".to_string(),
       true,
       HashMap::from([("word", "local".to_string().stg())]),
       //this account is in a layer that's above "Default" and "Global Settings" Accounts,
       //so it's values will have priority over it
       vec![],
   ),
   Valid::new_true(),
);// we could be getting this Account from a local file

account.push(
   Account::new(
       "Inactive Account".to_string(),
       false, //this account is inactive so its settings will be ignored.
       HashMap::from([("word", "inactive".to_string().stg())]),
       vec![],
   ),
   Valid::new_true(),
);

//getting values from the account
let word: String = account.get(&"word").unstg()?;
let word_repetition = account.get(&"word_repetition").unstg()?;
let lines =account.get(&"lines").unstg()?;

//example of using the values
let mut sentence = String::new();
for _ in 0..word_repetition {
   sentence.push_str(&word);
   sentence.push(' ');
}
sentence.pop();
for _ in 0..lines {
   println!("{sentence}");
}
//this will print the following:
/*
local local
local local
local local
*/

//values in child accounts are still accessible
let ref_child_account: &Account<_, _, _> = account
   .deep(&mut vec![&"Default".to_string()])
   .unwrap();
let inactive_word: String = ref_child_account.get(&"word").unstg()?;
println!("{inactive_word}");
//this will print "default"

//this includes inactive accounts
let ref_child_account: &Account<_, _, _> = account
   .deep(&mut vec![&"Inactive Account".to_string()])
   .unwrap();
let inactive_word: String = ref_child_account.get(&"word").unstg()?;
println!("{inactive_word}");
//this will print "inactive"

§How to use

This crate relies on the nightly feature dyn trait upcasting that was supposed to be stable in rust 1.76.0, unfortunately it has been delayed so currently the nightly compiler is required.

Add the following line to your Cargo.toml:

[dependencies]
hashmap_settings = "0.5"

Add the following line to your .rs file:

use hashmap_settings::prelude::*;

Modules§

  • Account and other related elements.
  • Prelude containing everything that will likely be needed while using Account
  • type abstraction Stg and other related elements.

Structs§