use crate::tina::data::binary::{BinaryContent, DataContent, MemoryData, StoredFileData};
use crate::tina::data::AppResult;
use crate::{app_error_from, app_system_error};
use bytes::Bytes;
use futures::executor::block_on;
use futures::{AsyncRead, AsyncSeek, Stream};
use futures_util::lock::Mutex;
use httpdate::HttpDate;
use mime::Mime;
use serde::de::Visitor;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Debug, Display, Formatter};
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::ops::{Deref, DerefMut};
use std::panic::AssertUnwindSafe;
use std::path::Path;
use std::pin::Pin;
use std::str::FromStr;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::time::SystemTime;
use std::{any::type_name, cell::RefCell};
use tokio::io::ReadBuf;
use utoipa::{
openapi::{schema::Schema, KnownFormat, ObjectBuilder, RefOr, SchemaFormat, SchemaType},
ToSchema,
};
#[derive(Debug, Clone)]
pub struct FileContent {
content: Arc<Mutex<Box<dyn BinaryContent<Item = AppResult<DataContent>>>>>,
last_modified: HttpDate,
size: Option<u64>,
}
impl FileContent {
pub fn from_content(data: impl BinaryContent) -> Self {
let last_modified = data.last_modified();
let size = data.get_size();
Self {
content: Arc::new(Mutex::new(Box::new(data))),
last_modified,
size,
}
}
pub fn from_stored_file_data(data: StoredFileData) -> Self {
Self::from_content(data)
}
pub fn from_memory_data(data: MemoryData) -> Self {
Self::from_content(data)
}
pub fn last_modified(&self) -> HttpDate {
self.last_modified
}
pub fn get_size(&self) -> Option<u64> {
self.size
}
pub fn into_sync(self) -> AppResult<SyncFileContent> {
let content = self.content;
let func = move || match Arc::try_unwrap(content) {
Ok(v) => Ok(v.into_inner()),
Err(_) => Err(app_system_error!("failed to unwrap data from Arc! maybe it have more than 1 reference")),
};
match std::panic::catch_unwind(AssertUnwindSafe(func)) {
Ok(r) => match r {
Ok(content) => Ok(SyncFileContent {
content,
last_modified: self.last_modified,
size: self.size,
}),
Err(err) => Err(err),
},
Err(_) => Err(app_system_error!("panic error to convert to SyncFileContent!")),
}
}
}
impl Deref for FileContent {
type Target = Arc<Mutex<Box<dyn BinaryContent<Item = AppResult<DataContent>>>>>;
fn deref(&self) -> &Self::Target {
&self.content
}
}
impl Stream for FileContent {
type Item = AppResult<DataContent>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut content = match futures::Future::poll(Pin::new(&mut self.content.lock()), cx) {
Poll::Ready(lock) => lock,
Poll::Pending => return Poll::Pending,
};
Stream::poll_next(Pin::new(content.deref_mut()), cx)
}
}
impl AsyncRead for FileContent {
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {
let mut content = match futures::Future::poll(Pin::new(&mut self.content.lock()), cx) {
Poll::Ready(lock) => lock,
Poll::Pending => return Poll::Pending,
};
AsyncRead::poll_read(Pin::new(content.deref_mut()), cx, buf)
}
}
impl AsyncSeek for FileContent {
fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) -> Poll<std::io::Result<u64>> {
let mut content = match futures::Future::poll(Pin::new(&mut self.content.lock()), cx) {
Poll::Ready(lock) => lock,
Poll::Pending => return Poll::Pending,
};
AsyncSeek::poll_seek(Pin::new(content.deref_mut()), cx, pos)
}
}
#[derive(Debug)]
pub struct SyncFileContent {
content: Box<dyn BinaryContent<Item = AppResult<DataContent>>>,
last_modified: HttpDate,
size: Option<u64>,
}
impl SyncFileContent {
pub fn from_content(data: impl BinaryContent) -> Self {
let last_modified = data.last_modified();
let size = data.get_size();
Self {
content: Box::new(data),
last_modified,
size,
}
}
pub fn from_stored_file_data(data: StoredFileData) -> Self {
Self::from_content(data)
}
pub fn from_memory_data(data: MemoryData) -> Self {
Self::from_content(data)
}
pub fn last_modified(&self) -> HttpDate {
self.last_modified
}
pub fn get_size(&self) -> Option<u64> {
self.size
}
pub fn into_async(self) -> FileContent {
FileContent {
content: Arc::new(Mutex::new(self.content)),
last_modified: self.last_modified,
size: self.size,
}
}
}
impl Deref for SyncFileContent {
type Target = dyn BinaryContent<Item = AppResult<DataContent>>;
fn deref(&self) -> &Self::Target {
self.content.deref()
}
}
impl DerefMut for SyncFileContent {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.content
}
}
impl Read for SyncFileContent {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
Read::read(self.content.deref_mut(), buf)
}
}
impl Seek for SyncFileContent {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
Seek::seek(self.content.deref_mut(), pos)
}
}
#[derive(Debug, Clone)]
pub struct FileData {
name: String,
original_filename: Option<String>,
content_type: String,
last_modified: HttpDate,
pub(crate) content: FileContent,
}
impl FileData {
pub fn from_file(
name: &str,
original_filename: Option<String>,
content_type: &str,
store_path: &str,
delete_on_drop: bool,
) -> AppResult<FileData> {
let path = Path::new(store_path);
let file = File::open(path).map_err(app_error_from!())?;
let file_metadata = file.metadata().map_err(app_error_from!())?;
let modified_time = file_metadata.modified().map_err(app_error_from!())?;
Ok(FileData {
name: name.to_owned(),
original_filename,
content_type: content_type.to_owned(),
last_modified: HttpDate::from(modified_time),
content: FileContent::from_stored_file_data(StoredFileData::new(store_path, delete_on_drop)),
})
}
pub fn from_local_file(store_path: &str, delete_on_drop: bool) -> AppResult<FileData> {
let path = Path::new(store_path);
let file_name = path
.file_name()
.ok_or_else(|| app_system_error!("file not exists: {}", store_path))?
.to_str()
.ok_or_else(|| app_system_error!("file not exists: {}", store_path))?;
let file = File::open(path).map_err(|_| app_system_error!("file not exists: {}", store_path))?;
let file_metadata = file.metadata().map_err(app_error_from!())?;
let modified_time = file_metadata.modified().map_err(app_error_from!())?;
let content_type = new_mime_guess::from_path(store_path).first();
let content_type = match content_type {
None => Mime::from_str("application/octet-stream").map_err(app_error_from!())?,
Some(v) => v,
};
Ok(FileData {
name: "".to_string(),
original_filename: Some(file_name.to_string()),
content_type: content_type.to_string(),
last_modified: HttpDate::from(modified_time),
content: FileContent::from_stored_file_data(StoredFileData::new(store_path, delete_on_drop)),
})
}
pub fn from_bytes(name: &str, original_filename: Option<String>, content_type: &str, buf: Bytes) -> FileData {
FileData {
name: name.to_owned(),
original_filename,
content_type: content_type.to_owned(),
last_modified: HttpDate::from(SystemTime::now()),
content: FileContent::from_memory_data(MemoryData::new(DataContent::BYTES(buf))),
}
}
pub fn from_content(name: &str, original_filename: Option<String>, content_type: &str, content: FileContent) -> FileData {
let last_modified = content.last_modified();
FileData {
name: name.to_owned(),
original_filename,
content_type: content_type.to_owned(),
last_modified,
content,
}
}
pub fn get_name(&self) -> &str {
self.name.as_str()
}
pub fn name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
pub fn get_original_filename(&self) -> Option<&str> {
match self.original_filename.as_ref() {
None => None,
Some(v) => Some(v.as_str()),
}
}
pub fn get_last_modified_time(&self) -> HttpDate {
self.last_modified
}
pub fn set_last_modified_time(&mut self, last_modified: HttpDate) {
self.last_modified = last_modified;
}
pub fn get_content_type(&self) -> &str {
self.content_type.as_str()
}
pub async fn is_valid(&self) -> bool {
let lock = self.content.lock().await;
lock.is_valid()
}
pub fn into_sync(self) -> AppResult<SyncFileData> {
Ok(SyncFileData {
name: self.name,
original_filename: self.original_filename,
content_type: self.content_type,
last_modified: self.last_modified,
content: self.content.into_sync()?,
})
}
}
impl Stream for FileData {
type Item = AppResult<DataContent>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut content = match futures::Future::poll(Pin::new(&mut self.content.lock()), cx) {
Poll::Ready(lock) => lock,
Poll::Pending => return Poll::Pending,
};
Stream::poll_next(Pin::new(&mut *content), cx)
}
}
impl AsyncSeek for FileData {
fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) -> Poll<std::io::Result<u64>> {
let mut content = match futures::Future::poll(Pin::new(&mut self.content.lock()), cx) {
Poll::Ready(lock) => lock,
Poll::Pending => return Poll::Pending,
};
AsyncSeek::poll_seek(Pin::new(content.deref_mut()), cx, pos)
}
}
impl AsyncRead for FileData {
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {
let mut content = match futures::Future::poll(Pin::new(&mut self.content.lock()), cx) {
Poll::Ready(lock) => lock,
Poll::Pending => return Poll::Pending,
};
AsyncRead::poll_read(Pin::new(content.deref_mut()), cx, buf)
}
}
impl tokio::io::AsyncRead for FileData {
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll<std::io::Result<()>> {
let mut content = match futures::Future::poll(Pin::new(&mut self.content.lock()), cx) {
Poll::Ready(lock) => lock,
Poll::Pending => return Poll::Pending,
};
tokio::io::AsyncRead::poll_read(Pin::new(content.deref_mut()), cx, buf)
}
}
impl Read for FileData {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let mut content = block_on(self.content.content.lock());
content.read(buf)
}
}
impl Seek for FileData {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
let mut content = block_on(self.content.content.lock());
content.seek(pos)
}
}
impl Serialize for FileData {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
self.original_filename.serialize(serializer)
}
}
impl Display for FileData {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.original_filename.fmt(f)
}
}
impl Default for FileData {
fn default() -> Self {
FileData::from_bytes("", Some("".to_owned()), "", Bytes::new())
}
}
impl<'a> ToSchema<'a> for FileData {
fn schema() -> (&'a str, RefOr<Schema>) {
(
type_name::<FileData>(),
RefOr::T(Schema::from(
ObjectBuilder::new().schema_type(SchemaType::String).format(Some(SchemaFormat::KnownFormat(KnownFormat::Binary))),
)),
)
}
}
#[derive(Debug)]
pub struct SyncFileData {
name: String,
original_filename: Option<String>,
content_type: String,
last_modified: HttpDate,
pub(crate) content: SyncFileContent,
}
impl SyncFileData {
pub fn from_file(
name: &str,
original_filename: Option<String>,
content_type: &str,
store_path: &str,
delete_on_drop: bool,
) -> AppResult<SyncFileData> {
let path = Path::new(store_path);
let file = File::open(path).map_err(app_error_from!())?;
let file_metadata = file.metadata().map_err(app_error_from!())?;
let modified_time = file_metadata.modified().map_err(app_error_from!())?;
Ok(SyncFileData {
name: name.to_owned(),
original_filename,
content_type: content_type.to_owned(),
last_modified: HttpDate::from(modified_time),
content: SyncFileContent::from_stored_file_data(StoredFileData::new(store_path, delete_on_drop)),
})
}
pub fn from_local_file(store_path: &str, delete_on_drop: bool) -> AppResult<SyncFileData> {
let path = Path::new(store_path);
let file_name = path
.file_name()
.ok_or_else(|| app_system_error!("file not exists: {}", store_path))?
.to_str()
.ok_or_else(|| app_system_error!("file not exists: {}", store_path))?;
let file = File::open(path).map_err(|_| app_system_error!("file not exists: {}", store_path))?;
let file_metadata = file.metadata().map_err(app_error_from!())?;
let modified_time = file_metadata.modified().map_err(app_error_from!())?;
let content_type = new_mime_guess::from_path(store_path).first();
let content_type = match content_type {
None => Mime::from_str("application/octet-stream").map_err(app_error_from!())?,
Some(v) => v,
};
Ok(SyncFileData {
name: "".to_string(),
original_filename: Some(file_name.to_string()),
content_type: content_type.to_string(),
last_modified: HttpDate::from(modified_time),
content: SyncFileContent::from_stored_file_data(StoredFileData::new(store_path, delete_on_drop)),
})
}
pub fn from_bytes(name: &str, original_filename: Option<String>, content_type: &str, buf: Bytes) -> SyncFileData {
SyncFileData {
name: name.to_owned(),
original_filename,
content_type: content_type.to_owned(),
last_modified: HttpDate::from(SystemTime::now()),
content: SyncFileContent::from_memory_data(MemoryData::new(DataContent::BYTES(buf))),
}
}
pub fn from_content(name: &str, original_filename: Option<String>, content_type: &str, content: SyncFileContent) -> SyncFileData {
let last_modified = content.last_modified();
SyncFileData {
name: name.to_owned(),
original_filename,
content_type: content_type.to_owned(),
last_modified,
content,
}
}
pub fn get_name(&self) -> &str {
self.name.as_str()
}
pub fn name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
pub fn get_original_filename(&self) -> Option<&str> {
match self.original_filename.as_ref() {
None => None,
Some(v) => Some(v.as_str()),
}
}
pub fn get_last_modified_time(&self) -> &HttpDate {
&self.last_modified
}
pub fn set_last_modified_time(&mut self, last_modified: HttpDate) {
self.last_modified = last_modified;
}
pub fn get_content_type(&self) -> &str {
self.content_type.as_str()
}
pub async fn is_valid(&self) -> bool {
self.content.is_valid()
}
pub fn into_async(self) -> FileData {
FileData {
name: self.name,
original_filename: self.original_filename,
content_type: self.content_type,
last_modified: self.last_modified,
content: self.content.into_async(),
}
}
}
impl Read for SyncFileData {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.content.read(buf)
}
}
impl Seek for SyncFileData {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
self.content.seek(pos)
}
}
impl Serialize for SyncFileData {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
self.original_filename.serialize(serializer)
}
}
impl Display for SyncFileData {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.original_filename.fmt(f)
}
}
impl Default for SyncFileData {
fn default() -> Self {
SyncFileData::from_bytes("", Some("".to_owned()), "", Bytes::new())
}
}
impl<'a> ToSchema<'a> for SyncFileData {
fn schema() -> (&'a str, RefOr<Schema>) {
(
type_name::<SyncFileData>(),
RefOr::T(Schema::from(
ObjectBuilder::new().schema_type(SchemaType::String).format(Some(SchemaFormat::KnownFormat(KnownFormat::Binary))),
)),
)
}
}
thread_local! {
static TEMP_UPLOAD_FILE: RefCell<Option<FileData>> = RefCell::new(None);
}
struct FileDataVisitor;
impl<'de> Visitor<'de> for FileDataVisitor {
type Value = FileData;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("FileDataVisitor")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(FileData::from_bytes("", Some(v.to_string()), "application/octet-stream", Bytes::new()))
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(FileData::from_bytes("", Some(v), "application/octet-stream", Bytes::new()))
}
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let file = TEMP_UPLOAD_FILE.with(|v| {
let mut opt = v.borrow_mut();
opt.take()
});
match file {
None => Err(E::custom("no file cache found")),
Some(f) => Ok(f),
}
}
}
impl<'de> Deserialize<'de> for FileData {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(FileDataVisitor)
}
}
impl<'de> Deserializer<'de> for FileData {
type Error = serde::de::value::Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
TEMP_UPLOAD_FILE.with(|v| {
let mut opt = v.borrow_mut();
(*opt) = Some(self);
});
match visitor.visit_unit() {
Ok(v) => Ok(v),
Err(err) => {
TEMP_UPLOAD_FILE.with(|v| {
let mut opt = v.borrow_mut();
opt.take()
});
Err(err)
}
}
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_unit_struct<V>(self, _name: &str, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_newtype_struct<V>(self, _name: &str, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_tuple_struct<V>(self, _name: &str, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_struct<V>(self, _name: &str, _fields: &'static [&str], visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_enum<V>(self, _name: &str, _variants: &'static [&str], visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
}