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