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