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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
|
use serde::{Deserialize, Serialize};
use shared_constants::bucket::PREFIX_BUCKET_BIND;
use space_system::{Space, SpaceError};
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt;
use std::fs::ReadDir;
use std::io::ErrorKind::NotFound;
use std::ops::{Deref, DerefMut};
use crate::{AsyncBucketTransferProtocol, Bucket};
#[cfg(test)]
mod test;
/// Represents a binding between a bucket and a URL.
///
/// `BucketBind` is a newtype wrapper around a `String` that stores a URL
/// associated with a bucket. It provides convenient access to the underlying
/// URL string through `Deref`, `DerefMut`, `Borrow`, and `Display` trait
/// implementations.
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq, Clone)]
pub struct BucketBind {
/// The index of the bucket bind
index: u8,
/// The URL associated with the bucket bind.
url: String,
}
impl BucketBind {
/// Creates a new `BucketBind` with the given URL.
fn new(index: u8, url: impl Into<String>) -> Self {
Self {
index,
url: url.into(),
}
}
/// Returns the index of the bucket bind.
pub fn get_index(&self) -> u8 {
self.index
}
/// Returns a reference to the URL of the bucket bind.
pub fn get_url(&self) -> &str {
&self.url
}
}
/// Reads all bucket bind records from the space.
///
/// This function traverses the root directory of the specified space, filters files
/// that start with a specific prefix (`PREFIX_BUCKET_BIND`), parses the index and URL
/// from each binding record, and returns them as `BucketBind` objects.
pub fn read_bucket_binds<Protocol: AsyncBucketTransferProtocol + Send + Sync>(
space: &Space<Bucket<Protocol>>,
) -> Result<Vec<BucketBind>, SpaceError> {
// Fixed prefix for bucket bind filenames
const PREFIX: &str = PREFIX_BUCKET_BIND;
// Open a read stream for the space root directory
let reader: ReadDir = space.read_dir(".")?;
let mut binds = Vec::new();
// Loop through each entry in the directory
for entry in reader {
let entry = entry?;
let file_name = entry.file_name();
let name = file_name.to_string_lossy().to_string();
// Only process files starting with the bind prefix
if let Some(suffix) = name.strip_prefix(PREFIX) {
// Extract the part after the prefix as the index string
// Attempt to parse the suffix as a u8 index value
if let Ok(index) = suffix.parse::<u8>() {
// Read the file content as the URL
let content = space.read_to_string(&name)?;
let url = content.trim().to_string();
// Add the parsed binding record to the list
binds.push(BucketBind::new(index, url));
}
}
}
// Sort by index before returning
binds.sort();
Ok(binds)
}
/// Writes a bucket bind record to the space.
///
/// This function creates or updates a binding between a bucket and a URL
/// at the specified index. It writes the URL content to a file named
/// with the prefix `PREFIX_BUCKET_BIND` followed by the zero-padded index.
pub fn write_bucket_bind<Protocol: AsyncBucketTransferProtocol + Send + Sync>(
space: &Space<Bucket<Protocol>>,
idx: u8,
url: &str,
) -> Result<(), SpaceError> {
const PREFIX: &str = PREFIX_BUCKET_BIND;
let file_name = format!("{}{:03}", PREFIX, idx);
space.write(&file_name, url.trim())
}
/// Reads a single bucket bind record from the space by index.
///
/// This function looks for a file named with the prefix `PREFIX_BUCKET_BIND`
/// followed by the zero-padded index, reads its content as a URL, and returns
/// the corresponding `BucketBind`. Returns `None` if the file does not exist.
pub fn read_bucket_bind<Protocol: AsyncBucketTransferProtocol + Send + Sync>(
space: &Space<Bucket<Protocol>>,
idx: u8,
) -> Result<Option<BucketBind>, SpaceError> {
const PREFIX: &str = PREFIX_BUCKET_BIND;
let file_name = format!("{}{:03}", PREFIX, idx);
match space.read_to_string(&file_name) {
Ok(content) => {
let url = content.trim().to_string();
Ok(Some(BucketBind::new(idx, url)))
}
Err(SpaceError::Io(err)) => {
if err.kind() == NotFound {
Ok(None)
} else {
Err(SpaceError::Io(err))
}
}
Err(e) => Err(e),
}
}
/// Checks whether a bucket bind record exists at the given index.
///
/// Returns `true` if a file named with the prefix `PREFIX_BUCKET_BIND` followed
/// by the zero-padded index exists in the space, `false` otherwise.
pub fn check_bucket_bind_exists<Protocol: AsyncBucketTransferProtocol + Send + Sync>(
space: &Space<Bucket<Protocol>>,
idx: u8,
) -> Result<bool, SpaceError> {
const PREFIX: &str = PREFIX_BUCKET_BIND;
let file_name = format!("{}{:03}", PREFIX, idx);
match space.read_to_string(&file_name) {
Ok(_) => Ok(true),
Err(SpaceError::Io(err)) => {
if err.kind() == NotFound {
Ok(false)
} else {
Err(SpaceError::Io(err))
}
}
Err(e) => Err(e),
}
}
/// Removes a bucket bind record from the space by index.
///
/// This function deletes the file named with the prefix `PREFIX_BUCKET_BIND`
/// followed by the zero-padded index from the space. Returns `Ok(())` if the
/// deletion succeeds, or an error if the operation fails (including if the
/// file does not exist).
pub fn remove_bucket_bind<Protocol: AsyncBucketTransferProtocol + Send + Sync>(
space: &Space<Bucket<Protocol>>,
idx: u8,
) -> Result<(), SpaceError> {
const PREFIX: &str = PREFIX_BUCKET_BIND;
let file_name = format!("{}{:03}", PREFIX, idx);
space.remove_file(&file_name)
}
impl Ord for BucketBind {
fn cmp(&self, other: &Self) -> Ordering {
self.index.cmp(&other.index)
}
}
impl PartialOrd for BucketBind {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Deref for BucketBind {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.url
}
}
impl DerefMut for BucketBind {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.url
}
}
impl Borrow<String> for BucketBind {
fn borrow(&self) -> &String {
&self.url
}
}
impl fmt::Display for BucketBind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.url)
}
}
|