diff --git a/bingus/Cargo.toml b/bingus/Cargo.toml index 5ccaad8..cb5de6b 100644 --- a/bingus/Cargo.toml +++ b/bingus/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bingus" -version = "0.4.1" +version = "0.5.0" edition.workspace = true license.workspace = true description.workspace = true diff --git a/bingus/src/lib.rs b/bingus/src/lib.rs index 422b821..729350d 100644 --- a/bingus/src/lib.rs +++ b/bingus/src/lib.rs @@ -19,7 +19,7 @@ mod dynamic { use std::{ borrow::Cow, fs::File, - io::{self, Read, Write}, + io::{self, Cursor, Read, Write}, path::Path, }; @@ -34,6 +34,8 @@ mod dynamic { #[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(feature = "fonts")] @@ -46,8 +48,6 @@ mod dynamic { use strum::EnumDiscriminants; use thiserror::Error; - cfg_if! { - if #[cfg(feature = "text")] { #[derive(EnumDiscriminants)] #[strum_discriminants(name(Format))] pub enum DynamicBendable<'a> { @@ -59,23 +59,8 @@ mod dynamic { Sound(Audio), #[cfg(feature = "text")] Text(Text<'a>), - #[cfg(feature = "documents")] - Doc(ShivaDocument), - #[cfg(feature = "documents")] - Archive(PdfDocument), - Meta, - #[cfg(feature = "fonts")] - Font(FontKitFont), - }} else { - #[derive(EnumDiscriminants)] - #[strum_discriminants(name(Format))] - pub enum DynamicBendable { - #[cfg(feature = "pictures")] - Image(DynamicImage), - #[cfg(feature = "binary")] - Binary(Bytes), - #[cfg(feature = "music")] - Sound(Audio), + #[cfg(not(feature = "text"))] + Phantom(PhantomData<&'a ()>), #[cfg(feature = "documents")] Doc(ShivaDocument), #[cfg(feature = "documents")] @@ -84,7 +69,6 @@ mod dynamic { #[cfg(feature = "fonts")] Font(FontKitFont), } - }} #[cfg(feature = "shiva")] #[derive(Debug, Error)] @@ -170,132 +154,75 @@ mod dynamic { } } - cfg_if! { - if #[cfg(feature = "text")] { - pub fn open>(path: P) -> Result>, OpenError> { + pub type DynamicResult = Result>, OpenError>; + + fn guess(t: Option, bytes: Bytes) -> DynamicResult { use MatcherType::*; - infer::get_from_path(&path)? - .map(|t| (t.matcher_type(), t.extension())) - .map( - |(matcher, extension)| -> Result { - Ok(match matcher { - #[cfg(feature = "pictures")] - Image => DynamicBendable::Image(img::open(path)?), - #[cfg(feature = "music")] - Audio => DynamicBendable::Sound(crate::snd::Audio::open(path)?), - #[cfg(feature = "documents")] - Archive if extension == "pdf" => DynamicBendable::Archive( - PdfDocument::try_from_data_bytes( - File::open(path)? - .bytes() - .collect::>()?, - (), - Default::default(), - ) - .map_err(OpenError::Pdf)?, - ), - #[cfg(feature = "documents")] - 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(std::fs::read(path)?), - document_type, - ) - .map_err(ShivaError::Anyhow)?, + t.map(|t| (t.matcher_type(), t.extension())) + .map( + |(matcher, extension)| -> Result { + Ok(match matcher { + #[cfg(feature = "pictures")] + Image => DynamicBendable::Image(img::load_from_memory(&bytes)?), + #[cfg(feature = "music")] + Audio => DynamicBendable::Sound(crate::snd::Audio::open(Cursor::new(bytes), None)?), + #[cfg(feature = "documents")] + Archive if extension == "pdf" => DynamicBendable::Archive( + PdfDocument::try_from_data_bytes( + bytes, + (), + Default::default(), + ) + .map_err(OpenError::Pdf)?, + ), + #[cfg(feature = "documents")] + 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, - )) - } - #[cfg(feature = "fonts")] - Font => DynamicBendable::Font(FontKitFont::try_from_data_bytes( - File::open(path)? - .bytes() - .collect::>()?, - (), - Default::default(), - )?), - #[cfg(feature = "text")] - Text => DynamicBendable::Text(crate::txt::Text::try_from_data_bytes( - File::open(path)? - .bytes() - .collect::>()?, - (), - Default::default(), - )?), - #[cfg(feature = "binary")] - _ => DynamicBendable::Binary({ - let mut buf = Vec::new(); - File::open(path)?.read_to_end(&mut buf)?; - buf - }), - #[cfg(not(feature = "binary"))] - _ => unimplemented!("no format reader available to open this thing (turn on the 'binary' feature to default to binary data)"), - }) - }, - ) - .transpose() - }} else { - pub fn open>(path: P) -> Result, OpenError> { - use MatcherType::*; - infer::get_from_path(&path)? - .map(|t| (t.matcher_type(), t.extension())) - .map( - |(matcher, extension)| -> Result { - Ok(match matcher { - #[cfg(feature = "pictures")] - Image => DynamicBendable::Image(img::open(path)?), - #[cfg(feature = "music")] - Audio => DynamicBendable::Sound(crate::snd::Audio::open(path)?), - #[cfg(feature = "documents")] - Archive if extension == "pdf" => DynamicBendable::Archive( - PdfDocument::try_from_data_bytes( - File::open(path)? - .bytes() - .collect::>()?, - (), - Default::default(), ) - .map_err(OpenError::Pdf)?, - ), - #[cfg(feature = "documents")] - 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(std::fs::read(path)?), - document_type, - ) - .map_err(ShivaError::Anyhow)?, - document_type, - )) - } - #[cfg(feature = "fonts")] - Font => todo!(), - #[cfg(feature = "text")] - Text => DynamicBendable::Text(crate::txt::Text::try_from_data_bytes( - File::open(path)? - .bytes() - .collect::>()?, - (), - Default::default(), - )?), - #[cfg(feature = "binary")] - _ => DynamicBendable::Binary({ - let mut buf = Vec::new(); - File::open(path)?.read_to_end(&mut buf)?; - buf - }), - _ => unimplemented!("no format available to open this thing"), - }) - }, - ) - .transpose() + .map_err(ShivaError::Anyhow)?, + document_type, + )) + } + #[cfg(feature = "fonts")] + 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(), + )?), + #[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) -> 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}; diff --git a/bingus/src/snd/simphonia.rs b/bingus/src/snd/simphonia.rs index 3c03847..20ef032 100644 --- a/bingus/src/snd/simphonia.rs +++ b/bingus/src/snd/simphonia.rs @@ -1,8 +1,4 @@ -use std::{ - fs::File, - io::{self, Read}, - path::Path, -}; +use std::io::{self, Read}; use derive_new::new; use symphonia::{ @@ -11,7 +7,7 @@ use symphonia::{ codecs::{Decoder, CODEC_TYPE_NULL}, conv::FromSample, formats::FormatReader, - io::MediaSourceStream, + io::{MediaSource, MediaSourceStream}, probe::Hint, sample::{i24, u24}, }, @@ -51,19 +47,22 @@ impl PartialEq for AudioOpenError { } impl Audio { - pub fn open>(path: P) -> Result { + pub fn open( + source: impl MediaSource + 'static, + extension: Option<&str>, + ) -> Result { let registry = default::get_codecs(); let probe = default::get_probe(); - let mediasource = File::open(path.as_ref())?; - let mss = MediaSourceStream::new(Box::new(mediasource), Default::default()); + let mss = MediaSourceStream::new(Box::new(source), Default::default()); let reader = probe .format( - Hint::new().with_extension( - path.as_ref() - .extension() - .and_then(|os_str| os_str.to_str()) - .unwrap_or("mp3"), - ), + &{ + let mut hint = Hint::new(); + if let Some(e) = extension { + hint.with_extension(e); + } + hint + }, mss, &Default::default(), &Default::default(), @@ -161,6 +160,8 @@ where #[cfg(test)] mod tests { + use std::fs::File; + use project_root::get_project_root; use crate::IntoDataBytes; @@ -171,12 +172,14 @@ mod tests { fn open_sample_file() { let original = &include_bytes!("../../../testing material/sound/sample-3s.mp3")[52079 - 51826..]; + let path = get_project_root() + .expect("can't find project root!") + .join("testing material") + .join("sound") + .join("sample-3s.mp3"); let result = Audio::open( - get_project_root() - .expect("can't find project root!") - .join("testing material") - .join("sound") - .join("sample-3s.mp3"), + File::open(&path).expect("can't find test file!"), + path.extension().and_then(|s| s.to_str()), ) .map(|audio| audio.into_data_bytes()); dbg!(original.len());