Crate preferences [−] [src]
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
rustc-serialize
's Encodable
and Decodable
traits can be stored and retrieved as user data. Implementing those traits is
trivial; just use #[derive(RustcEncodable, RustcDecodable)
.
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
.
Roadmap
This crate aims to provide a convenient API for both stable and nightly Rust, which is why
it currently uses rustc-serialize
instead of the more recent
serde
library. In the distant future, when custom derives are stabilized
and serde
is available in stable Rust, this library will migrate to serde
. This will be
a breaking change (and will update the semantic version number accordingly so that your
builds don't break).
At that point, updating should be dead simple; you'll just have to
replace #[derive(RustcEncodable, RustcDecodable)
with #[derive(Serialize, Deserialize)
.
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
// `rustc_serialize` will be replaced with `serde` when custom derive is stabilized extern crate rustc_serialize; extern crate preferences; use preferences::{AppInfo, Preferences}; const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"}; // Deriving `RustcEncodable` and `RustcDecodable` on a struct/enum automatically implements // the `Preferences` trait. #[derive(RustcEncodable, RustcDecodable, 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 rustc_serialize; extern crate preferences; use preferences::{AppInfo, PreferencesMap, Preferences}; const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"}; #[derive(RustcEncodable, RustcDecodable, 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 rustc_serialize; extern crate preferences; use preferences::{AppInfo, Preferences}; const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"}; #[derive(RustcEncodable, RustcDecodable, 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. 😊
Structs
AppInfo |
Struct that holds information about your app. |
Enums
PreferencesError |
Error type representing the errors that can occur when saving or loading user data. |
Traits
Preferences |
Trait for types that can be saved & loaded as user data. |
Functions
prefs_base_dir |
Get full path to the base directory for preferences. |
Type Definitions
PreferencesMap |
Generic key-value store for user data. |