use std::{collections::HashMap, path::Path, sync::Arc};
use crate::{
eval::EvaluatedComponent,
ir::{ModuleSource, Span},
};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct ExternalModuleRef {
pub source_raw: Arc<str>,
pub source: ModuleSource,
pub first_seen: Span,
}
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct ModuleRegistry {
pub local_modules: HashMap<Arc<Path>, EvaluatedComponent>,
pub external_refs: Vec<ExternalModuleRef>,
}
impl ModuleRegistry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn insert_local(&mut self, canonical: Arc<Path>, component: EvaluatedComponent) {
self.local_modules.insert(canonical, component);
}
pub fn record_external(&mut self, source_raw: Arc<str>, source: ModuleSource, span: Span) {
if self
.external_refs
.iter()
.any(|e| e.source_raw == source_raw)
{
return;
}
self.external_refs.push(ExternalModuleRef {
source_raw,
source,
first_seen: span,
});
}
#[must_use]
pub fn get_local(&self, canonical: &Path) -> Option<&EvaluatedComponent> {
self.local_modules.get(canonical)
}
#[must_use]
pub fn local_count(&self) -> usize {
self.local_modules.len()
}
#[must_use]
pub fn external_count(&self) -> usize {
self.external_refs.len()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
mod tests {
use std::path::PathBuf;
use super::*;
use crate::{
eval::EvaluatedComponent,
ir::{Component, ComponentId, ComponentKind, Span},
};
fn evaluated_component(path: &str) -> EvaluatedComponent {
EvaluatedComponent {
raw: Arc::new(
Component::builder()
.id(ComponentId::from_index(0))
.path(Arc::<Path>::from(PathBuf::from(path)))
.kind(ComponentKind::Module)
.build(),
),
variables: Vec::new(),
locals: Vec::new(),
providers: Vec::new(),
resources: Vec::new(),
modules: Vec::new(),
outputs: Vec::new(),
diagnostics: Vec::new(),
}
}
#[test]
fn test_should_insert_and_lookup_local_module() {
let mut reg = ModuleRegistry::new();
let canonical: Arc<Path> = Arc::from(PathBuf::from("/repo/modules/s3"));
reg.insert_local(Arc::clone(&canonical), evaluated_component("modules/s3"));
assert_eq!(reg.local_count(), 1);
assert!(reg.get_local(&canonical).is_some());
}
#[test]
fn test_should_dedup_external_refs_by_source_raw() {
let mut reg = ModuleRegistry::new();
let span = Span::synthetic();
reg.record_external(
Arc::<str>::from("terraform-aws-modules/eks/aws"),
ModuleSource::Registry(Arc::<str>::from("terraform-aws-modules/eks/aws")),
span.clone(),
);
reg.record_external(
Arc::<str>::from("terraform-aws-modules/eks/aws"),
ModuleSource::Registry(Arc::<str>::from("terraform-aws-modules/eks/aws")),
span,
);
assert_eq!(reg.external_count(), 1);
}
}