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
//! The filesystem SDK module.
mod error;
mod file;
pub use file::MenmosFile;
use futures::TryStreamExt;
use snafu::prelude::*;
use crate::util;
use crate::{ClientRC, FileMetadata};
pub use error::FsError;
use error::*;
/// The entrypoint structure of the filesystem SDK.
#[derive(Clone)]
pub struct MenmosFs {
client: ClientRC,
}
impl MenmosFs {
#[doc(hidden)]
pub fn new(client: ClientRC) -> Self {
Self { client }
}
/// Create a new file with the provided metadata.
///
/// This function will return a handle to the created file, at offset 0.
///
/// # Examples
/// ```no_run
/// use menmos::FileMetadata;
/// # use menmos::fs::MenmosFs;
///
/// # #[tokio::main]
/// # async fn main() {
/// # let client = menmos_client::Client::new("a", "b", "c").await.unwrap();
/// # let fs = MenmosFs::new(std::sync::Arc::new(client));
/// let handle = fs.create_file(FileMetadata::new("test.txt").with_tag("sdk_file"))
/// .await
/// .unwrap();
/// # }
/// ```
pub async fn create_file(&self, metadata: FileMetadata) -> Result<MenmosFile> {
MenmosFile::create(self.client.clone(), metadata).await
}
async fn remove_blob_unchecked<S: AsRef<str>>(&self, id: S) -> Result<()> {
// TODO: Update the menmos client so that Client::delete takes a ref.
self.client
.delete(String::from(id.as_ref()))
.await
.with_context(|_| BlobDeleteSnafu {
blob_id: String::from(id.as_ref()),
})?;
Ok(())
}
/// Remove a blob by its ID.
///
/// If the specified blob ID does not exist, no error is returned and no operation
/// is performed.
///
/// # Errors
///
/// If this function is called with an ID corresponding to a blob that is _not_
/// a file, an error variant will be returned.
///
/// # Examples
/// ```no_run
/// # use menmos::fs::MenmosFs;
/// # #[tokio::main]
/// # async fn main() {
/// # let client = menmos_client::Client::new("a", "b", "c").await.unwrap();
/// # let fs = MenmosFs::new(std::sync::Arc::new(client));
/// fs.remove("<a file blob ID>").await.unwrap();
/// # }
/// ```
pub async fn remove<S: AsRef<str>>(&self, id: S) -> Result<()> {
match util::get_meta_if_exists(&self.client, id.as_ref())
.await
.context(FileRemoveSnafu {
blob_id: String::from(id.as_ref()),
})? {
Some(_) => self.remove_blob_unchecked(id).await,
None => Ok(()),
}
}
/// Recursively remove a blob along with all its children.
///
/// If the specified blob ID does not exist, no error is returned and no operation
/// is performed.
///
/// # Errors
///
/// If this function is called with an ID corresponding to a blob that is _not_
/// a directory, an error variant will be returned.
///
/// # Examples
/// ```no_run
/// # use menmos::fs::MenmosFs;
/// # #[tokio::main]
/// # async fn main() {
/// # let client = menmos_client::Client::new("a", "b", "c").await.unwrap();
/// # let fs = MenmosFs::new(std::sync::Arc::new(client));
/// fs.remove_all("<a dir blob ID>").await.unwrap();
/// # }
/// ```
pub async fn remove_all<S: AsRef<str>>(&self, id: S) -> Result<()> {
match util::get_meta_if_exists(&self.client, id.as_ref())
.await
.context(DirRemoveSnafu)?
{
Some(_) => {
// We don't do the deletion recursively because recursivity + async requires a lot of indirection.
let mut delete_stack: Vec<MenmosFile> = vec![];
while let Some(target) = delete_stack.pop() {
let children = target.list().try_collect::<Vec<_>>().await?;
delete_stack.extend(children.into_iter());
self.remove_blob_unchecked(target.id()).await?;
}
Ok(())
}
None => Ok(()),
}
}
}