diff options
author | Vika <vika@fireburn.ru> | 2022-07-10 00:55:20 +0300 |
---|---|---|
committer | Vika <vika@fireburn.ru> | 2022-07-10 00:55:20 +0300 |
commit | 1031d495d5b78d9b19dcdc414b6d7b0daf313bb2 (patch) | |
tree | a6ea6d347bc690c467df798f5576800d371b2254 /kittybox-rs/src/media/storage/mod.rs | |
parent | 2cbd19693115bf19da0ab888372cb1ff086967cd (diff) | |
download | kittybox-1031d495d5b78d9b19dcdc414b6d7b0daf313bb2.tar.zst |
media: media endpoint PoC
Supported features: - Streaming upload - Content-addressed storage - Metadata - MIME type (taken from Content-Type) - Length (I could use stat() for this one tho) - filename (for Content-Disposition: attachment, WIP)
Diffstat (limited to 'kittybox-rs/src/media/storage/mod.rs')
-rw-r--r-- | kittybox-rs/src/media/storage/mod.rs | 60 |
1 files changed, 48 insertions, 12 deletions
diff --git a/kittybox-rs/src/media/storage/mod.rs b/kittybox-rs/src/media/storage/mod.rs index ba880ab..cb8b38f 100644 --- a/kittybox-rs/src/media/storage/mod.rs +++ b/kittybox-rs/src/media/storage/mod.rs @@ -1,12 +1,39 @@ use async_trait::async_trait; +use axum::extract::multipart::Field; +use tokio_stream::Stream; +use bytes::Bytes; +use serde::{Deserialize, Serialize}; +use std::pin::Pin; pub mod file; +#[derive(Debug, Deserialize, Serialize)] +pub struct Metadata { + pub(super) content_type: String, + pub(super) filename: Option<String>, + pub(super) length: Option<usize> +} +impl From<&Field<'_>> for Metadata { + fn from(field: &Field<'_>) -> Self { + Self { + content_type: field.content_type() + .map(|i| i.to_owned()) + .or_else(|| Some("application/octet-stream".to_owned())) + .unwrap(), + filename: field.file_name() + .map(|i| i.to_owned()), + length: None + } + } +} + + #[derive(Debug, Clone, Copy)] pub enum ErrorKind { Backend, Permission, - Conflict, + Json, + NotFound, Other, } @@ -17,6 +44,12 @@ pub struct MediaStoreError { msg: String, } +impl MediaStoreError { + pub fn kind(&self) -> ErrorKind { + self.kind + } +} + impl std::error::Error for MediaStoreError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.source @@ -33,7 +66,8 @@ impl std::fmt::Display for MediaStoreError { match self.kind { ErrorKind::Backend => "media storage backend error", ErrorKind::Permission => "permission denied", - ErrorKind::Conflict => "conflict with existing data", + ErrorKind::Json => "failed to parse json", + ErrorKind::NotFound => "blob not found", ErrorKind::Other => "unknown media storage error", }, self.msg @@ -45,18 +79,20 @@ pub type Result<T> = std::result::Result<T, MediaStoreError>; #[async_trait] pub trait MediaStore: 'static + Send + Sync + Clone { - async fn write_streaming( + async fn write_streaming<T>( &self, - domain: url::Host, - filename: &str, - content: axum::extract::multipart::Field<'_>, - ) -> Result<()>; - async fn write(&self, domain: url::Host, filename: &str, content: &[u8]) -> Result<()>; + domain: &str, + metadata: Metadata, + content: T, + ) -> Result<String> + where + T: tokio_stream::Stream<Item = std::result::Result<bytes::Bytes, axum::extract::multipart::MultipartError>> + Unpin + Send; + async fn read_streaming( &self, - domain: url::Host, + domain: &str, filename: &str, - ) -> Result<tokio_util::io::ReaderStream<Box<dyn tokio::io::AsyncRead>>>; - async fn read(&self, domain: url::Host, filename: &str) -> Result<Vec<u8>>; - async fn delete(&self, domain: url::Host, filename: &str) -> Result<()>; + ) -> Result<(Metadata, Pin<Box<dyn Stream<Item = std::io::Result<Bytes>> + Send>>)>; + + async fn delete(&self, domain: &str, filename: &str) -> Result<()>; } |