#preferences #data #read-write #user #persistent #storage #replace

preferences-serde1

Read and write user-specific application data

1 stable release

Uses old Rust 2015

2.0.0 Oct 30, 2023

#1507 in Encoding

Download history 166/week @ 2023-12-18 95/week @ 2023-12-25 102/week @ 2024-01-01 242/week @ 2024-01-08 129/week @ 2024-01-15 320/week @ 2024-01-22 417/week @ 2024-01-29 406/week @ 2024-02-05 137/week @ 2024-02-12 159/week @ 2024-02-19 334/week @ 2024-02-26 353/week @ 2024-03-04 197/week @ 2024-03-11 151/week @ 2024-03-18 168/week @ 2024-03-25 163/week @ 2024-04-01

699 downloads per month

MIT license

19KB
171 lines

preferences-serde1

Read and write user-specific application data in Rust

This is a replacement for the old preferences crate to use serde version 1.0. To update your old Cargo.toml which previously looked like this:

preferences = "1.1"

use this:

preferences = {version="2.0.0", package = "preferences-serde1"}

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; just include the crate serde_derive (don't forget #[macro_use]!) and add #[derive(Serialize, Deserialize) to your struct definition. (See examples below.)

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_serde1 as 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

#[macro_use]
extern crate serde_derive;
extern crate preferences_serde1 as preferences;
use preferences::{AppInfo, Preferences};

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

#[macro_use]
extern crate serde_derive;
extern crate preferences_serde1 as preferences;
use preferences::{AppInfo, PreferencesMap, Preferences};

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

#[macro_use]
extern crate serde_derive;
extern crate preferences_serde1 as preferences;
use preferences::{AppInfo, Preferences};

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 directories crate with the function config_dir(). 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.4–1.2MB
~22K SLoC