feat(pubky): shallow list folder and files

This commit is contained in:
nazeh
2024-08-07 18:52:04 +03:00
parent dd2c9ea93c
commit e10cf77fe2
3 changed files with 152 additions and 18 deletions

View File

@@ -98,39 +98,82 @@ impl DB {
cursor: Option<String>,
shallow: bool,
) -> anyhow::Result<Vec<String>> {
// Vector to store results
let mut results = Vec::new();
let limit = limit.unwrap_or(MAX_LIST_LIMIT).min(MAX_LIST_LIMIT);
// Remove directories from the cursor;
let cursor = cursor
.as_deref()
.and_then(|mut cursor| cursor.rsplit('/').next())
.unwrap_or(if reverse { "~" } else { "" });
let cursor = format!("{path}{cursor}");
let mut cursor = cursor.as_str();
let limit = limit.unwrap_or(MAX_LIST_LIMIT).min(MAX_LIST_LIMIT);
// Vector to store results
let mut results = Vec::new();
let mut cursor = format!("{path}{cursor}");
// Fetch data based on direction
if reverse {
for _ in 0..limit {
if let Some((key, _)) = self.tables.entries.get_lower_than(txn, cursor)? {
if let Some((key, _)) = self.tables.entries.get_lower_than(txn, &cursor)? {
if !key.starts_with(path) {
break;
}
cursor = key;
results.push(format!("pubky://{}", key))
if shallow {
let mut split = key[path.len()..].split('/');
let item = split.next().expect("should not be reachable");
let is_directory = split.next().is_some();
cursor = format!(
"{}{}",
&key[..(path.len() + item.len())],
// `.` is immediately lower than `/`
if is_directory { "." } else { "" }
);
let url = format!(
"pubky://{path}{item}{}",
if is_directory { "/" } else { "" }
);
results.push(url);
} else {
cursor = key.to_string();
results.push(format!("pubky://{}", key))
}
};
}
} else {
for _ in 0..limit {
if let Some((key, _)) = self.tables.entries.get_greater_than(txn, cursor)? {
if let Some((key, _)) = self.tables.entries.get_greater_than(txn, &cursor)? {
if !key.starts_with(path) {
break;
}
cursor = key;
results.push(format!("pubky://{}", key))
if shallow {
let mut split = key[path.len()..].split('/');
let item = split.next().expect("should not be reachable");
let is_directory = split.next().is_some();
cursor = format!(
"{}{}",
&key[..(path.len() + item.len())],
// `0` is immediately higher than `/`
if is_directory { "0" } else { "" }
);
let url = format!(
"pubky://{path}{item}{}",
if is_directory { "/" } else { "" }
);
results.push(url);
} else {
cursor = key.to_string();
results.push(format!("pubky://{}", key))
}
};
}
};

View File

@@ -10,6 +10,7 @@ pub struct ListBuilder<'a> {
limit: Option<u16>,
cursor: Option<&'a str>,
client: &'a PubkyClient,
shallow: bool,
}
impl<'a> ListBuilder<'a> {
@@ -21,12 +22,13 @@ impl<'a> ListBuilder<'a> {
limit: None,
cursor: None,
reverse: false,
shallow: false,
}
}
/// Set the `reverse` option.
pub fn reverse(mut self, reverse: bool) -> Self {
self.reverse = reverse;
pub fn reverse(mut self) -> Self {
self.reverse = true;
self
}
@@ -44,6 +46,11 @@ impl<'a> ListBuilder<'a> {
self
}
pub fn shallow(mut self) -> Self {
self.shallow = true;
self
}
/// Send the list request.
///
/// Returns a list of Pubky URLs of the files in the path of the `url`
@@ -68,6 +75,10 @@ impl<'a> ListBuilder<'a> {
query.append_key_only("reverse");
}
if self.shallow {
query.append_key_only("shallow");
}
if let Some(limit) = self.limit {
query.append_pair("limit", &limit.to_string());
}

View File

@@ -319,7 +319,7 @@ mod tests {
let list = client
.list(url.as_str())
.unwrap()
.reverse(true)
.reverse()
.send()
.await
.unwrap();
@@ -340,7 +340,7 @@ mod tests {
let list = client
.list(url.as_str())
.unwrap()
.reverse(true)
.reverse()
.limit(2)
.send()
.await
@@ -360,7 +360,7 @@ mod tests {
let list = client
.list(url.as_str())
.unwrap()
.reverse(true)
.reverse()
.limit(2)
.cursor("d.txt")
.send()
@@ -377,4 +377,84 @@ mod tests {
);
}
}
#[tokio::test]
async fn list_shallow() {
let testnet = Testnet::new(10);
let server = Homeserver::start_test(&testnet).await.unwrap();
let client = PubkyClient::test(&testnet);
let keypair = Keypair::random();
client.signup(&keypair, &server.public_key()).await.unwrap();
let urls = vec![
format!("pubky://{}/pub/a.com/a.txt", keypair.public_key()),
format!("pubky://{}/pub/example.com/a.txt", keypair.public_key()),
format!("pubky://{}/pub/example.com/b.txt", keypair.public_key()),
format!("pubky://{}/pub/example.com/c.txt", keypair.public_key()),
format!("pubky://{}/pub/example.com/d.txt", keypair.public_key()),
format!("pubky://{}/pub/example.con/d.txt", keypair.public_key()),
format!("pubky://{}/pub/example.con", keypair.public_key()),
format!("pubky://{}/pub/file", keypair.public_key()),
format!("pubky://{}/pub/file2", keypair.public_key()),
format!("pubky://{}/pub/z.com/a.txt", keypair.public_key()),
];
for url in urls {
client.put(url.as_str(), &[0]).await.unwrap();
}
let url = format!("pubky://{}/pub/", keypair.public_key());
{
let list = client
.list(url.as_str())
.unwrap()
.shallow()
.send()
.await
.unwrap();
assert_eq!(
list,
vec![
format!("pubky://{}/pub/a.com/", keypair.public_key()),
format!("pubky://{}/pub/example.com/", keypair.public_key()),
format!("pubky://{}/pub/example.con", keypair.public_key()),
format!("pubky://{}/pub/example.con/", keypair.public_key()),
format!("pubky://{}/pub/file", keypair.public_key()),
format!("pubky://{}/pub/file2", keypair.public_key()),
format!("pubky://{}/pub/z.com/", keypair.public_key()),
],
"normal list shallow"
);
}
{
let list = client
.list(url.as_str())
.unwrap()
.shallow()
.reverse()
.send()
.await
.unwrap();
assert_eq!(
list,
vec![
format!("pubky://{}/pub/z.com/", keypair.public_key()),
format!("pubky://{}/pub/file2", keypair.public_key()),
format!("pubky://{}/pub/file", keypair.public_key()),
format!("pubky://{}/pub/example.con/", keypair.public_key()),
format!("pubky://{}/pub/example.con", keypair.public_key()),
format!("pubky://{}/pub/example.com/", keypair.public_key()),
format!("pubky://{}/pub/a.com/", keypair.public_key()),
],
"reverse list shallow"
);
}
}
}