koit_toml/backend.rs
1//! Backends persist the database. They allow reading and writing bytes. Bytes-to-data conversion,
2//! and back, is handled by a [`Format`](crate::format::Format).
3//!
4//! # Examples
5//!
6//! ```
7//! use std::default::Default;
8//! use koit::{Database, format::Json, backend::Memory};
9//!
10//! type Messages = Vec<String>;
11//!
12//! #[async_std::main]
13//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
14//! let db: Database<Messages, Memory, Json> = Database::from_parts(
15//! Messages::default(), Memory::default()
16//! );
17//!
18//! db.write(|messages| {
19//! messages.push("a message".to_owned());
20//! messages.push("from me to you".to_owned());
21//! }).await;
22//! db.save().await?;
23//!
24//! let (_data, mut backend) = db.into_parts();
25//! assert_eq!(&mut backend.take(),
26//! br#"[
27//! "a message",
28//! "from me to you"
29//! ]"#
30//! );
31//!
32//! Ok(())
33//! }
34//! ```
35
36use async_trait::async_trait;
37
38/// Trait implementable by bytes storage providers.
39///
40/// # Examples
41///
42/// See the [backend module documentation](crate::backend).
43#[async_trait]
44pub trait Backend {
45 type Error: std::error::Error + Send + Sync + 'static;
46
47 /// Read all data from the backend.
48 ///
49 /// # Errors
50 ///
51 /// If the bytes failed to be read by the backend, an error variant is returned.
52 async fn read(&mut self) -> Result<Vec<u8>, Self::Error>;
53
54 /// Overwrite the backend with the given data.
55 ///
56 /// # Errors
57 ///
58 /// If the bytes failed to be written to the backend, an error variant is returned.
59 /// This may mean the backend is now corrupted.
60 async fn write(&mut self, data: Vec<u8>) -> Result<(), Self::Error>;
61}
62
63/// An in-memory backend.
64///
65/// # Examples
66///
67/// See the [backend module documentation](crate::backend).
68#[derive(std::default::Default, Debug, Clone, PartialEq, Eq)]
69pub struct Memory(Vec<u8>);
70
71impl Memory {
72 pub fn new() -> Self {
73 Self(Vec::new())
74 }
75
76 /// Take the data out of the backend, leaving an empty backend in its place.
77 pub fn take(&mut self) -> Vec<u8> {
78 std::mem::take(&mut self.0)
79 }
80}
81
82impl From<Vec<u8>> for Memory {
83 fn from(buf: Vec<u8>) -> Self {
84 Self(buf)
85 }
86}
87
88#[async_trait]
89impl Backend for Memory {
90 type Error = std::convert::Infallible;
91
92 async fn read(&mut self) -> Result<Vec<u8>, Self::Error> {
93 Ok(self.0.clone())
94 }
95 async fn write(&mut self, data: Vec<u8>) -> Result<(), Self::Error> {
96 self.0 = data;
97 Ok(())
98 }
99}
100
101#[cfg(feature = "file-backend")]
102pub use self::file::File;
103
104#[cfg(feature = "file-backend")]
105mod file {
106 use async_std::io::prelude::{ReadExt, SeekExt, WriteExt};
107 use async_trait::async_trait;
108
109 use super::Backend;
110
111 /// A file-backed backend.
112 ///
113 /// Note: this requires its futures to be executed on the Tokio 0.3 runtime.
114 #[cfg_attr(docsrs, doc(cfg(feature = "file-backend")))]
115 #[derive(Debug)]
116 pub struct File(async_std::fs::File);
117
118 impl File {
119 /// Creates the backend by opening the file at the given path.
120 ///
121 /// # Errors
122 ///
123 /// If the file does not exist or could not be opened for reading and writing, an error
124 /// variant is returned.
125 pub async fn from_path<P>(path: P) -> Result<Self, std::io::Error>
126 where
127 P: AsRef<std::path::Path>,
128 {
129 Ok(Self(
130 async_std::fs::OpenOptions::new()
131 .read(true)
132 .write(true)
133 .open(path.as_ref())
134 .await?,
135 ))
136 }
137
138 /// Creates the backend by opening a file at the given path. Creates the file if it
139 /// does not exist yet.
140 ///
141 /// # Errors
142 ///
143 /// If the file does not exist, but could not be created, or could not be opened for
144 /// reading and writing, an error variant is returned.
145 pub async fn from_path_or_create<P>(path: P) -> Result<(Self, bool), std::io::Error>
146 where
147 P: AsRef<std::path::Path>,
148 {
149 let backend = Self::from_path(&path).await;
150 match backend {
151 Ok(self_) => Ok((self_, true)),
152 Err(err) => match err.kind() {
153 std::io::ErrorKind::NotFound => Ok((
154 Self(
155 async_std::fs::OpenOptions::new()
156 .read(true)
157 .write(true)
158 .create(true)
159 .open(path.as_ref())
160 .await?,
161 ),
162 false,
163 )),
164 _ => Err(err),
165 },
166 }
167 }
168 }
169
170 #[async_trait]
171 impl Backend for File {
172 type Error = std::io::Error;
173
174 async fn read(&mut self) -> Result<Vec<u8>, Self::Error> {
175 let mut buffer = Vec::new();
176 self.0.seek(std::io::SeekFrom::Start(0)).await?;
177 self.0.read_to_end(&mut buffer).await?;
178 Ok(buffer)
179 }
180
181 async fn write(&mut self, data: Vec<u8>) -> Result<(), Self::Error> {
182 self.0.seek(std::io::SeekFrom::Start(0)).await?;
183 self.0.set_len(0).await?;
184 self.0.write_all(&data).await?;
185 self.0.sync_all().await?;
186 Ok(())
187 }
188 }
189}