use std::collections::HashMap;
use genotype_parser::{
tree::{GTIdentifier, GTImportName, GTImportReference, GTPath},
GTDefinitionId, GTPathKind,
};
use miette::Result;
use crate::error::GTProjectError;
use super::{
identifier::GTPModuleIdentifierSource, GTPModuleDefinitionResolve, GTPModuleIdentifierResolve,
GTPModulePathResolve, GTProjectModuleParse,
};
#[derive(Debug, PartialEq, Clone)]
pub struct GTPModuleResolve {
pub paths: HashMap<GTPath, GTPModulePathResolve>,
pub identifiers: HashMap<GTIdentifier, GTPModuleIdentifierResolve>,
pub definitions: HashMap<GTDefinitionId, GTPModuleDefinitionResolve>,
}
impl GTPModuleResolve {
pub fn try_new(
modules: &Vec<GTProjectModuleParse>,
parse: >ProjectModuleParse,
) -> Result<Self> {
let mut paths: HashMap<GTPath, GTPModulePathResolve> = HashMap::new();
for local_path in parse.1.resolve.deps.iter() {
if paths.contains_key(local_path) || local_path.kind() == GTPathKind::Package {
continue;
}
let module_path = parse
.0
.resolve(local_path)
.map_err(|_| GTProjectError::CannotResolve(local_path.source_str().to_owned()))?;
paths.insert(local_path.clone(), GTPModulePathResolve { module_path });
}
let mut identifiers: HashMap<GTIdentifier, GTPModuleIdentifierResolve> = HashMap::new();
for reference in parse.1.resolve.references.iter() {
if identifiers.contains_key(reference) {
continue;
};
let package_import = parse.1.module.imports.iter().find(|import| {
if import.path.kind() != GTPathKind::Package {
return false;
}
match &import.reference {
GTImportReference::Name(_, name) => name.1 == reference.1,
GTImportReference::Names(_, names) => names.iter().any(|name| match name {
GTImportName::Name(_, name) => name.1 == reference.1,
GTImportName::Alias(_, _, alias) => alias.1 == reference.1,
}),
GTImportReference::Glob(_) => false,
}
});
if let Some(import) = package_import {
identifiers.insert(
reference.clone(),
GTPModuleIdentifierResolve {
source: GTPModuleIdentifierSource::Package(import.path.clone()),
},
);
continue;
}
if parse
.1
.resolve
.exports
.iter()
.any(|export| export.1 == reference.1)
{
identifiers.insert(
reference.clone(),
GTPModuleIdentifierResolve {
source: GTPModuleIdentifierSource::Local,
},
);
continue;
}
let (local_path, _) = paths
.iter()
.find(|(local_path, module_resolve)| {
let import = parse.1.module.imports.iter().find(|import| {
if import.path != **local_path {
return false;
}
match &import.reference {
GTImportReference::Glob(_) => {
let module = modules
.iter()
.find(|module| module.0 == module_resolve.module_path)
.unwrap();
module
.1
.resolve
.exports
.iter()
.any(|export| export.1 == reference.1)
}
GTImportReference::Name(_, name) => name.1 == reference.1,
GTImportReference::Names(_, names) => {
names.iter().any(|name| match name {
GTImportName::Name(_, name) => name.1 == reference.1,
GTImportName::Alias(_, _, alias) => alias.1 == reference.1,
})
}
}
});
!import.is_none()
})
.ok_or_else(|| GTProjectError::UndefinedType {
span: reference.as_span(),
identifier: reference.as_string(),
})?;
identifiers.insert(
reference.clone(),
GTPModuleIdentifierResolve {
source: GTPModuleIdentifierSource::External(local_path.clone()),
},
);
}
Ok(GTPModuleResolve {
paths,
identifiers,
definitions: Default::default(),
})
}
}