use std::collections::BTreeMap;
use crate::application::support::load_source_content;
use crate::config::LoadOptions;
use crate::domain::{ParsedContent, Result, SourceError};
use super::entry::SourceEntry;
use super::result::SourceLayer;
pub(super) struct LayerCollection {
pub(super) layers: Vec<SourceLayer>,
pub(super) loaded_sources: Vec<String>,
pub(super) failed_sources: Vec<(String, String)>,
pub(super) loaded_count: usize,
}
enum SourceLoadAttempt {
Loaded {
source_name: String,
layer: SourceLayer,
},
Failed {
source_name: String,
error: SourceError,
},
SkippedOptional,
}
pub(super) fn collect_loaded_layers(
sources: &[SourceEntry],
options: &LoadOptions,
cache: &mut BTreeMap<String, ParsedContent>,
) -> Result<LayerCollection> {
let mut layers = Vec::new();
let mut loaded_sources = Vec::new();
let mut failed_sources = Vec::new();
let mut loaded_count = 0;
for index in sorted_source_indices(sources) {
match load_attempt(&sources[index], options, cache)? {
SourceLoadAttempt::Loaded { source_name, layer } => {
layers.push(layer);
loaded_sources.push(source_name);
loaded_count += 1;
}
SourceLoadAttempt::Failed { source_name, error } => {
failed_sources.push((source_name, error.to_string()));
}
SourceLoadAttempt::SkippedOptional => {}
}
}
Ok(LayerCollection {
layers,
loaded_sources,
failed_sources,
loaded_count,
})
}
fn sorted_source_indices(sources: &[SourceEntry]) -> Vec<usize> {
let mut indices: Vec<_> = (0..sources.len()).collect();
indices.sort_by(|left, right| {
sources[*left]
.priority
.cmp(&sources[*right].priority)
.then_with(|| sources[*left].order.cmp(&sources[*right].order))
});
indices
}
fn load_attempt(
entry: &SourceEntry,
options: &LoadOptions,
cache: &mut BTreeMap<String, ParsedContent>,
) -> Result<SourceLoadAttempt> {
let source_name = entry.source.display_name();
let cache_key = entry.source.cache_key();
if options.is_cache_enabled()
&& let Some(cached) = cache.get(&cache_key)
{
return Ok(SourceLoadAttempt::Loaded {
source_name,
layer: layer_from_entry(entry, cached.clone()),
});
}
match load_source_content(&*entry.source, None) {
Ok(content) => {
if options.is_cache_enabled() {
cache.insert(cache_key, content.clone());
}
Ok(SourceLoadAttempt::Loaded {
source_name,
layer: layer_from_entry(entry, content),
})
}
Err(error) => {
if entry.source.is_optional() && options.ignores_missing_optional() {
Ok(SourceLoadAttempt::SkippedOptional)
} else if options.is_fail_fast() {
Err(error)
} else {
Ok(SourceLoadAttempt::Failed { source_name, error })
}
}
}
}
fn layer_from_entry(entry: &SourceEntry, content: ParsedContent) -> SourceLayer {
SourceLayer {
metadata: entry.source.metadata().with_priority(entry.priority),
priority: entry.priority,
registration_index: entry.order,
content,
}
}