use std::collections::{BTreeMap, BTreeSet};
use crate::error::{Error, Result};
use crate::language::Language;
use crate::parser::{ManifestModel, ManifestService, NameManifest, build_name_manifest};
use crate::spec::{ApiSpecLeaf, ApiSpecNode, ApiSpecTransform, ApiSpecTree, CompilerPass};
use crate::spec::{ExternalTypeSpec, LanguageStringSpec, ModulePath};
use super::{
PlannedAliasType, PlannedEnumType, PlannedFamily, PlannedFieldData, PlannedFlagsType,
PlannedJsonType, PlannedOperationData, PlannedProtoType, PlannedRecordData, PlannedRecordType,
PlannedResource, PlannedResourceType, PlannedSpec, PlannedSpecData, PlannedVariantType,
SupportSpec,
};
pub(crate) struct EmittedNameResolutionPass {
manifest: NameManifest,
tree_models: Vec<TreeModel>,
}
struct TreeModel {
module_key: String,
full_name: String,
planned_name: String,
}
impl EmittedNameResolutionPass {
pub(crate) fn new(language: Language, tree: &ApiSpecTree<PlannedFamily>) -> Result<Self> {
let mut models = Vec::new();
let mut services = Vec::new();
collect_tree_manifest_inputs(language, &tree.root, &mut models, &mut services);
let manifest = build_name_manifest(language, &models, &services)?;
let tree_models = models
.iter()
.map(|model| TreeModel {
module_key: model.module_key.clone(),
full_name: model.full_name.clone(),
planned_name: model.model_name.clone(),
})
.collect();
Ok(Self {
manifest,
tree_models,
})
}
fn model_name(&self, full_name: &str) -> Option<&str> {
self.manifest.type_name(full_name)
}
fn cross_module_model_names(&self, module_key: &str) -> BTreeMap<String, String> {
self.tree_models
.iter()
.filter(|model| model.module_key != module_key)
.filter_map(|model| {
let resolved = self.model_name(&model.full_name)?;
Some((model.full_name.clone(), resolved.to_string()))
})
.collect()
}
fn imported_model_name(&self, module_path: &ModulePath, planned_name: &str) -> Option<&str> {
let module_key = module_path.as_module_key();
self.tree_models
.iter()
.find(|model| model.module_key == module_key && model.planned_name == planned_name)
.and_then(|model| self.model_name(&model.full_name))
}
}
impl CompilerPass<PlannedFamily, PlannedFamily> for EmittedNameResolutionPass {
type Error = Error;
fn transform_leaf(
&mut self,
leaf: ApiSpecLeaf<PlannedFamily>,
) -> Result<ApiSpecLeaf<PlannedFamily>> {
let module_key = leaf.spec.module_path.as_module_key();
let spec = leaf.spec.map_names(EmittedNameMapper {
pass: self,
module_key,
});
Ok(ApiSpecLeaf { spec, ..leaf })
}
}
struct EmittedNameMapper<'a> {
pass: &'a EmittedNameResolutionPass,
module_key: String,
}
impl ApiSpecTransform<PlannedFamily, PlannedFamily> for EmittedNameMapper<'_> {
fn map_spec_data(&mut self, data: PlannedSpecData) -> PlannedSpecData {
PlannedSpecData {
module_imports: data
.module_imports
.into_iter()
.map(|(module_path, names)| {
let names: BTreeSet<String> = names
.into_iter()
.map(|name| {
self.pass
.imported_model_name(&module_path, &name)
.unwrap_or(name.as_str())
.to_string()
})
.collect();
(module_path, names)
})
.collect(),
cross_module_model_names: self.pass.cross_module_model_names(&self.module_key),
}
}
fn map_json(&mut self, mut value: PlannedJsonType) -> PlannedJsonType {
if let Some(resolved) = self.pass.model_name(&value.full_name) {
value.model_name = resolved.to_string();
}
value
}
fn map_record(&mut self, value: PlannedRecordType) -> PlannedRecordType {
value
}
fn map_enum(&mut self, value: PlannedEnumType) -> PlannedEnumType {
value
}
fn map_flags(&mut self, value: PlannedFlagsType) -> PlannedFlagsType {
value
}
fn map_variant(&mut self, value: PlannedVariantType) -> PlannedVariantType {
value
}
fn map_resource(&mut self, value: PlannedResourceType) -> PlannedResourceType {
value
}
fn map_proto(&mut self, value: PlannedProtoType) -> PlannedProtoType {
value
}
fn map_alias(&mut self, value: PlannedAliasType) -> PlannedAliasType {
value
}
fn map_service_data(&mut self, _: &str, _: ()) {}
fn map_record_data(&mut self, _: &str, value: PlannedRecordData) -> PlannedRecordData {
value
}
fn map_resource_data(&mut self, _: &str, value: PlannedResource) -> PlannedResource {
value
}
fn map_operation_data(&mut self, _: &str, value: PlannedOperationData) -> PlannedOperationData {
value
}
fn map_field_data(&mut self, _: &str, _: &str, value: PlannedFieldData) -> PlannedFieldData {
value
}
fn map_text(&mut self, value: LanguageStringSpec) -> LanguageStringSpec {
value
}
fn map_support(&mut self, value: SupportSpec) -> SupportSpec {
value
}
}
pub(crate) fn build_json_name_manifest(
language: Language,
api_plan: &PlannedSpec,
) -> Result<NameManifest> {
let models = manifest_models(api_plan);
let services = manifest_services(language, api_plan);
build_name_manifest(language, &models, &services)
}
fn collect_tree_manifest_inputs(
language: Language,
node: &ApiSpecNode<PlannedFamily>,
models: &mut Vec<ManifestModel>,
services: &mut Vec<ManifestService>,
) {
match node {
ApiSpecNode::Leaf(leaf) => {
models.extend(manifest_models(&leaf.spec));
services.extend(manifest_services(language, &leaf.spec));
}
ApiSpecNode::Branch(branch) => {
for child in branch.children.values() {
collect_tree_manifest_inputs(language, child, models, services);
}
}
}
}
fn manifest_models(api_plan: &PlannedSpec) -> Vec<ManifestModel> {
api_plan
.external_types()
.filter_map(|(_full_name, binding)| match &binding.external_type {
ExternalTypeSpec::Json(json) => Some(manifest_model(json)),
_ => None,
})
.collect()
}
fn manifest_model(json: &PlannedJsonType) -> ManifestModel {
let module_key = json
.module_path
.as_ref()
.map(ModulePath::as_module_key)
.unwrap_or_default();
let local_name = json
.full_name
.rsplit(['#', '/'])
.next()
.unwrap_or(&json.full_name)
.to_string();
ManifestModel {
full_name: json.full_name.clone(),
local_name,
model_name: json.model_name.clone(),
module_key,
schema: json.schema.clone(),
}
}
fn manifest_services(language: Language, api_plan: &PlannedSpec) -> Vec<ManifestService> {
api_plan
.services
.iter()
.map(|service| ManifestService {
name: service.name.clone(),
code_name: service.code_name.for_language(language).map(str::to_string),
module_key: api_plan.module_path.as_module_key(),
})
.collect()
}