6 releases

0.2.2 Mar 7, 2023
0.2.1 Mar 5, 2023
0.1.2 Mar 5, 2023

#667 in Filesystem


Used in binroots-proc-macros

Apache-2.0

52KB
1K SLoC

binroots   Version Downloads Docs License Liberapay

Binroots is a (cross-platform!) crate that provides a simple and efficient way to save Rust data structures to disk. It allows you to save each field of a struct or enum variant as a separate file, making it easy to store reactive data, allowing end-users and hackers to watch individual files for changes and automate command-line tools for your app.

Project Status

Writing the initial commit of this crate took me about 7 hours. There are no unit tests yet, and it requires nightly Rust. If you care about your code, please do not use this in production (yet!). I can't guarantee that your files will remain safe. If you're interested in the development of binroots, check out the planned features and follow my Twitter (no promises of whatever else you'll see on there)

Installation

Add it to your project using cargo add binroots

Setting up a struct

To save a struct, annotate it with #[binroots_struct]:

use binroots::binroots_struct;

#[binroots_struct]
struct Status {
    connections: usize,
    is_online: bool,
    activity: Activity,
}

#[binroots_struct] Automatically derives Debug, Default and serde::Serialize. It wraps each field in BinrootsField, which allows saving of individual fields without having to serialize the entire struct.

Setting up an enum

In the struct above, we use an enum named Activity. Here's how it can be defined:

use binroots::binroots_enum;

#[binroots_enum]
enum Activity {
    None, // <- Automatically chosen as the default value
    Playing(String),
}

#[binroots_enum] Also automatically derives Debug, Default and serde::Serialize. Wrapper types aren't needed.

In order to satisfy Default, it also picks the first variant named either None, Nothing, Default, or Empty. If you wish to use a different default type, you may annotate the enum with #[binroots_enum(manual)], and mark a unit variant with #[default].

Saving data

In this example, we initialize status using Status::default (generated by #[binroots_struct])

When saving a struct annotated with #[binroots_struct], it will save to a subfolder named after the struct in kebab-case. In this example, on Unix, it saves to /tmp/<crate name>/status, and %LOCALAPPDATA\<crate name>\cache\status on Windows.

use binroots::save::{RootType, SaveError};

fn main() -> Result<(), SaveError> {
    let mut status = Status::default();

    *status.is_online = true;
    status.save()?; // <- Saves the entire struct to the disk

    *status.activity = Activity::Playing("video gamb".into());
    status.activity.save(Status::ROOT_FOLDER, RootType::InMemory)?; // <- Only saves status.activity to the disk

    Ok(())
}

After saving, the status folder should look like this:


/tmp/binroots/status
├── activity           => "Playing"
├── activity.value     => "video gamb"
├── connections        => "0"
└── is_online          => "true"

Planned Features

I'm most likely going to add these in order. If it's not on this list, it's either implemented or unconsidered.

  • Setting hFile = INVALID_HANDLE_VALUE on Windows when using in-memory storage. Currently can only save to persistent storage on Windows.
  • Unit tests lol
  • Union support
  • Unify #[binroots_*] macros into a single #[binroots] macro
  • Self::enable_autosave(self) -> Self for reactive data
  • Deserialize
  • Send + Sync
  • BinrootsField::watch(Fn(T)) for socket-ish behavior
  • Self::enable_rx(self) -> Self for true two-way reactive data
  • Async support?
  • Dual free/commercial license

Dependencies

~0.8–1.5MB
~29K SLoC