#save-file #local-file #struct #save-load #game #load-file #wrapper

localsavefile

Save and load structs from a local file. A convenience wrapper around the savefile crate.

8 releases

0.2.5 Sep 8, 2024
0.2.4 Aug 30, 2024
0.1.3 Aug 1, 2024
0.1.2 Jul 28, 2024

#307 in Filesystem

Download history 225/week @ 2024-07-23 283/week @ 2024-07-30 63/week @ 2024-08-06 4/week @ 2024-08-13 170/week @ 2024-08-20 211/week @ 2024-08-27 188/week @ 2024-09-03 18/week @ 2024-09-10

587 downloads per month
Used in 2 crates

MIT/Apache

22KB
355 lines

LocalSaveFile

Crates.io Docs.rs CI

About - Usage - Related - License

About

Save and load structs from a local file. A convenience wrapper around the savefile crate.

LocalSaveFile takes care of where and how a struct should be saved to disk. savefile allows for serialization and compression of a rust data-structure while directories-rs decides where that file should go.

This crate is not meant to be used as a database or anything more complex then a simple struct. Carrying over from savefile, this crate could be used, for example, for a save file in a game.

Why

I have been making a few toy program's in rust and kept finding a need to have some form of persistent storage. I did not want anything that was complicated to implement, and so, something as simple as attaching an attribute to a struct seemed like a good idea.

Usage

[!NOTE] Currently, only structs have been tested and are the scope of this crate.

Requirements

Cargo

cargo add localsavefile savefile

[!IMPORTANT] As this is mainly a convenience wrapper, savefile also needs to be added with cargo to be used by the exported macros.

Minimal Example

[!NOTE] The macros Default and Savefile are automatically set to be derived. In any case, use localsavefile_impl instead of localsavefile to manually derive them.

use localsavefile::{localsavefile, LocalSaveFile, LocalSaveFileCommon};

#[localsavefile]
struct MySave {
    val: u32,
}

let foo = MySave { val: 21 };
foo.save();
let bar = MySave::load_default();
let mut baz = MySave { val: 42 };
baz.load();
assert_eq!(foo.val, bar.val); // Should never trigger
assert_eq!(bar.val, foo.val); // Should never trigger
MySave::remove_file(); // Removes the default file

[!WARNING] If, for whatever reason, you implement localsavefile in a library, it is recommened to re-export the macro setlsf and have the user call this macro before anything. It will set the env variables LOCAL_SAVE_FILE_CARGO_PKG_NAME and LOCAL_SAVE_FILE_CARGO_PKG_AUTHORS to be used in place of CARGO_PKG_NAME and CARGO_PKG_AUTHORS respectively. Otherwise, the default paths will be in regards to your crate, not the user's.

// In lib.rs, probably
pub use localsavefile::setlsf;

Persistent File

If you wish to maintain the underlying file open, as in, not having to reopen it each time save or load is called, a file handler can be added to your struct through the parameter persist = true. This will modify your struct and add an additional field. It's usage is the same as the non-persistent version, with a few caveats as shown.

[!TIP] I have not done any testing as to whether there is any real benefit from holding persistent metadata. If you are unsure, just use the non-persistent version.

[!NOTE] Persistent localsavefiles will store it's path upon loading or saving. This means any subsequent calls to setlsf will not affect it.

use localsavefile::{localsavefile, LocalSaveFilePersistent, LocalSaveFileCommon};

#[localsavefile(persist = true)]
struct MySavePersist {
    val: u32,
}

let mut foo = MySavePersist {
    val: 21,
    // If you must create an inline struct, make use of your IDE to auto fill the following
    __place_localsavefile_above_any_derives: Default::default(),
};
// foo.open_default(); // You should call open or open_default first
// but foo.save() will also open_default if needed
foo.save(); // Save now requires foo to be mutable
let mut bar = MySavePersist::load_default();
assert_eq!(foo.val, bar.val); // Should never trigger
foo.close();
bar.close(); // Requires bar to be mutable
// Close any instances before removing the file
MySavePersist::remove_file(); // Removes the default file

[!CAUTION] Because localsavefile(persist = true) modifies your struct, it is important to place it before any derives that must be aware of every field, such as when using localsavefile_impl.

// First localsavefile
#[localsavefile_impl(persist = true)]
// Then whatever else ...
#[derive(Savefile, Default)]
struct MySave {
    val: u32,
    // HIDDEN: __place_localsavefile_above_any_derives : Option<File>
}

In this case, this ensures the added field gets processed by Default and Savefile.

Passthrough Functions

Internal functionality is exposed to allow for direct control over loading and saving in terms of what file to use. These paths are not saved and are immediately used for the respective operation.

use localsavefile::{localsavefile, LocalSaveFile, LocalSaveFileCommon};

#[localsavefile]
struct MySave {
    val: u32,
}

let foo = MySave::load_file_or_default("./data.bin");
foo.load_file("./other_data.bin");
foo.save_file("./this_data.bin");

// Replaces the default file
MySave::replace_file("./this_data.bin");

[!IMPORTANT] No check is made as to whether the file directory is valid. Particularly when saving to a file.

[!NOTE] load_file_or_default, load_file, and save_file ignore the usage of setlsf as paths are immediately used.

Options

By default, the underlying file name is based off a sanitized combination of module_path!, called from where the struct is defined, and the struct name.

The directory where files are stored is based off of directories::ProjectDirs.data_dir, where the name and first author in your Cargo.toml are used as parameters. Author does not need to be defined, but should be anyways.

As you can imagine, changing anything that these defaults use will sneakily change what your struct loads. The following options shown allow to override any of the mention values to maintain a static path.

#[localsavefile(name = "a_unique_name", path = "./a/valid/path")]
struct TestStruct {
    val: u32,
    str: String,
}

The following is what the Minimal Example will output as on my windows machine using localsavefile-test.

C:\\Users\\%USER%\\AppData\\Roaming\\localsavefile-test-author🧪\\localsavefile-test\\data\\localsavefile_test.mysave.bin

The version option takes a u32 and is passed to the underlying savefile crate. Take a look at the version section of that crate for more information, as that is all still relevant on this struct.

#[localsavefile(version = 1)]
struct TestStruct {
    val: u32,
    #[savefile_default_val = "not-blank"]
    #[savefile_versions = "0..0"]
    str: String,
}

License

Licensed under either of

at your option.

Dependencies

~7–17MB
~215K SLoC