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}