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}