14 releases (3 stable)
2.0.0 | Jul 9, 2024 |
---|---|
1.1.0 | Feb 16, 2017 |
0.7.3 | Nov 10, 2016 |
0.5.0 | Mar 10, 2016 |
#127 in Configuration
1,193 downloads per month
Used in 9 crates
19KB
155 lines
preferences
Read and write user-specific application data in Rust
Documentation
Installation
cargo add preferences
lib.rs
:
Read and write user-specific application data
This crate allows Rust developers to store and retrieve user-local preferences and other application data in a flexible and platform-appropriate way.
Though it was originally inspired by Java's convenient
Preferences API,
this crate is more flexible. Any struct or enum that implements
serde
's Serialize
and Deserialize
traits can be stored and retrieved as user data. Implementing those traits is
trivial with the #[derive(Serialize, Deserialize)]
attribute.
Usage
For convenience, the type PreferencesMap<T>
is provided. (It's
actually just std::collections::HashMap<String, T>
, where T
defaults to
String
). This mirrors the Java API, which models user data as an opaque key-value store. As
long as T
is serializable and deserializable, Preferences
will be implemented for your map instance. This allows you to seamlessly save and load
user data with the save(..)
and load(..)
trait methods from Preferences
.
Basic example
extern crate preferences;
use preferences::{AppInfo, PreferencesMap, Preferences};
const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"};
fn main() {
// Create a new preferences key-value map
// (Under the hood: HashMap<String, String>)
let mut faves: PreferencesMap<String> = PreferencesMap::new();
// Edit the preferences (std::collections::HashMap)
faves.insert("color".into(), "blue".into());
faves.insert("programming language".into(), "Rust".into());
// Store the user's preferences
let prefs_key = "tests/docs/basic-example";
let save_result = faves.save(&APP_INFO, prefs_key);
assert!(save_result.is_ok());
// ... Then do some stuff ...
// Retrieve the user's preferences
let load_result = PreferencesMap::<String>::load(&APP_INFO, prefs_key);
assert!(load_result.is_ok());
assert_eq!(load_result.unwrap(), faves);
}
Using custom data types
extern crate preferences;
extern crate serde;
use preferences::{AppInfo, Preferences};
use serde::{Serialize, Deserialize};
const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"};
// Deriving `Serialize` and `Deserialize` on a struct/enum automatically implements
// the `Preferences` trait.
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct PlayerData {
level: u32,
health: f32,
}
fn main() {
let player = PlayerData{level: 2, health: 0.75};
let prefs_key = "tests/docs/custom-types";
let save_result = player.save(&APP_INFO, prefs_key);
assert!(save_result.is_ok());
// Method `load` is from trait `Preferences`.
let load_result = PlayerData::load(&APP_INFO, prefs_key);
assert!(load_result.is_ok());
assert_eq!(load_result.unwrap(), player);
}
Using custom data types with PreferencesMap
extern crate preferences;
extern crate serde;
use preferences::{AppInfo, PreferencesMap, Preferences};
use serde::{Serialize, Deserialize};
const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Point(f32, f32);
fn main() {
let mut places = PreferencesMap::new();
places.insert("treasure".into(), Point(1.0, 1.0));
places.insert("home".into(), Point(-1.0, 6.6));
let prefs_key = "tests/docs/custom-types-with-preferences-map";
let save_result = places.save(&APP_INFO, prefs_key);
assert!(save_result.is_ok());
let load_result = PreferencesMap::load(&APP_INFO, prefs_key);
assert!(load_result.is_ok());
assert_eq!(load_result.unwrap(), places);
}
Using custom data types with serializable containers
extern crate preferences;
extern crate serde;
use preferences::{AppInfo, Preferences};
use serde::{Serialize, Deserialize};
const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Point(usize, usize);
fn main() {
let square = vec![
Point(0,0),
Point(1,0),
Point(1,1),
Point(0,1),
];
let prefs_key = "tests/docs/custom-types-in-containers";
let save_result = square.save(&APP_INFO, prefs_key);
assert!(save_result.is_ok());
let load_result = Vec::<Point>::load(&APP_INFO, prefs_key);
assert!(load_result.is_ok());
assert_eq!(load_result.unwrap(), square);
}
Under the hood
Data is written to flat files under the active user's home directory in a location specific to
the operating system. This location is decided by the app_dirs
crate with the data type
UserConfig
. Within the data directory, the files are stored in a folder hierarchy that maps
to a sanitized version of the preferences key passed to save(..)
.
The data is stored in JSON format. This has several advantages:
- Human-readable and self-describing
- More compact than e.g. XML
- Better adoption rates and language compatibility than e.g. TOML
- Not reliant on a consistent memory layout like e.g. binary
You could, of course, implement Preferences
yourself and store your user data in
whatever location and format that you wanted. But that would defeat the purpose of this
library. 😊
Dependencies
~0.7–12MB
~81K SLoC