random_access_storage/
lib.rs

1#![deny(missing_docs)]
2#![cfg_attr(test, deny(warnings))]
3#![doc(test(attr(deny(warnings))))]
4//! # Abstract interface to implement random-access instances
5//!
6//! This crate defines the shared [RandomAccess] trait that makes it possible to create
7//! different backends for reading, writing and deleting bytes. With a shared interface,
8//! implementations can easily be swapped, depending on the needs and the environment.
9//!
10//! ## Known Implementations
11//!
12//! Full implementations of [RandomAccess] include:
13//!
14//! * [random-access-memory](https://docs.rs/random-access-memory) for in-memory storage
15//! * [random-access-disk](https://docs.rs/random-access-disk) for disk storage
16//!
17//! ## Examples
18//!
19//! Your own random-access backend can be implemented like this:
20//!
21//! ```
22//! use random_access_storage::{RandomAccess, RandomAccessError};
23//! use async_trait::async_trait;
24//!
25//! struct MyRandomAccess {
26//!   // Add fields here
27//! }
28//!
29//! #[async_trait]
30//! impl RandomAccess for MyRandomAccess {
31//!   async fn write(&mut self, _offset: u64, _data: &[u8]) -> Result<(), RandomAccessError> {
32//!     unimplemented!();
33//!   }
34//
35//!   async fn read(&mut self, _offset: u64, _length: u64) -> Result<Vec<u8>, RandomAccessError> {
36//!     unimplemented!();
37//!   }
38//!
39//!   async fn del(&mut self, _offset: u64, _length: u64) -> Result<(), RandomAccessError> {
40//!     unimplemented!();
41//!   }
42//!
43//!   async fn truncate(&mut self, _length: u64) -> Result<(), RandomAccessError> {
44//!     unimplemented!();
45//!   }
46//!
47//!   async fn len(&mut self) -> Result<u64, RandomAccessError> {
48//!     unimplemented!();
49//!   }
50//!
51//!   async fn is_empty(&mut self) -> Result<bool, RandomAccessError> {
52//!     unimplemented!();
53//!   }
54//!
55//!   async fn sync_all(&mut self) -> Result<(), RandomAccessError> {
56//!     unimplemented!();
57//!   }
58//! }
59//! ```
60use thiserror::Error;
61
62/// Error type for the [RandomAccess] trait methods.
63#[derive(Error, Debug)]
64pub enum RandomAccessError {
65  /// Given parameters are out of bounds.
66  #[error("{} out of bounds. {} < {}{}",
67          .end.as_ref().map_or_else(|| "Offset", |_| "Range"),
68          .length,
69          .offset,
70          .end.as_ref().map_or_else(String::new, |end| format!("..{}", end)))]
71  OutOfBounds {
72    /// Offset that was out of bounds
73    offset: u64,
74    /// If it was a range that was out of bounds, the end of the range.
75    end: Option<u64>,
76    /// The length in the implementation that was exceeded.
77    length: u64,
78  },
79  /// Unexpected [std::io::Error].
80  #[error("Unrecoverable input/output error occured.{}{}",
81          .return_code.as_ref().map_or_else(String::new, |rc| format!(" Return code: {}.", rc)),
82          .context.as_ref().map_or_else(String::new, |ctx| format!(" Context: {}.", ctx)))]
83  IO {
84    /// Optional system return code that caused the error.
85    return_code: Option<i32>,
86    /// Optional context of the error.
87    context: Option<String>,
88    /// Source of the error.
89    #[source]
90    source: std::io::Error,
91  },
92}
93
94impl From<std::io::Error> for RandomAccessError {
95  fn from(err: std::io::Error) -> Self {
96    Self::IO {
97      return_code: None,
98      context: None,
99      source: err,
100    }
101  }
102}
103
104/// Interface for reading from, writing to and deleting from a
105/// randomly accessible storage of bytes.
106#[async_trait::async_trait]
107pub trait RandomAccess {
108  /// Write bytes of `data` at an `offset` to the backend.
109  ///
110  /// # Errors
111  ///
112  /// * [RandomAccessError::OutOfBounds] if the backend has
113  /// a maximum capacity that would be exceeded by the write.
114  ///
115  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
116  async fn write(
117    &mut self,
118    offset: u64,
119    data: &[u8],
120  ) -> Result<(), RandomAccessError>;
121
122  /// Read a sequence of bytes at an `offset` from the backend.
123  ///
124  /// # Errors
125  ///
126  /// * [RandomAccessError::OutOfBounds] if
127  /// [RandomAccess::len] > `offset` + `length`.
128  ///
129  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
130  async fn read(
131    &mut self,
132    offset: u64,
133    length: u64,
134  ) -> Result<Vec<u8>, RandomAccessError>;
135
136  /// Delete a sequence of bytes of given `length` at an `offset` from the backend.
137  /// This either sets the bytes in the given slice to zeroes, or if
138  /// `offset` + `length` >= [RandomAccess::len()] is the same as
139  /// `truncate(offset)`.
140  ///
141  /// # Errors
142  ///
143  /// * [RandomAccessError::OutOfBounds] if [RandomAccess::len()] < `offset` + `length`.
144  ///
145  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
146  async fn del(
147    &mut self,
148    offset: u64,
149    length: u64,
150  ) -> Result<(), RandomAccessError>;
151
152  /// Resize the sequence of bytes so that [RandomAccess::len()] is set to
153  /// `length`. If `length` < [RandomAccess::len()], the bytes are disregarded.
154  /// If `length` > [RandomAccess::len()], the storage is zero-padded.
155  ///
156  /// # Errors
157  ///
158  /// * [RandomAccessError::OutOfBounds] if the backend has
159  /// a maximum capacity smaller than `length`.
160  ///
161  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
162  async fn truncate(&mut self, length: u64) -> Result<(), RandomAccessError>;
163
164  /// Get the size of the storage in bytes.
165  ///
166  /// # Errors
167  ///
168  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
169  async fn len(&mut self) -> Result<u64, RandomAccessError>;
170
171  /// Whether the storage is empty. For some storage backends it may be
172  /// cheaper to calculate whether the storage is empty than to calculate the
173  /// length.
174  ///
175  /// # Errors
176  ///
177  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
178  async fn is_empty(&mut self) -> Result<bool, RandomAccessError>;
179
180  /// Flush buffered data on the underlying storage resource.
181  ///
182  /// # Errors
183  ///
184  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
185  async fn sync_all(&mut self) -> Result<(), RandomAccessError>;
186}