1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]
#![doc(test(attr(deny(warnings))))]
//! # Abstract interface to implement random-access instances
//!
//! This crate defines the shared [RandomAccess] trait that makes it possible to create
//! different backends for reading, writing and deleting bytes. With a shared interface,
//! implementations can easily be swapped, depending on the needs and the environment.
//!
//! ## Known Implementations
//!
//! Full implementations of [RandomAccess] include:
//!
//! * [random-access-memory](https://docs.rs/random-access-memory) for in-memory storage
//! * [random-access-disk](https://docs.rs/random-access-disk) for disk storage
//!
//! ## Examples
//!
//! Your own random-access backend can be implemented like this:
//!
//! ```
//! use random_access_storage::{RandomAccess, RandomAccessError};
//! use async_trait::async_trait;
//!
//! struct MyRandomAccess {
//!   // Add fields here
//! }
//!
//! #[async_trait]
//! impl RandomAccess for MyRandomAccess {
//!   async fn write(&mut self, _offset: u64, _data: &[u8]) -> Result<(), RandomAccessError> {
//!     unimplemented!();
//!   }
//
//!   async fn read(&mut self, _offset: u64, _length: u64) -> Result<Vec<u8>, RandomAccessError> {
//!     unimplemented!();
//!   }
//!
//!   async fn del(&mut self, _offset: u64, _length: u64) -> Result<(), RandomAccessError> {
//!     unimplemented!();
//!   }
//!
//!   async fn truncate(&mut self, _length: u64) -> Result<(), RandomAccessError> {
//!     unimplemented!();
//!   }
//!
//!   async fn len(&mut self) -> Result<u64, RandomAccessError> {
//!     unimplemented!();
//!   }
//!
//!   async fn is_empty(&mut self) -> Result<bool, RandomAccessError> {
//!     unimplemented!();
//!   }
//!
//!   async fn sync_all(&mut self) -> Result<(), RandomAccessError> {
//!     unimplemented!();
//!   }
//! }
//! ```
use thiserror::Error;

/// Error type for the [RandomAccess] trait methods.
#[derive(Error, Debug)]
pub enum RandomAccessError {
  /// Given parameters are out of bounds.
  #[error("{} out of bounds. {} < {}{}",
          .end.as_ref().map_or_else(|| "Offset", |_| "Range"),
          .length,
          .offset,
          .end.as_ref().map_or_else(String::new, |end| format!("..{}", end)))]
  OutOfBounds {
    /// Offset that was out of bounds
    offset: u64,
    /// If it was a range that was out of bounds, the end of the range.
    end: Option<u64>,
    /// The length in the implementation that was exceeded.
    length: u64,
  },
  /// Unexpected [std::io::Error].
  #[error("Unrecoverable input/output error occured.{}{}",
          .return_code.as_ref().map_or_else(String::new, |rc| format!(" Return code: {}.", rc)),
          .context.as_ref().map_or_else(String::new, |ctx| format!(" Context: {}.", ctx)))]
  IO {
    /// Optional system return code that caused the error.
    return_code: Option<i32>,
    /// Optional context of the error.
    context: Option<String>,
    /// Source of the error.
    #[source]
    source: std::io::Error,
  },
}

impl From<std::io::Error> for RandomAccessError {
  fn from(err: std::io::Error) -> Self {
    Self::IO {
      return_code: None,
      context: None,
      source: err,
    }
  }
}

/// Interface for reading from, writing to and deleting from a
/// randomly accessible storage of bytes.
#[async_trait::async_trait]
pub trait RandomAccess {
  /// Write bytes of `data` at an `offset` to the backend.
  ///
  /// # Errors
  ///
  /// * [RandomAccessError::OutOfBounds] if the backend has
  /// a maximum capacity that would be exceeded by the write.
  ///
  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
  async fn write(
    &mut self,
    offset: u64,
    data: &[u8],
  ) -> Result<(), RandomAccessError>;

  /// Read a sequence of bytes at an `offset` from the backend.
  ///
  /// # Errors
  ///
  /// * [RandomAccessError::OutOfBounds] if
  /// [RandomAccess::len] > `offset` + `length`.
  ///
  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
  async fn read(
    &mut self,
    offset: u64,
    length: u64,
  ) -> Result<Vec<u8>, RandomAccessError>;

  /// Delete a sequence of bytes of given `length` at an `offset` from the backend.
  /// This either sets the bytes in the given slice to zeroes, or if
  /// `offset` + `length` >= [RandomAccess::len()] is the same as
  /// `truncate(offset)`.
  ///
  /// # Errors
  ///
  /// * [RandomAccessError::OutOfBounds] if [RandomAccess::len()] < `offset` + `length`.
  ///
  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
  async fn del(
    &mut self,
    offset: u64,
    length: u64,
  ) -> Result<(), RandomAccessError>;

  /// Resize the sequence of bytes so that [RandomAccess::len()] is set to
  /// `length`. If `length` < [RandomAccess::len()], the bytes are disregarded.
  /// If `length` > [RandomAccess::len()], the storage is zero-padded.
  ///
  /// # Errors
  ///
  /// * [RandomAccessError::OutOfBounds] if the backend has
  /// a maximum capacity smaller than `length`.
  ///
  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
  async fn truncate(&mut self, length: u64) -> Result<(), RandomAccessError>;

  /// Get the size of the storage in bytes.
  ///
  /// # Errors
  ///
  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
  async fn len(&mut self) -> Result<u64, RandomAccessError>;

  /// Whether the storage is empty. For some storage backends it may be
  /// cheaper to calculate whether the storage is empty than to calculate the
  /// length.
  ///
  /// # Errors
  ///
  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
  async fn is_empty(&mut self) -> Result<bool, RandomAccessError>;

  /// Flush buffered data on the underlying storage resource.
  ///
  /// # Errors
  ///
  /// * [RandomAccessError::IO] if an unexpected IO error occurred.
  async fn sync_all(&mut self) -> Result<(), RandomAccessError>;
}