use anyhow::{Context, Result};
use reblessive::tree::Stk;
use super::CursorDoc;
use super::args::Optional;
use crate::buc::BucketController;
use crate::buc::store::ObjectKey;
use crate::ctx::FrozenContext;
use crate::dbs::Options;
use crate::val::{File, Object, Value};
pub async fn put(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(file, value): (File, Value),
) -> Result<Value> {
let mut controller = BucketController::new(stk, ctx, opt, doc, &file.bucket).await?;
controller.put(&ObjectKey::new(file.key), value).await?;
Ok(Value::None)
}
pub async fn put_if_not_exists(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(file, value): (File, Value),
) -> Result<Value> {
let mut controller = BucketController::new(stk, ctx, opt, doc, &file.bucket).await?;
controller.put_if_not_exists(&ObjectKey::new(file.key), value).await?;
Ok(Value::None)
}
pub async fn get(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(file,): (File,),
) -> Result<Value> {
let mut controller = BucketController::new(stk, ctx, opt, doc, &file.bucket).await?;
let res = controller.get(&ObjectKey::new(file.key)).await?;
Ok(res.map(Value::Bytes).unwrap_or_default())
}
pub async fn head(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(file,): (File,),
) -> Result<Value> {
let mut controller = BucketController::new(stk, ctx, opt, doc, &file.bucket).await?;
let res = controller.head(&ObjectKey::new(file.key)).await?;
Ok(res.map(|v| v.into_value(file.bucket)).unwrap_or_default())
}
pub async fn delete(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(file,): (File,),
) -> Result<Value> {
let mut controller = BucketController::new(stk, ctx, opt, doc, &file.bucket).await?;
controller.delete(&ObjectKey::new(file.key)).await?;
Ok(Value::None)
}
pub async fn copy(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(src, dst): (File, Value),
) -> Result<Value> {
let dst = value_to_file(dst)?;
if dst.bucket_matches_source(&src.bucket) {
let mut controller = BucketController::new(stk, ctx, opt, doc, &src.bucket).await?;
let src_key = ObjectKey::new(src.key);
let dst_key = ObjectKey::new(dst.key);
controller.copy(&src_key, dst_key).await?;
} else {
let data = {
let mut src_controller = BucketController::new(stk, ctx, opt, doc, &src.bucket).await?;
let src_key = ObjectKey::new(src.key);
let Some(data) = src_controller.get(&src_key).await? else {
return Err(anyhow::anyhow!("Source file does not exist"));
};
data
};
let DestinationFile {
bucket,
key,
} = dst;
let dst_bucket = bucket.context("destination bucket must be set")?;
let dst_key = ObjectKey::new(key);
let mut dst_controller = BucketController::new(stk, ctx, opt, doc, &dst_bucket).await?;
dst_controller.put(&dst_key, data.into()).await?;
}
Ok(Value::None)
}
pub async fn copy_if_not_exists(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(src, dst): (File, Value),
) -> Result<Value> {
let dst = value_to_file(dst)?;
if dst.bucket_matches_source(&src.bucket) {
let mut controller = BucketController::new(stk, ctx, opt, doc, &src.bucket).await?;
let src_key = ObjectKey::new(src.key);
let dst_key = ObjectKey::new(dst.key);
controller.copy_if_not_exists(&src_key, dst_key).await?;
} else {
let data = {
let mut src_controller = BucketController::new(stk, ctx, opt, doc, &src.bucket).await?;
let src_key = ObjectKey::new(src.key);
let Some(data) = src_controller.get(&src_key).await? else {
return Err(anyhow::anyhow!("Source file does not exist"));
};
data
};
let DestinationFile {
bucket,
key,
} = dst;
let dst_bucket = bucket.context("destination bucket must be set")?;
let dst_key = ObjectKey::new(key);
let mut dst_controller = BucketController::new(stk, ctx, opt, doc, &dst_bucket).await?;
dst_controller.put_if_not_exists(&dst_key, data.into()).await?;
}
Ok(Value::None)
}
pub async fn rename(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(file, target): (File, String),
) -> Result<Value> {
let target = ObjectKey::new(target);
let mut controller = BucketController::new(stk, ctx, opt, doc, &file.bucket).await?;
controller.rename(&ObjectKey::new(file.key), target).await?;
Ok(Value::None)
}
pub async fn rename_if_not_exists(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(file, target): (File, String),
) -> Result<Value> {
let target = ObjectKey::new(target);
let mut controller = BucketController::new(stk, ctx, opt, doc, &file.bucket).await?;
controller.rename_if_not_exists(&ObjectKey::new(file.key), target).await?;
Ok(Value::None)
}
pub async fn exists(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(file,): (File,),
) -> Result<Value> {
let mut controller = BucketController::new(stk, ctx, opt, doc, &file.bucket).await?;
let exists = controller.exists(&ObjectKey::new(file.key)).await?;
Ok(Value::Bool(exists))
}
pub async fn list(
(stk, ctx, opt, doc): (&mut Stk, &FrozenContext, &Options, Option<&CursorDoc>),
(bucket, Optional(opts)): (String, Optional<Object>),
) -> Result<Value> {
let mut controller = BucketController::new(stk, ctx, opt, doc, &bucket).await?;
let opts = opts.map(|v| v.try_into()).transpose()?.unwrap_or_default();
let res = controller
.list(&opts)
.await?
.into_iter()
.map(|v| v.into_value(bucket.clone()))
.collect::<Vec<Value>>()
.into();
Ok(res)
}
pub fn bucket((file,): (File,)) -> Result<Value> {
Ok(file.bucket.into())
}
pub fn key((file,): (File,)) -> Result<Value> {
Ok(file.key.into())
}
struct DestinationFile {
bucket: Option<String>,
key: String,
}
impl DestinationFile {
pub fn bucket_matches_source(&self, other: &str) -> bool {
self.bucket.as_ref().map(|b| b == other).unwrap_or(true)
}
}
fn value_to_file(value: Value) -> Result<DestinationFile> {
match value {
Value::File(file) => Ok(DestinationFile {
bucket: Some(file.bucket),
key: file.key,
}),
Value::String(s) => Ok(DestinationFile {
bucket: None,
key: s.into_string(),
}),
_ => Err(anyhow::anyhow!("Invalid destination file value")),
}
}