nuts_backend/
lib.rs

1// MIT License
2//
3// Copyright (c) 2022-2024 Robin Doer
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to
7// deal in the Software without restriction, including without limitation the
8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9// sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21// IN THE SOFTWARE.
22
23//! The backend of a container
24//!
25//! # Create a container
26//!
27//! The [`Create`] trait is used to create a new instance of a [`Backend`].
28//!
29//! Mainly, is performs the following tasks:
30//!
31//! 1. It creates the [`Backend::Settings`] of the backend. The settings
32//!    contains runtime information which are stored in the header of the
33//!    container. Later, when the container is opened again, the settings are
34//!    extracted from the header and passed back to the backend.
35//! 2. The final [`Create::build()`] call creates the backend instance, which
36//!    is used by the container. Its gets the binary data of the header (which
37//!    already contains the binary encoded settings) and must store it at a
38//!    suitable location.
39//!
40//! # Open a container
41//!
42//! The [`Open`] trait is used to open an existing [`Backend`] instance.
43//!
44//! The container asks the trait for the binary header data using the
45//! [`ReceiveHeader`] trait. The implementation should load it from a suitable
46//! location.
47//!
48//! The final [`Open::build()`] call creates the backend instance, which is
49//! used by the container.
50
51use std::error;
52use std::fmt::Display;
53use std::str::FromStr;
54
55// The maximun size of the header.
56pub const HEADER_MAX_SIZE: usize = 512;
57
58/// Trait for binary conversion.
59///
60/// * [`Self::as_bytes`] is used to create a binary representation of this
61///   instance.
62/// * The [`Self::from_bytes`] method is used to convert the binary data back
63///   to its instance.
64pub trait Binary
65where
66    Self: Sized,
67{
68    /// Creates an instance of [`Self`] based on its binary representation.
69    ///
70    /// On success an instance of [`Self`] wrapped into a [`Some`] is returned.
71    /// On any error [`None`] is returned.
72    fn from_bytes(bytes: &[u8]) -> Option<Self>;
73
74    /// Creates the binary representation of [`self`].
75    fn as_bytes(&self) -> Vec<u8>;
76}
77
78/// Trait evaluates the size of an [id](Backend::Id).
79pub trait IdSize {
80    /// Returns the number of bytes needed to store the id.
81    fn size() -> usize;
82}
83
84/// Trait used to receive the header of a container.
85///
86/// The container uses the [`ReceiveHeader::get_header_bytes()`] method to ask
87/// the backend for the header bytes. The container does not know where the
88/// backend stores the header, that's why such a method is used. Not more than
89/// [`HEADER_MAX_SIZE`] bytes can be stored in the header.
90pub trait ReceiveHeader<B: Backend> {
91    /// Receives the binary header data from the backend.
92    ///
93    /// The container uses this method to ask the backend for the header bytes.
94    /// The container does not know where the backend stores the header, that's
95    /// why such a method is used. Not more than [`HEADER_MAX_SIZE`] bytes can
96    /// be stored in the header.
97    ///
98    /// The method should put the data into the `bytes` slice.
99    fn get_header_bytes(&mut self, bytes: &mut [u8; HEADER_MAX_SIZE]) -> Result<(), B::Err>;
100}
101
102/// Trait to configure the creation of a [`Backend`].
103///
104/// Should contain options used to create a specific backend. When a container
105/// is created, this trait is used to create the related backend.
106///
107/// The [`Create::settings()`] method returns an instance of the settings of
108/// this backend instance. The settings contains runtime configuration used by
109/// the backend. The container stores the settings (possibly encrypted) in the
110/// header of the container and should contain all settings/information needed
111/// by the backend. It is loaded again from the header when the container is
112/// opened.
113///
114/// Finally, the container calls [`Create::build()`] to create an instance of the
115/// [`Backend`]. The resulting backend instance should be able to handle all
116/// operations on it. The [`Create::build()`] method should validate all its
117/// settings before returning the backend instance!
118pub trait Create<B: Backend> {
119    /// Returns the settings of this backend instance.
120    ///
121    /// The settings contains runtime configuration used by the backend. The
122    /// container stores the settings (possibly encrypted) in the header of the
123    /// container and should contain all settings/information needed by the
124    /// backend. It is loaded again from the header when the container is
125    /// opened.
126    fn settings(&self) -> B::Settings;
127
128    /// Create an instance of the [`Backend`].
129    ///
130    /// The container calls [`Create::build()`] to create an instance of the
131    /// [`Backend`]. The resulting backend instance should be able to handle
132    /// all operations on it. The [`Create::build()`] method should validate
133    /// all its settings before returning the backend instance!
134    ///
135    /// The `header` argument contains the binary data of the (possibly
136    /// encrypted) header of the container. The method should persist the
137    /// header in the backend.
138    ///
139    /// If `overwrite` is `true`, then an existing backend instance should be
140    /// overwritten. If `overwrite` is set to `false` and the requested backend
141    /// instance exists, the build should fail.
142    fn build(self, header: [u8; HEADER_MAX_SIZE], overwrite: bool) -> Result<B, B::Err>;
143}
144
145/// Trait used to open a [`Backend`].
146///
147/// Should contain options used to open a specific backend. When a container
148/// is opened, this trait is used to open the related backend.
149///
150/// Any type that implements this `Open` trait must also implement the
151/// [`ReceiveHeader`] trait because the header of the container is read here.
152///
153/// Finally, the container calls [`Open::build()`] to create an instance of the
154/// [`Backend`]. The settings of the backend (extracted from the header) are
155/// passed to the [`Open::build()`] method and can be used to configure the
156/// [`Backend`] instance. The resulting backend instance should be able to
157/// handle all operations on it. The [`Open::build()`] method should validate
158/// all its settings before returning the backend instance!
159pub trait Open<B: Backend>: ReceiveHeader<B> {
160    /// Create an instance of the [`Backend`].
161    ///
162    /// The container calls [`Create::build()`] to create an instance of the
163    /// [`Backend`]. The settings of the backend (extracted from the header) are
164    /// passed to the method and can be used to configure the [`Backend`]. The
165    /// resulting backend instance should be able to handle all operations on
166    /// it. The method should validate all its settings before returning the
167    /// backend instance!
168    fn build(self, settings: B::Settings) -> Result<B, B::Err>;
169}
170
171/// Trait that describes a backend of a container.
172pub trait Backend: ReceiveHeader<Self>
173where
174    Self: Sized,
175{
176    /// Runtime configuration used by the backend.
177    ///
178    /// It should contain all settings/information needed by the backend. It is
179    /// loaded from the header when the backend is opened. See the [`Open`]
180    /// trait for more information on how the backend is opened.
181    ///
182    /// The [`Create`] trait is used to create the settings of a backend.
183    type Settings: Binary + Clone;
184
185    /// The error type used by methods of this trait.
186    type Err: error::Error + Send + Sync;
187
188    /// The id identifies a block in the storage. It is used everywhere you
189    /// need a pointer to a block.
190    type Id: Binary + Clone + Display + FromStr + IdSize + PartialEq;
191
192    /// Information of the backend.
193    ///
194    /// It includes information like public settings. The difference to
195    /// [`Backend::Settings`] is that [`Backend::Settings`] might include
196    /// sensible information which are removed from [`Backend::Info`].
197    type Info;
198
199    /// Returns information from the backend.
200    ///
201    /// It includes information like public settings. The difference to
202    /// [`Backend::Settings`] is that [`Backend::Settings`] might include
203    /// sensible information which are removed from [`Backend::Info`].
204    ///
205    /// # Errors
206    ///
207    /// On any error a self-defined [`Backend::Err`] is returned.
208    fn info(&self) -> Result<Self::Info, Self::Err>;
209
210    /// Returns the block size of the backend.
211    fn block_size(&self) -> u32;
212
213    /// Aquires a new block in the backend.
214    ///
215    /// Once aquired you should be able to [read](Backend::read) and
216    /// [write](Backend::write) from/to it.
217    ///
218    /// `buf` contains the initial data, which should be copied into the block.
219    ///
220    /// * A `buf` which is not large enough to fill the whole block must be
221    ///   rejected and an error must be returned.
222    /// * If `buf` holds more data than the [block-size](Backend::block_size),
223    ///   then only the first [block-size](Backend::block_size) bytes are
224    ///   copied into the block.
225    ///
226    /// By default an aquired block, which is not written yet, should return
227    /// an all-zero buffer.
228    ///
229    /// Returns the [id](Backend::Id) if the block.
230    ///
231    /// # Errors
232    ///
233    /// On any error a self-defined [`Backend::Err`] is returned.
234    fn aquire(&mut self, buf: &[u8]) -> Result<Self::Id, Self::Err>;
235
236    /// Releases a block again.
237    ///
238    /// A released block cannot be [read](Backend::read) and
239    /// [written](Backend::write), the [id](Self::Id) cannot be used
240    /// afterwards.
241    ///
242    /// # Errors
243    ///
244    /// On any error a self-defined [`Backend::Err`] is returned.
245    fn release(&mut self, id: Self::Id) -> Result<(), Self::Err>;
246
247    /// Reads a block from the backend.
248    ///
249    /// Reads the block with the given `id` and places the data in `buf`.
250    ///
251    /// You cannot read not more data than the
252    /// [block-size](Backend::block_size) bytes. If `buf` is larger, than not
253    /// the whole buffer is filled. In the other direction, if `buf` is not
254    /// large enough to store the whole block, `buf` is filled with the first
255    /// `buf.len()` bytes.
256    ///
257    /// The methods returns the number of bytes actually read, which cannot be
258    /// greater than the [block-size](Backend::block_size).
259    ///
260    /// # Errors
261    ///
262    /// On any error a self-defined [`Backend::Err`] is returned.
263    fn read(&mut self, id: &Self::Id, buf: &mut [u8]) -> Result<usize, Self::Err>;
264
265    /// Writes a block into the backend.
266    ///
267    /// Writes up to `buf.len()` bytes from the unencrypted `buf` buffer into
268    /// the block with the given `id`.
269    ///
270    /// * A `buf` which is not large enough to fill the whole block must be
271    ///   rejected and an error must be returned.
272    /// * If `buf` holds more data than the [block-size](Backend::block_size),
273    ///   then only the first [block-size](Backend::block_size) bytes are
274    ///   copied into the block.
275    ///
276    /// The method returns the number of bytes actually written.
277    ///
278    /// # Errors
279    ///
280    /// On any error a self-defined [`Backend::Err`] is returned.
281    fn write(&mut self, id: &Self::Id, buf: &[u8]) -> Result<usize, Self::Err>;
282
283    /// Puts the given `buf` into the header of the backend.
284    ///
285    /// The container uses this method to ask the backend to put data into the
286    /// header. The container does not know where the backend stores the
287    /// header, that's why such a method is used. Not more than
288    /// [`HEADER_MAX_SIZE`] bytes can be stored in the header.
289    fn write_header(&mut self, buf: &[u8; HEADER_MAX_SIZE]) -> Result<(), Self::Err>;
290
291    /// Deletes the entire instance and all traces.
292    ///
293    /// The method must not fail!
294    fn delete(self);
295}