use std::time::Instant;
use serde::de::DeserializeOwned;
use crate::application::support::{elapsed_millis_u64, load_source_content, merge_contents};
use crate::domain::{Result, SourceError};
use crate::infrastructure::FileSource;
use super::{CascadeLoadResult, CascadeLoader, ResolvedCascadeLayer};
impl CascadeLoader {
pub fn load(&self) -> Result<CascadeLoadResult> {
let start = Instant::now();
let mut ordered_layers = self.layers.clone();
ordered_layers.sort_by(|left, right| {
left.priority
.cmp(&right.priority)
.then_with(|| left.path.cmp(&right.path))
});
let mut contents = Vec::new();
let mut loaded_layers = Vec::new();
let mut skipped_layers = Vec::new();
let mut failed_layers = Vec::new();
for layer in &ordered_layers {
let layer_info = ResolvedCascadeLayer::from(layer);
if layer.optional && !layer.path.exists() {
skipped_layers.push(layer_info);
continue;
}
let source = FileSource::new(layer.path.clone());
match load_source_content(&source, layer.format.or(self.default_format)) {
Ok(content) => {
contents.push(content);
loaded_layers.push(layer_info);
}
Err(error) if layer.optional && error.is_not_found() => {
skipped_layers.push(layer_info);
}
Err(error) if self.options.is_fail_fast() => return Err(error),
Err(error) => failed_layers.push((layer_info, error.to_string())),
}
}
if contents.is_empty() && !failed_layers.is_empty() {
let messages = failed_layers
.iter()
.map(|(layer, error)| format!("{}:{}: {error}", layer.scope, layer.path.display()))
.collect::<Vec<_>>()
.join(", ");
return Err(SourceError::custom(&messages));
}
let content = merge_contents(contents, self.options.merge_strategy)?;
Ok(CascadeLoadResult {
content,
loaded_layers,
skipped_layers,
failed_layers,
processing_time_ms: elapsed_millis_u64(start),
})
}
pub fn load_as<T: DeserializeOwned>(&self) -> Result<T> {
self.load()?.to_type()
}
}