1#[cfg(feature = "binary")]
4pub mod bin;
5#[cfg(feature = "documents")]
6pub mod doc;
7#[cfg(feature = "fonts")]
8pub mod fnt;
9#[cfg(feature = "pictures")]
10pub mod img;
11#[cfg(feature = "music")]
12pub mod snd;
13#[cfg(feature = "text")]
14pub mod txt;
15
16pub type Bytes = Vec<u8>;
17
18pub mod dynamic {
19 #[cfg(feature = "text")]
20 use std::string::FromUtf8Error;
21 use std::{
22 borrow::Cow,
23 fs::File,
24 io::{self, Cursor, Read, Write},
25 path::Path,
26 };
27
28 #[cfg(all(feature = "documents", feature = "shiva"))]
29 use super::doc::ShivaDocument;
30 #[cfg(all(feature = "fonts", feature = "font-kit"))]
31 use super::fnt::Font as FontKitFont;
32 #[cfg(all(feature = "pictures", feature = "image"))]
33 use super::img::{self, DynamicImage};
34 #[cfg(feature = "music")]
35 use super::snd::{self, Audio};
36 #[cfg(feature = "text")]
37 use super::txt::Text;
38 use super::{Bendable, Bytes, IntoDataBytes, TryFromDataBytes};
39 #[cfg(not(feature = "text"))]
40 use std::marker::PhantomData;
41
42 use cfg_if::cfg_if;
43 #[cfg(all(feature = "fonts", feature = "font-kit"))]
44 use font_kit::error::FontLoadingError;
45 pub use infer::*;
46 #[cfg(all(feature = "documents", feature = "printpdf"))]
47 use printpdf::PdfDocument;
48 #[cfg(all(feature = "documents", feature = "shiva"))]
49 use shiva::core::{bytes, Document, DocumentType};
50 use thiserror::Error;
51
52 pub enum DynamicBendable<'a> {
53 #[cfg(all(feature = "pictures", feature = "image"))]
54 Image(DynamicImage),
55 #[cfg(feature = "binary")]
56 Binary(Bytes),
57 #[cfg(feature = "music")]
58 Sound(Audio),
59 #[cfg(feature = "text")]
60 Text(Text<'a>),
61 #[cfg(not(feature = "text"))]
62 Phantom(PhantomData<&'a ()>),
63 #[cfg(all(feature = "documents", feature = "shiva"))]
64 Doc(ShivaDocument),
65 #[cfg(all(feature = "documents", feature = "printpdf"))]
66 Archive(PdfDocument),
67 Meta,
68 #[cfg(all(feature = "fonts", feature = "font-kit"))]
69 Font(FontKitFont),
70 }
71
72 #[cfg(feature = "shiva")]
73 #[derive(Debug, Error)]
74 #[error("extension is unknown by Shiva")]
75 pub struct ShivaUnknownExtensionError;
76
77 #[cfg(feature = "shiva")]
78 #[derive(Debug, Error)]
79 pub enum ShivaError {
80 #[error("{0}")]
81 UnknownExtension(#[from] ShivaUnknownExtensionError),
82 #[error("{0}")]
83 Anyhow(#[from] anyhow::Error),
84 }
85
86 #[derive(Debug, Error)]
87 pub enum OpenError {
88 #[error("io: {0}")]
89 Io(#[from] io::Error),
90 #[cfg(all(feature = "pictures", feature = "image"))]
91 #[error("image: {0}")]
92 Image(#[from] img::ImageError),
93 #[cfg(feature = "music")]
94 #[error("audio: {0}")]
95 Audio(#[from] snd::AudioOpenError),
96 #[cfg(all(feature = "documents", feature = "printpdf"))]
97 #[error("pdf: {0}")]
98 Pdf(String),
99 #[cfg(feature = "text")]
100 #[error("text: {0}")]
101 Text(#[from] FromUtf8Error),
102 #[cfg(all(feature = "documents", feature = "shiva"))]
103 #[error("document: {0}")]
104 Document(#[from] ShivaError),
105 #[cfg(all(feature = "fonts", feature = "font-kit"))]
106 #[error("font: {0:?}")]
107 Font(#[from] FontLoadingError),
108 }
109
110 impl TryFromDataBytes for File {
111 type Error = io::Error;
112 type Format = Box<dyn AsRef<Path>>;
113
114 fn try_from_data_bytes(
115 bytes: Bytes,
116 format: Self::Format,
117 _crop: crate::Crop,
118 ) -> Result<Self, Self::Error>
119 where
120 Self: Sized,
121 {
122 let mut file = File::create(format.as_ref())?;
123 file.write_all(&bytes)?;
124 Ok(file)
125 }
126 }
127
128 impl IntoDataBytes for File {
129 fn into_data_bytes(self) -> Bytes {
130 self.bytes().flatten().collect() }
132 }
133
134 impl Bendable for File {
135 type Unit = u8;
136
137 fn map<F: Fn(Cow<Self::Unit>) -> Self::Unit + Sync>(mut self, f: F) -> Self {
139 let mut bytes = Vec::new();
140 self.read_to_end(&mut bytes).expect("couldn't read file");
141 cfg_if! {
142 if #[cfg(feature = "binary")] {
143 self.write_all(&bytes.map(f)).expect("couldn't write file");
144 } else {
145 self.write_all(&bytes.into_iter().map(|e| f(Cow::Owned(e))).collect::<Vec<u8>>()).expect("couldn't write file");
146 }
147 }
148 self
149 }
150 }
151
152 pub type DynamicResult = Result<Option<DynamicBendable<'static>>, OpenError>;
153
154 pub fn guess(t: Option<infer::Type>, bytes: Bytes) -> DynamicResult {
155 use MatcherType::*;
156 t.map(|t| (t.matcher_type(), t.extension()))
157 .map(
158 |(matcher, extension)| -> Result<DynamicBendable, OpenError> {
159 Ok(match matcher {
160 #[cfg(all(feature = "pictures", feature = "image"))]
161 Image => DynamicBendable::Image(img::load_from_memory(&bytes)?),
162 #[cfg(feature = "music")]
163 Audio => DynamicBendable::Sound(crate::snd::Audio::open(Cursor::new(bytes), None)?),
164 #[cfg(all(feature = "documents", feature = "printpdf"))]
165 Archive if extension == "pdf" => DynamicBendable::Archive(
166 PdfDocument::try_from_data_bytes(
167 bytes,
168 (),
169 Default::default(),
170 )
171 .map_err(OpenError::Pdf)?,
172 ),
173 #[cfg(all(feature = "documents", feature = "shiva"))]
174 Archive | Doc => {
175 let document_type = DocumentType::from_extension(extension)
176 .ok_or(ShivaUnknownExtensionError)
177 .map_err(ShivaError::UnknownExtension)?;
178 DynamicBendable::Doc(ShivaDocument::new(
179 Document::parse(
180 &bytes::Bytes::from(bytes),
181 document_type,
182 )
183 .map_err(ShivaError::Anyhow)?,
184 document_type,
185 ))
186 }
187 #[cfg(all(feature = "fonts", feature = "font-kit"))]
188 Font => DynamicBendable::Font(FontKitFont::try_from_data_bytes(
189 bytes,
190 (),
191 Default::default(),
192 )?),
193 #[cfg(feature = "text")]
194 Text => DynamicBendable::Text(crate::txt::Text::try_from_data_bytes(
195 bytes,
196 (),
197 Default::default(),
198 ).unwrap()),
199 #[cfg(feature = "binary")]
200 _ => DynamicBendable::Binary(bytes),
201 #[cfg(not(feature = "binary"))]
202 _ => unimplemented!("no format reader available to open this thing (turn on the 'binary' feature to default to binary data)"),
203 })
204 },
205 )
206 .transpose()
207 }
208
209 pub fn open_file(path: impl AsRef<Path>) -> DynamicResult {
210 open(&mut File::open(path)?)
211 }
212
213 pub fn open(source: &mut impl Read) -> DynamicResult {
214 let contents = {
215 let mut c = Vec::new();
216 source.read_to_end(&mut c)?;
217 c
218 };
219 guess(infer::get(&contents), contents)
220 }
221}
222
223use std::{borrow::Cow, convert::Infallible};
224
225pub trait Bendable: TryFromDataBytes + IntoDataBytes {
226 type Unit;
227 fn bend_into<T: TryFromDataBytes>(
228 self,
229 format: <T as TryFromDataBytes>::Format,
230 crop: Crop,
231 ) -> Result<T, <T as TryFromDataBytes>::Error> {
232 T::try_from_data_bytes(self.into_data_bytes(), format, crop)
233 }
234 fn bend_from<T: IntoDataBytes>(
235 b: T,
236 format: <Self as TryFromDataBytes>::Format,
237 crop: Crop,
238 ) -> Result<Self, <Self as TryFromDataBytes>::Error> {
239 Self::try_from_data_bytes(b.into_data_bytes(), format, crop)
240 }
241 fn map<F: Fn(Cow<Self::Unit>) -> Self::Unit + Sync>(self, f: F) -> Self;
242}
243
244pub trait IntoDataBytes: Sized {
245 fn into_data_bytes(self) -> Bytes;
246}
247
248#[derive(Default)]
249pub enum Crop {
250 Start,
251 #[default]
252 End,
253}
254
255pub trait TryFromDataBytes {
256 type Error;
257 type Format;
258 fn try_from_data_bytes(
259 bytes: Bytes,
260 format: Self::Format,
261 crop: Crop,
262 ) -> Result<Self, Self::Error>
263 where
264 Self: Sized;
265}
266
267pub trait FromDataBytes {
268 type Format;
269 fn from_data_bytes(bytes: Bytes, format: Self::Format, crop: Crop) -> Self
270 where
271 Self: Sized;
272}
273
274impl<F, T> FromDataBytes for T
275where
276 T: TryFromDataBytes<Error = Infallible, Format = F>,
277{
278 type Format = <T as TryFromDataBytes>::Format;
279 fn from_data_bytes(bytes: Bytes, format: Self::Format, crop: Crop) -> Self {
280 T::try_from_data_bytes(bytes, format, crop).unwrap_or_else(|_| unreachable!())
281 }
282}