singlefile/
lib.rs

1//! This library is designed to be a dead-simple way of reading and writing your rust values to and from disk.
2//!
3//! ## Usage
4//! `singlefile` provides a generic [`Container`] type, along with type alias variants for different use cases.
5//! [`Container`] is named so to indicate that it contains and manages a file and a value.
6//!
7//! ```no_run
8//! # use singlefile_formats::data::json_serde::{Json, JsonError};
9//! // A readable, writable container
10//! use singlefile::container::ContainerWritable;
11//! use serde::{Serialize, Deserialize};
12//!
13//! #[derive(Serialize, Deserialize, Default)]
14//! struct MyData {
15//!   magic_number: i32
16//! }
17//!
18//! // Attempts to open 'my_data.json', creating it from default if it does not exist,
19//! // expecting data that the `Json` format can decode into `MyData`
20//! let mut my_container = ContainerWritable::<MyData, Json>::create_or_default("my_data.json", Json)?;
21//! // For regular `Container`s, `Deref` and `DerefMut` can be used to access the contained type
22//! println!("magic_number: {}", my_container.magic_number); // 0 (as long as the file didn't exist before)
23//! my_container.magic_number += 1;
24//! // Write the new state of `MyData` to disk
25//! my_container.commit()?;
26//! # Ok::<(), singlefile::Error<JsonError>>(())
27//! ```
28//!
29//! We'd then expect the resulting `my_data.json` to look like:
30//!
31//! ```json
32//! {
33//!   "magic_number": 1
34//! }
35//! ```
36//!
37//! ## Shared and async containers
38//! `singlefile` also provides a [`ContainerShared`] type that can be used from multiple threads, as well as
39//! a [`ContainerSharedAsync`] that can be used from multiple threads and spawns its operations asynchronously.
40//! Currently, [`ContainerSharedAsync`] can only be guaranteed to work alongside Tokio.
41//!
42//! The shared container types can be enabled with the `shared` cargo feature.
43//! The async container types can be enabled with the `shared-async` cargo feature.
44//!
45//! ```no_run
46//! # use singlefile_formats::data::json_serde::{Json, JsonError};
47//! # use std::convert::Infallible;
48//! // A readable, writable container with multiple-ownership
49//! use singlefile::container_shared::ContainerSharedWritable;
50//! use serde::{Serialize, Deserialize};
51//!
52//! #[derive(Serialize, Deserialize, Default)]
53//! struct MyData {
54//!   magic_number: i32
55//! }
56//!
57//! // `ContainerShared` types may be cloned cheaply, they behave like `Arc`s
58//! let my_container = ContainerSharedWritable::<MyData, Json>::create_or_default("my_data.json", Json)?;
59//!
60//! // Get access to the contained `MyData`, increment it, and commit changes to disk
61//! std::thread::spawn(move || {
62//!   my_container.operate_mut_commit(|my_data| {
63//!     my_data.magic_number += 1;
64//!     Ok::<(), Infallible>(())
65//!   });
66//! });
67//! # Ok::<(), singlefile::Error<JsonError>>(())
68//! ```
69//!
70//! ## File formats
71//! `singlefile` is serialization framework-agnostic, so you will need a [`FileFormat`] adapter
72//! before you are able to read and write a given file format to disk.
73//!
74//! Here is how you'd write a `Json` adapter for the above examples, using `serde`.
75//!
76//! ```no_run
77//! # use singlefile_formats::data::json_serde::original as serde_json;
78//! use serde::ser::Serialize;
79//! use serde::de::DeserializeOwned;
80//! use singlefile::FileFormat;
81//! use std::io::{Read, Write};
82//!
83//! struct Json;
84//!
85//! impl<T> FileFormat<T> for Json
86//! where T: Serialize + DeserializeOwned {
87//!   type FormatError = serde_json::Error;
88//!
89//!   fn to_writer<W: Write>(&self, writer: W, value: &T) -> Result<(), Self::FormatError> {
90//!     serde_json::to_writer_pretty(writer, value).map_err(From::from)
91//!   }
92//!
93//!   fn from_reader<R: Read>(&self, reader: R) -> Result<T, Self::FormatError> {
94//!     serde_json::from_reader(reader).map_err(From::from)
95//!   }
96//! }
97//! ```
98//!
99//! Alternatively, you can use one of the preset file formats provided by
100//! [`singlefile-formats`](https://crates.io/crates/singlefile-formats).
101//!
102//! ## Features
103//! By default, only the `tokio-parking-lot` feature is enabled.
104//!
105//! - `shared`: Enables [`ContainerShared`], pulling in `parking_lot`.
106//! - `shared-async`: Enables [`ContainerSharedAsync`], pulling in `tokio` and (by default) `parking_lot`.
107//! - `fs-err2`: Enables this crate to use `fs-err` v2 for everything filesystem-related.
108//! - `fs-err3`: Enables this crate to use `fs-err` v3 for everything filesystem-related. Overrides `fs-err2` if present.
109//! - `fs-err`: An alias for the `fs-err3` feature (and will correspond to the latest version of `fs-err` going forwards).
110//! - `deadlock-detection`: Enables `parking_lot`'s `deadlock_detection` feature, if it is present.
111//! - `tokio-parking-lot`: Enables `parking_lot` for use in `tokio`, if it is present. Enabled by default.
112//!
113//! [`Container`]: crate::container::Container
114//! [`ContainerShared`]: crate::container_shared::ContainerShared
115//! [`ContainerSharedAsync`]: crate::container_shared_async::ContainerSharedAsync
116//! [`FileFormat`]: crate::manager::format::FileFormat
117
118#![cfg_attr(docsrs, feature(doc_cfg))]
119#![allow(
120  clippy::wrong_self_convention
121)]
122#![warn(
123  future_incompatible,
124  missing_copy_implementations,
125  missing_debug_implementations,
126  missing_docs,
127  unreachable_pub
128)]
129
130extern crate fs4;
131extern crate thiserror;
132#[cfg(feature = "shared")]
133extern crate parking_lot;
134#[cfg(feature = "shared-async")]
135extern crate tokio;
136
137pub mod container;
138#[cfg_attr(docsrs, doc(cfg(feature = "shared")))]
139#[cfg(feature = "shared")]
140pub mod container_shared;
141#[cfg_attr(docsrs, doc(cfg(feature = "shared-async")))]
142#[cfg(feature = "shared-async")]
143pub mod container_shared_async;
144pub mod error;
145pub mod fs;
146pub mod manager;
147pub mod utils;
148
149pub use crate::error::{Error, UserError};
150
151#[doc(inline)]
152pub use crate::manager::format::{FileFormat, FileFormatUtf8};
153
154pub(crate) mod sealed {
155  pub trait Sealed {}
156}