use pile_config::Label; use std::{collections::HashMap, sync::Arc}; mod flac; pub use flac::*; mod id3; pub use id3::*; mod fs; pub use fs::*; mod epub; pub use epub::*; mod exif; pub use exif::*; mod pdf; pub use pdf::*; mod toml; pub use toml::*; mod map; pub use map::*; mod sidecar; pub use sidecar::*; use crate::Item; /// An attachment that extracts metadata from an [Item]. /// /// Metadata is exposed as an immutable map of {label: value}, /// much like a json object. #[async_trait::async_trait] pub trait Extractor: Send + Sync { /// Get the field at `name` from `item`. /// - returns `None` if `name` is not a valid field /// - returns `Some(Null)` if `name` is not available async fn field<'a>( &'a self, name: &pile_config::Label, ) -> Result>, std::io::Error>; /// Return all fields in this extractor. /// `Self::field` must return [Some] for all these keys /// and [None] for all others. async fn fields(&self) -> Result, std::io::Error>; } pub struct MetaExtractor<'a> { inner: MapExtractor<'a>, } // // MARK: file // impl<'a> MetaExtractor<'a> { #[expect(clippy::unwrap_used)] pub fn new(item: &'a Item) -> Self { let inner = MapExtractor { inner: HashMap::from([ ( Label::new("flac").unwrap(), crate::PileValue::Extractor(Arc::new(FlacExtractor::new(item))), ), ( Label::new("id3").unwrap(), crate::PileValue::Extractor(Arc::new(Id3Extractor::new(item))), ), ( Label::new("fs").unwrap(), crate::PileValue::Extractor(Arc::new(FsExtractor::new(item))), ), ( Label::new("epub").unwrap(), crate::PileValue::Extractor(Arc::new(EpubExtractor::new(item))), ), ( Label::new("exif").unwrap(), crate::PileValue::Extractor(Arc::new(ExifExtractor::new(item))), ), ( Label::new("pdf").unwrap(), crate::PileValue::Extractor(Arc::new(PdfExtractor::new(item))), ), ( Label::new("toml").unwrap(), crate::PileValue::Extractor(Arc::new(TomlExtractor::new(item))), ), ( Label::new("sidecar").unwrap(), crate::PileValue::Extractor(Arc::new(SidecarExtractor::new(item))), ), ]), }; Self { inner } } } #[async_trait::async_trait] impl Extractor for MetaExtractor<'_> { async fn field<'a>( &'a self, name: &pile_config::Label, ) -> Result>, std::io::Error> { self.inner.field(name).await } #[expect(clippy::unwrap_used)] async fn fields(&self) -> Result, std::io::Error> { return Ok(vec![ Label::new("flac").unwrap(), Label::new("id3").unwrap(), Label::new("fs").unwrap(), Label::new("epub").unwrap(), Label::new("exif").unwrap(), Label::new("pdf").unwrap(), Label::new("sidecar").unwrap(), ]); } }