use std::{
fmt,
io::{Cursor, Read, Seek, Write},
path::Path,
};
use tempfile::NamedTempFile;
use crate::{assertions::BoxMap, error::Result};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HashBlockObjectType {
Cai,
Xmp,
Other,
}
impl fmt::Display for HashBlockObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Debug)]
pub struct HashObjectPositions {
pub offset: usize, pub length: usize, pub htype: HashBlockObjectType, }
pub trait CAIRead: Read + Seek + Send {}
impl<T> CAIRead for T where T: Read + Seek + Send {}
impl From<String> for Box<dyn CAIRead> {
fn from(val: String) -> Self {
Box::new(Cursor::new(val))
}
}
pub(crate) struct CAIReadWrapper<'a> {
pub reader: &'a mut dyn CAIRead,
}
impl Read for CAIReadWrapper<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.reader.read(buf)
}
}
impl Seek for CAIReadWrapper<'_> {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
self.reader.seek(pos)
}
}
pub trait CAIReadWrite: CAIRead + Write {}
impl<T> CAIReadWrite for T where T: CAIRead + Write {}
pub(crate) struct CAIReadWriteWrapper<'a> {
pub reader_writer: &'a mut dyn CAIReadWrite,
}
impl Read for CAIReadWriteWrapper<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.reader_writer.read(buf)
}
}
impl Write for CAIReadWriteWrapper<'_> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.reader_writer.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.reader_writer.flush()
}
}
impl Seek for CAIReadWriteWrapper<'_> {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
self.reader_writer.seek(pos)
}
}
pub trait CAIReader: Sync + Send {
fn read_cai(&self, asset_reader: &mut dyn CAIRead) -> Result<Vec<u8>>;
fn read_xmp(&self, asset_reader: &mut dyn CAIRead) -> Option<String>;
}
pub trait CAIWriter: Sync + Send {
fn write_cai(
&self,
input_stream: &mut dyn CAIRead,
output_stream: &mut dyn CAIReadWrite,
store_bytes: &[u8],
) -> Result<()>;
fn get_object_locations_from_stream(
&self,
input_stream: &mut dyn CAIRead,
) -> Result<Vec<HashObjectPositions>>;
fn remove_cai_store_from_stream(
&self,
input_stream: &mut dyn CAIRead,
output_stream: &mut dyn CAIReadWrite,
) -> Result<()>;
}
pub trait AssetIO: Sync + Send {
fn new(asset_type: &str) -> Self
where
Self: Sized;
fn get_handler(&self, asset_type: &str) -> Box<dyn AssetIO>;
fn get_reader(&self) -> &dyn CAIReader;
fn get_writer(&self, _asset_type: &str) -> Option<Box<dyn CAIWriter>> {
None
}
#[allow(dead_code)]
fn read_cai_store(&self, asset_path: &Path) -> Result<Vec<u8>>;
fn save_cai_store(&self, asset_path: &Path, store_bytes: &[u8]) -> Result<()>;
#[allow(dead_code)] fn get_object_locations(&self, asset_path: &Path) -> Result<Vec<HashObjectPositions>>;
#[allow(dead_code)] fn remove_cai_store(&self, asset_path: &Path) -> Result<()>;
fn supported_types(&self) -> &[&str];
#[allow(dead_code)] fn asset_patch_ref(&self) -> Option<&dyn AssetPatch> {
None
}
fn remote_ref_writer_ref(&self) -> Option<&dyn RemoteRefEmbed> {
None
}
fn asset_box_hash_ref(&self) -> Option<&dyn AssetBoxHash> {
None
}
fn composed_data_ref(&self) -> Option<&dyn ComposedManifestRef> {
None
}
}
pub trait AssetPatch {
#[allow(dead_code)] fn patch_cai_store(&self, asset_path: &Path, store_bytes: &[u8]) -> Result<()>;
}
pub trait AssetBoxHash {
fn get_box_map(&self, input_stream: &mut dyn CAIRead) -> Result<Vec<BoxMap>>;
}
#[allow(dead_code)]
pub enum RemoteRefEmbedType {
Xmp(String),
StegoS(String),
StegoB(Vec<u8>),
Watermark(String),
}
pub trait RemoteRefEmbed {
#[allow(dead_code)] fn embed_reference(&self, asset_path: &Path, embed_ref: RemoteRefEmbedType) -> Result<()>;
fn embed_reference_to_stream(
&self,
source_stream: &mut dyn CAIRead,
output_stream: &mut dyn CAIReadWrite,
embed_ref: RemoteRefEmbedType,
) -> Result<()>;
}
pub trait ComposedManifestRef {
fn compose_manifest(&self, manifest_data: &[u8], format: &str) -> Result<Vec<u8>>;
}
pub fn rename_or_copy<P>(temp_file: NamedTempFile, asset_path: P) -> Result<()>
where
P: AsRef<Path>,
{
let (_, path) = temp_file
.keep()
.map_err(|e| crate::Error::OtherError(Box::new(e)))?;
std::fs::rename(&path, asset_path.as_ref())
.or_else(|_| std::fs::copy(&path, asset_path).and(Ok(())))
.map_err(crate::Error::IoError)
}