Skip to main content

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}