use pile_config::Label; use std::{collections::HashMap, sync::OnceLock}; use crate::{FileItem, Item, PileValue, extract::Extractor}; fn toml_to_pile(value: toml::Value) -> PileValue<'static, I> { match value { toml::Value::String(s) => PileValue::String(s.into()), toml::Value::Integer(i) => PileValue::String(i.to_string().into()), toml::Value::Float(f) => PileValue::String(f.to_string().into()), toml::Value::Boolean(b) => PileValue::String(b.to_string().into()), toml::Value::Datetime(d) => PileValue::String(d.to_string().into()), toml::Value::Array(a) => PileValue::Array(a.into_iter().map(toml_to_pile).collect()), toml::Value::Table(_) => PileValue::Null, } } pub struct SidecarExtractor<'a> { item: &'a FileItem, output: OnceLock>>, } impl<'a> SidecarExtractor<'a> { pub fn new(item: &'a FileItem) -> Self { Self { item, output: OnceLock::new(), } } fn get_inner(&self) -> Result<&HashMap>, std::io::Error> { if let Some(x) = self.output.get() { return Ok(x); } let sidecar_file = self.item.path.with_extension("toml"); if !(sidecar_file.is_file() && self.item.sidecar) { return Ok(self.output.get_or_init(HashMap::new)); } let sidecar = std::fs::read(&sidecar_file)?; let sidecar: toml::Value = match toml::from_slice(&sidecar) { Ok(x) => x, Err(_) => return Ok(self.output.get_or_init(HashMap::new)), }; let output: HashMap> = match sidecar { toml::Value::Table(t) => t .into_iter() .filter_map(|(k, v)| Label::new(&k).map(|label| (label, toml_to_pile(v)))) .collect(), _ => HashMap::new(), }; return Ok(self.output.get_or_init(|| output)); } } impl Extractor for SidecarExtractor<'_> { fn field<'a>( &'a self, name: &Label, ) -> Result>, std::io::Error> { Ok(self.get_inner()?.get(name)) } fn fields(&self) -> Result, std::io::Error> { Ok(self.get_inner()?.keys().cloned().collect()) } }