Skip to content

Commit 4c55038

Browse files
committed
feat(foundationdb): allow prefix for Directory
1 parent be40732 commit 4c55038

File tree

3 files changed

+94
-20
lines changed

3 files changed

+94
-20
lines changed

foundationdb/src/directory/mod.rs

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ impl Default for DirectoryLayer {
151151
}
152152

153153
impl DirectoryLayer {
154-
/// `CreateOrOpen` opens the directory specified by path (relative to this
154+
/// `create_or_open` opens the directory specified by path (relative to this
155155
/// Directory), and returns the directory and its contents as a
156156
/// Subspace. If the directory does not exist, it is created
157157
/// (creating parent directories if necessary).
@@ -160,20 +160,33 @@ impl DirectoryLayer {
160160
txn: &Transaction,
161161
path: Vec<String>,
162162
) -> Result<Subspace, DirectoryError> {
163-
self.create_or_open_internal(txn, path, vec![], true, true)
163+
self.create_or_open_internal(txn, path, None, true, true)
164164
.await
165165
}
166166

167-
/// `Create` creates a directory specified by path (relative to this
167+
/// `create_or_open_with_prefix` is similar to `create_or_open`, but you can directly provide the keys
168+
/// that will be used as the content_subspace, instead of using the allocator to allocate.
169+
/// You must opened the directory with `allow_manual_prefixes` to do this.
170+
pub async fn create_or_open_with_prefix(
171+
&self,
172+
txn: &Transaction,
173+
path: Vec<String>,
174+
prefix: Vec<u8>,
175+
) -> Result<Subspace, DirectoryError> {
176+
self.create_or_open_internal(txn, path, Some(prefix), true, true)
177+
.await
178+
}
179+
180+
/// `create` creates a directory specified by path (relative to this
168181
/// Directory), and returns the directory and its contents as a
169182
/// Subspace (or ErrDirAlreadyExists if the directory already exists).
170183
pub async fn create(&self, txn: &Transaction, path: Vec<String>) -> Option<DirectoryError> {
171-
self.create_or_open_internal(txn, path, vec![], true, false)
184+
self.create_or_open_internal(txn, path, None, true, false)
172185
.await
173186
.err()
174187
}
175188

176-
/// `Open` opens the directory specified by path (relative to this Directory),
189+
/// `open` opens the directory specified by path (relative to this Directory),
177190
/// and returns the directory and its contents as a Subspace (or Err(DirNotExists)
178191
/// error if the directory does not exist, or ErrParentDirDoesNotExist if one of the parent
179192
/// directories in the path does not exist).
@@ -182,11 +195,11 @@ impl DirectoryLayer {
182195
txn: &Transaction,
183196
path: Vec<String>,
184197
) -> Result<Subspace, DirectoryError> {
185-
self.create_or_open_internal(txn, path, vec![], false, true)
198+
self.create_or_open_internal(txn, path, None, false, true)
186199
.await
187200
}
188201

189-
/// `Exists` returns true if the directory at path (relative to the default root directory) exists, and false otherwise.
202+
/// `exists` returns true if the directory at path (relative to the default root directory) exists, and false otherwise.
190203
pub async fn exists(
191204
&self,
192205
trx: &Transaction,
@@ -199,7 +212,7 @@ impl DirectoryLayer {
199212
}
200213
}
201214

202-
/// `Move moves` the directory from old_path to new_path(both relative to this
215+
/// `move_to` the directory from old_path to new_path(both relative to this
203216
/// Directory), and returns the directory (at its new location) and its
204217
/// contents as a Subspace. Move will return an error if a directory
205218
/// does not exist at oldPath, a directory already exists at newPath, or the
@@ -265,7 +278,7 @@ impl DirectoryLayer {
265278
Ok(content_subspace.to_owned())
266279
}
267280

268-
/// `Remove` the subdirectory of this Directory located at `path` and all of its subdirectories,
281+
/// `remove` the subdirectory of this Directory located at `path` and all of its subdirectories,
269282
/// as well as all of their contents.
270283
pub async fn remove(
271284
&self,
@@ -289,7 +302,7 @@ impl DirectoryLayer {
289302
}
290303
}
291304

292-
/// `List` returns the names of the immediate subdirectories of the default root directory as a slice of strings.
305+
/// `list` returns the names of the immediate subdirectories of the default root directory as a slice of strings.
293306
/// Each string is the name of the last component of a subdirectory's path.
294307
pub async fn list(
295308
&self,
@@ -309,13 +322,13 @@ impl DirectoryLayer {
309322
&self,
310323
trx: &Transaction,
311324
path: Vec<String>,
312-
prefix: Vec<u8>,
325+
prefix: Option<Vec<u8>>,
313326
allow_create: bool,
314327
allow_open: bool,
315328
) -> Result<Subspace, DirectoryError> {
316329
self.check_version(trx, allow_create).await?;
317330

318-
if !prefix.is_empty() && !self.allow_manual_prefixes {
331+
if prefix.is_some() && !self.allow_manual_prefixes {
319332
if self.path.is_empty() {
320333
return Err(DirectoryError::PrefixNotAllowed);
321334
}
@@ -327,7 +340,7 @@ impl DirectoryLayer {
327340
return Err(DirectoryError::NoPathProvided);
328341
}
329342

330-
let nodes = self.find_nodes(trx, path.to_owned()).await?;
343+
let mut nodes = self.find_nodes(trx, path.to_owned()).await?;
331344

332345
let last_node = nodes.last().expect("could not contain 0 nodes");
333346

@@ -352,21 +365,40 @@ impl DirectoryLayer {
352365
return Err(DirectoryError::PathDoesNotExists);
353366
}
354367

355-
let mut parent_subspace = self.content_subspace.clone();
368+
let (last, parent_nodes) = nodes.split_last_mut().expect("already checked");
356369

357-
for mut node in nodes {
358-
match node.content_subspace {
370+
// let's create parents first.
371+
let mut parent_subspace = self.content_subspace.clone();
372+
for parent_node in parent_nodes {
373+
match &parent_node.content_subspace {
359374
None => {
360375
// creating subspace
361376
let allocator = self.allocator.allocate(trx).await?;
362-
parent_subspace = node
377+
parent_subspace = parent_node
363378
.create_and_write_content_subspace(&trx, allocator, &parent_subspace)
364379
.await?;
365380
}
366-
Some(subspace) => parent_subspace = subspace.clone(),
381+
Some(subspace) => {
382+
parent_subspace = subspace.clone();
383+
}
384+
}
385+
}
386+
387+
// parents are created, let's create the final node
388+
match prefix {
389+
None => {
390+
let allocator = self.allocator.allocate(trx).await?;
391+
parent_subspace = last
392+
.create_and_write_content_subspace(&trx, allocator, &parent_subspace)
393+
.await?;
394+
Ok(parent_subspace)
395+
}
396+
Some(prefix) => {
397+
last.persist_prefix_as_content_subspace(&trx, prefix.to_owned())
398+
.await?;
399+
Ok(Subspace::from_bytes(prefix.as_ref()))
367400
}
368401
}
369-
Ok(parent_subspace)
370402
}
371403

372404
/// `check_version` is checking the Directory's version in FDB.

foundationdb/src/directory/node.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@ impl Node {
5858
self.persist_content_subspace(&trx, subspace).await
5959
}
6060

61+
/// persist a prefix as the content_subspace
62+
pub(crate) async fn persist_prefix_as_content_subspace(
63+
&mut self,
64+
trx: &Transaction,
65+
prefix: Vec<u8>,
66+
) -> Result<(), DirectoryError> {
67+
let key = self.node_subspace.to_owned();
68+
trx.set(key.bytes(), &*prefix);
69+
Ok(())
70+
}
71+
6172
/// `persist_content_subspace` will save the provided subspace as the `content_subspace`
6273
pub(crate) async fn persist_content_subspace(
6374
&mut self,

foundationdb/tests/directory.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,11 @@ fn test_create_or_open_directory() {
8383
))
8484
.expect("failed to run");
8585

86-
// moves
86+
futures::executor::block_on(test_prefix(&db, vec![0xFC, 0xFC])).expect("failed to run");
87+
futures::executor::block_on(test_not_allowed_prefix(&db, vec![0xFC, 0xFC]))
88+
.expect_err("should have failed");
8789

90+
// moves
8891
eprintln!("clearing all keys");
8992
let trx = db.create_trx().expect("cannot create txn");
9093
trx.clear_range(b"", b"\xff");
@@ -170,6 +173,34 @@ fn test_create_or_open_directory() {
170173
}
171174
}
172175

176+
async fn test_prefix(db: &Database, prefix: Vec<u8>) -> Result<(), DirectoryError> {
177+
let directory = DirectoryLayer {
178+
allow_manual_prefixes: true,
179+
..Default::default()
180+
};
181+
let trx = db.create_trx()?;
182+
183+
let subspace = directory
184+
.create_or_open_with_prefix(&trx, vec![String::from("bad_layer")], prefix.to_owned())
185+
.await?;
186+
187+
assert!(subspace.bytes().starts_with(prefix.as_slice()));
188+
Ok(())
189+
}
190+
191+
async fn test_not_allowed_prefix(db: &Database, prefix: Vec<u8>) -> Result<(), DirectoryError> {
192+
let directory = DirectoryLayer {
193+
..Default::default()
194+
};
195+
let trx = db.create_trx()?;
196+
197+
directory
198+
.create_or_open_with_prefix(&trx, vec![String::from("bad_layer")], prefix.to_owned())
199+
.await?;
200+
201+
Ok(())
202+
}
203+
173204
async fn test_create_then_delete(
174205
db: &Database,
175206
directory: &DirectoryLayer,

0 commit comments

Comments
 (0)