use pile_config::Label; use std::sync::Arc; mod epub_cover; pub use epub_cover::*; mod epub_meta; pub use epub_meta::*; mod epub_text; pub use epub_text::*; use crate::{ extract::traits::{ExtractState, ObjectExtractor}, value::{Item, PileValue}, }; pub struct EpubExtractor { text: Arc, meta: Arc, cover: Arc, } impl EpubExtractor { pub fn new(item: &Item) -> Self { Self { text: Arc::new(EpubTextExtractor::new(item)), meta: Arc::new(EpubMetaExtractor::new(item)), cover: Arc::new(EpubCoverExtractor::new(item)), } } } #[async_trait::async_trait] impl ObjectExtractor for EpubExtractor { async fn field( &self, state: &ExtractState, name: &pile_config::Label, args: Option<&str>, ) -> Result, std::io::Error> { match (name.as_str(), args) { ("text", args) => Ok(Some( self.text .field(state, name, args) .await .map(|x| x.unwrap_or(PileValue::Null))?, )), ("meta", None) => Ok(Some(PileValue::ObjectExtractor(self.meta.clone()))), ("cover", None) => self.cover.get(state).await, _ => Ok(None), } } #[expect(clippy::unwrap_used)] async fn fields(&self) -> Result, std::io::Error> { Ok(vec![ Label::new("text").unwrap(), Label::new("meta").unwrap(), Label::new("cover").unwrap(), ]) } async fn to_json(&self, state: &ExtractState) -> Result { let keys = self.fields().await?; let mut map = serde_json::Map::new(); for k in &keys { let v = match self.field(state, k, None).await? { Some(x) => x, None => continue, }; if k.as_str() == "text" { map.insert( k.to_string(), serde_json::Value::String(format!( " x.len(), _ => 0, } )), ); continue; } if k.as_str() == "cover" { let summary = match &v { PileValue::Blob { mime, bytes } => { format!("", mime, bytes.len()) } PileValue::Null => "".to_owned(), _ => "".to_owned(), }; map.insert(k.to_string(), serde_json::Value::String(summary)); continue; } map.insert(k.to_string(), Box::pin(v.to_json(state)).await?); } Ok(serde_json::Value::Object(map)) } }