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 type that implements rustc-serialize's Encodable and Decodable traits can be stored and retrieved as user data! Thankfully, implementing those traits is trivial; just use #[derive(RustcEncodable, RustcDecodable).

Usage

For convenience, the type PreferencesMap<T> is provided. (It's actually just a 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 the map is instantiated over a type T which is serializable and deserializable, Preferences will be implemented for your map instance. This will allow you to seamlessly save and load user data with the save(..) and load(..) methods on 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 compiler plugins 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), and only if you store custom data types in your user data.

Basic example

extern crate preferences;
use preferences::{PreferencesMap, Preferences};

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 = "preferences-rs/examples/faves";
    faves.save(prefs_key);

    // ... Then do some stuff ...

    // Retrieve the user's preferences
    let mut loaded_faves = PreferencesMap::new();
    let load_result = loaded_faves.load(prefs_key);
    assert!(load_result.is_ok());
    assert_eq!(loaded_faves, faves);

}

Using custom data types

extern crate rustc_serialize;
extern crate preferences;
use preferences::{PreferencesMap, Preferences};

#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
struct PlayerData {
    level: u32,
    health: f32,
}

fn main() {

    let player = PlayerData{level: 2, health: 0.75};

    let prefs_key = "preferences-rs/examples/player";
    player.save(prefs_key);

    let mut loaded_player = PlayerData{level: 0, health: 0.0};
    let load_result = loaded_player.load(prefs_key);
    assert!(load_result.is_ok());
    assert_eq!(loaded_player, player);

}

Using custom data types with PreferencesMap

extern crate rustc_serialize;
extern crate preferences;
use preferences::{PreferencesMap, Preferences};

#[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 = "preferences-rs/examples/places";
    places.save(prefs_key);

    let mut loaded_places = PreferencesMap::new();
    let load_result = loaded_places.load(prefs_key);
    assert!(load_result.is_ok());
    assert_eq!(loaded_places, places);

}

Using custom data types with serializable containers

extern crate rustc_serialize;
extern crate preferences;
use preferences::{PreferencesMap, Preferences};

#[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 = "preferences-rs/examples/square";
    square.save(prefs_key);

    let mut loaded_square: Vec<Point> = Vec::new();
    let load_result = loaded_square.load(prefs_key);
    assert!(load_result.is_ok());
    assert_eq!(loaded_square, 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. 😊

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.