#[cfg(feature = "binary")]
pub mod bin;
#[cfg(feature = "documents")]
pub mod doc;
#[cfg(feature = "fonts")]
pub mod fnt;
#[cfg(feature = "pictures")]
pub mod img;
#[cfg(feature = "music")]
pub mod snd;
#[cfg(feature = "text")]
pub mod txt;
pub type Bytes = Vec<u8>;
pub mod dynamic {
#[cfg(feature = "text")]
use std::string::FromUtf8Error;
use std::{
borrow::Cow,
fs::File,
io::{self, Cursor, Read, Write},
path::Path,
};
#[cfg(all(feature = "documents", feature = "shiva"))]
use super::doc::ShivaDocument;
#[cfg(all(feature = "fonts", feature = "font-kit"))]
use super::fnt::Font as FontKitFont;
#[cfg(all(feature = "pictures", feature = "image"))]
use super::img::{self, DynamicImage};
#[cfg(feature = "music")]
use super::snd::{self, Audio};
#[cfg(feature = "text")]
use super::txt::Text;
use super::{Bendable, Bytes, IntoDataBytes, TryFromDataBytes};
#[cfg(not(feature = "text"))]
use std::marker::PhantomData;
use cfg_if::cfg_if;
#[cfg(all(feature = "fonts", feature = "font-kit"))]
use font_kit::error::FontLoadingError;
pub use infer::*;
#[cfg(all(feature = "documents", feature = "printpdf"))]
use printpdf::PdfDocument;
#[cfg(all(feature = "documents", feature = "shiva"))]
use shiva::core::{bytes, Document, DocumentType};
use thiserror::Error;
pub enum DynamicBendable<'a> {
#[cfg(all(feature = "pictures", feature = "image"))]
Image(DynamicImage),
#[cfg(feature = "binary")]
Binary(Bytes),
#[cfg(feature = "music")]
Sound(Audio),
#[cfg(feature = "text")]
Text(Text<'a>),
#[cfg(not(feature = "text"))]
Phantom(PhantomData<&'a ()>),
#[cfg(all(feature = "documents", feature = "shiva"))]
Doc(ShivaDocument),
#[cfg(all(feature = "documents", feature = "printpdf"))]
Archive(PdfDocument),
Meta,
#[cfg(all(feature = "fonts", feature = "font-kit"))]
Font(FontKitFont),
}
#[cfg(feature = "shiva")]
#[derive(Debug, Error)]
#[error("extension is unknown by Shiva")]
pub struct ShivaUnknownExtensionError;
#[cfg(feature = "shiva")]
#[derive(Debug, Error)]
pub enum ShivaError {
#[error("{0}")]
UnknownExtension(#[from] ShivaUnknownExtensionError),
#[error("{0}")]
Anyhow(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum OpenError {
#[error("io: {0}")]
Io(#[from] io::Error),
#[cfg(all(feature = "pictures", feature = "image"))]
#[error("image: {0}")]
Image(#[from] img::ImageError),
#[cfg(feature = "music")]
#[error("audio: {0}")]
Audio(#[from] snd::AudioOpenError),
#[cfg(all(feature = "documents", feature = "printpdf"))]
#[error("pdf: {0}")]
Pdf(String),
#[cfg(feature = "text")]
#[error("text: {0}")]
Text(#[from] FromUtf8Error),
#[cfg(all(feature = "documents", feature = "shiva"))]
#[error("document: {0}")]
Document(#[from] ShivaError),
#[cfg(all(feature = "fonts", feature = "font-kit"))]
#[error("font: {0:?}")]
Font(#[from] FontLoadingError),
}
impl TryFromDataBytes for File {
type Error = io::Error;
type Format = Box<dyn AsRef<Path>>;
fn try_from_data_bytes(
bytes: Bytes,
format: Self::Format,
_crop: crate::Crop,
) -> Result<Self, Self::Error>
where
Self: Sized,
{
let mut file = File::create(format.as_ref())?;
file.write_all(&bytes)?;
Ok(file)
}
}
impl IntoDataBytes for File {
fn into_data_bytes(self) -> Bytes {
self.bytes().flatten().collect() }
}
impl Bendable for File {
type Unit = u8;
fn map<F: Fn(Cow<Self::Unit>) -> Self::Unit + Sync>(mut self, f: F) -> Self {
let mut bytes = Vec::new();
self.read_to_end(&mut bytes).expect("couldn't read file");
cfg_if! {
if #[cfg(feature = "binary")] {
self.write_all(&bytes.map(f)).expect("couldn't write file");
} else {
self.write_all(&bytes.into_iter().map(|e| f(Cow::Owned(e))).collect::<Vec<u8>>()).expect("couldn't write file");
}
}
self
}
}
pub type DynamicResult = Result<Option<DynamicBendable<'static>>, OpenError>;
pub fn guess(t: Option<infer::Type>, bytes: Bytes) -> DynamicResult {
use MatcherType::*;
t.map(|t| (t.matcher_type(), t.extension()))
.map(
|(matcher, extension)| -> Result<DynamicBendable, OpenError> {
Ok(match matcher {
#[cfg(all(feature = "pictures", feature = "image"))]
Image => DynamicBendable::Image(img::load_from_memory(&bytes)?),
#[cfg(feature = "music")]
Audio => DynamicBendable::Sound(crate::snd::Audio::open(Cursor::new(bytes), None)?),
#[cfg(all(feature = "documents", feature = "printpdf"))]
Archive if extension == "pdf" => DynamicBendable::Archive(
PdfDocument::try_from_data_bytes(
bytes,
(),
Default::default(),
)
.map_err(OpenError::Pdf)?,
),
#[cfg(all(feature = "documents", feature = "shiva"))]
Archive | Doc => {
let document_type = DocumentType::from_extension(extension)
.ok_or(ShivaUnknownExtensionError)
.map_err(ShivaError::UnknownExtension)?;
DynamicBendable::Doc(ShivaDocument::new(
Document::parse(
&bytes::Bytes::from(bytes),
document_type,
)
.map_err(ShivaError::Anyhow)?,
document_type,
))
}
#[cfg(all(feature = "fonts", feature = "font-kit"))]
Font => DynamicBendable::Font(FontKitFont::try_from_data_bytes(
bytes,
(),
Default::default(),
)?),
#[cfg(feature = "text")]
Text => DynamicBendable::Text(crate::txt::Text::try_from_data_bytes(
bytes,
(),
Default::default(),
).unwrap()),
#[cfg(feature = "binary")]
_ => DynamicBendable::Binary(bytes),
#[cfg(not(feature = "binary"))]
_ => unimplemented!("no format reader available to open this thing (turn on the 'binary' feature to default to binary data)"),
})
},
)
.transpose()
}
pub fn open_file(path: impl AsRef<Path>) -> DynamicResult {
open(&mut File::open(path)?)
}
pub fn open(source: &mut impl Read) -> DynamicResult {
let contents = {
let mut c = Vec::new();
source.read_to_end(&mut c)?;
c
};
guess(infer::get(&contents), contents)
}
}
use std::{borrow::Cow, convert::Infallible};
pub trait Bendable: TryFromDataBytes + IntoDataBytes {
type Unit;
fn bend_into<T: TryFromDataBytes>(
self,
format: <T as TryFromDataBytes>::Format,
crop: Crop,
) -> Result<T, <T as TryFromDataBytes>::Error> {
T::try_from_data_bytes(self.into_data_bytes(), format, crop)
}
fn bend_from<T: IntoDataBytes>(
b: T,
format: <Self as TryFromDataBytes>::Format,
crop: Crop,
) -> Result<Self, <Self as TryFromDataBytes>::Error> {
Self::try_from_data_bytes(b.into_data_bytes(), format, crop)
}
fn map<F: Fn(Cow<Self::Unit>) -> Self::Unit + Sync>(self, f: F) -> Self;
}
pub trait IntoDataBytes: Sized {
fn into_data_bytes(self) -> Bytes;
}
#[derive(Default)]
pub enum Crop {
Start,
#[default]
End,
}
pub trait TryFromDataBytes {
type Error;
type Format;
fn try_from_data_bytes(
bytes: Bytes,
format: Self::Format,
crop: Crop,
) -> Result<Self, Self::Error>
where
Self: Sized;
}
pub trait FromDataBytes {
type Format;
fn from_data_bytes(bytes: Bytes, format: Self::Format, crop: Crop) -> Self
where
Self: Sized;
}
impl<F, T> FromDataBytes for T
where
T: TryFromDataBytes<Error = Infallible, Format = F>,
{
type Format = <T as TryFromDataBytes>::Format;
fn from_data_bytes(bytes: Bytes, format: Self::Format, crop: Crop) -> Self {
T::try_from_data_bytes(bytes, format, crop).unwrap_or_else(|_| unreachable!())
}
}