use pile_config::Label; use std::{ collections::HashMap, sync::{Arc, OnceLock}, }; use crate::{ extract::traits::ObjectExtractor, value::{AsyncReader, Item, PileValue}, }; fn toml_to_pile(value: toml::Value) -> PileValue { match value { toml::Value::String(s) => PileValue::String(Arc::new(s.into())), toml::Value::Integer(i) => PileValue::String(Arc::new(i.to_string().into())), toml::Value::Float(f) => PileValue::String(Arc::new(f.to_string().into())), toml::Value::Boolean(b) => PileValue::String(Arc::new(b.to_string().into())), toml::Value::Datetime(d) => PileValue::String(Arc::new(d.to_string().into())), toml::Value::Array(a) => { PileValue::Array(Arc::new(a.into_iter().map(toml_to_pile).collect())) } toml::Value::Table(_) => PileValue::Null, } } pub struct TomlExtractor { item: Item, output: OnceLock>, } impl TomlExtractor { pub fn new(item: &Item) -> Self { Self { item: item.clone(), output: OnceLock::new(), } } async fn get_inner(&self) -> Result<&HashMap, std::io::Error> { if let Some(x) = self.output.get() { return Ok(x); } let mut reader = match self.item.read().await { Ok(r) => r, Err(e) if e.kind() == std::io::ErrorKind::NotFound => { return Ok(self.output.get_or_init(HashMap::new)); } Err(e) => return Err(e), }; let bytes = reader.read_to_end().await?; let toml: toml::Value = match toml::from_slice(&bytes) { Ok(x) => x, Err(_) => return Ok(self.output.get_or_init(HashMap::new)), }; let output: HashMap = match toml { 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)); } } #[async_trait::async_trait] impl ObjectExtractor for TomlExtractor { async fn field(&self, name: &Label) -> Result, std::io::Error> { Ok(self.get_inner().await?.get(name).cloned()) } async fn fields(&self) -> Result, std::io::Error> { Ok(self.get_inner().await?.keys().cloned().collect()) } }