presolve_compiler/
context_declaration_candidate.rs1use std::collections::BTreeMap;
2
3use crate::{
4 AuthoredContextDeclarationCandidate, ConsumerEntity, ConsumerId, ContextDeclarationCandidateId,
5 ContextDeclarationCandidateKind, ContextDeclarationViolation, ContextEntity, ContextId,
6 ProviderEntity, ProviderId, SemanticId,
7};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum ContextSemanticEntityId {
12 Context(ContextId),
13 Provider(ProviderId),
14 Consumer(ConsumerId),
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum ContextDeclarationStatus {
19 Valid(ContextSemanticEntityId),
20 Invalid(Vec<ContextDeclarationViolation>),
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct ContextDeclarationCandidate {
27 pub authored: AuthoredContextDeclarationCandidate,
28 pub status: ContextDeclarationStatus,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct ContextDeclarationCandidateRegistry {
33 candidates: Vec<ContextDeclarationCandidate>,
34 by_id: BTreeMap<ContextDeclarationCandidateId, usize>,
35}
36
37impl ContextDeclarationCandidateRegistry {
38 #[must_use]
39 pub fn candidates(&self) -> &[ContextDeclarationCandidate] {
40 &self.candidates
41 }
42
43 #[must_use]
44 pub fn candidate(
45 &self,
46 id: &ContextDeclarationCandidateId,
47 ) -> Option<&ContextDeclarationCandidate> {
48 self.by_id
49 .get(id)
50 .and_then(|index| self.candidates.get(*index))
51 }
52
53 #[must_use]
54 pub fn invalid_candidates(&self) -> Vec<&ContextDeclarationCandidate> {
55 self.candidates
56 .iter()
57 .filter(|candidate| matches!(candidate.status, ContextDeclarationStatus::Invalid(_)))
58 .collect()
59 }
60}
61
62#[must_use]
69pub fn collect_context_declaration_candidates(
70 components: &[crate::ComponentNode],
71 contexts: &BTreeMap<ContextId, ContextEntity>,
72 providers: &BTreeMap<ProviderId, ProviderEntity>,
73 consumers: &BTreeMap<ConsumerId, ConsumerEntity>,
74) -> ContextDeclarationCandidateRegistry {
75 let mut candidates = components
76 .iter()
77 .flat_map(|component| component.context_declaration_candidates.iter())
78 .cloned()
79 .map(|authored| {
80 let mut violations = authored.violations.clone();
81 let link = match authored.kind {
82 ContextDeclarationCandidateKind::Context => entity_for_context(&authored, contexts),
83 ContextDeclarationCandidateKind::Provider => {
84 entity_for_provider(&authored, providers)
85 }
86 ContextDeclarationCandidateKind::Consumer => {
87 entity_for_consumer(&authored, consumers)
88 }
89 };
90 if violations.is_empty()
91 && link.is_none()
92 && matches!(
93 authored.kind,
94 ContextDeclarationCandidateKind::Provider
95 | ContextDeclarationCandidateKind::Consumer
96 )
97 {
98 violations.push(ContextDeclarationViolation::UnresolvedContextDesignator);
99 }
100 let status = if violations.is_empty() {
101 ContextDeclarationStatus::Valid(
102 link.unwrap_or_else(|| unreachable!("structurally valid candidate must lower")),
103 )
104 } else {
105 violations.sort_by_key(violation_rank);
106 violations.dedup();
107 ContextDeclarationStatus::Invalid(violations)
108 };
109 ContextDeclarationCandidate { authored, status }
110 })
111 .collect::<Vec<_>>();
112
113 let mut groups = BTreeMap::<(SemanticId, String, String), Vec<usize>>::new();
116 for (index, candidate) in candidates.iter().enumerate() {
117 if candidate.authored.kind == ContextDeclarationCandidateKind::Provider {
118 if let Some(designator) = &candidate.authored.context_designator {
119 groups
120 .entry((
121 candidate.authored.owner_component.clone(),
122 designator.component_symbol.clone(),
123 designator.context_member.clone(),
124 ))
125 .or_default()
126 .push(index);
127 }
128 }
129 }
130 for group in groups.values().filter(|group| group.len() > 1) {
131 for index in group {
132 let candidate = &mut candidates[*index];
133 match &mut candidate.status {
134 ContextDeclarationStatus::Valid(_) => {
135 candidate.status = ContextDeclarationStatus::Invalid(vec![
136 ContextDeclarationViolation::DuplicateProvider,
137 ]);
138 }
139 ContextDeclarationStatus::Invalid(violations) => {
140 if violations.as_slice()
141 == [ContextDeclarationViolation::UnresolvedContextDesignator]
142 {
143 violations.clear();
144 }
145 violations.push(ContextDeclarationViolation::DuplicateProvider);
146 violations.sort_by_key(violation_rank);
147 violations.dedup();
148 }
149 }
150 }
151 }
152 candidates.sort_by(|left, right| left.authored.id.cmp(&right.authored.id));
153 let by_id = candidates
154 .iter()
155 .enumerate()
156 .map(|(index, candidate)| (candidate.authored.id.clone(), index))
157 .collect();
158 ContextDeclarationCandidateRegistry { candidates, by_id }
159}
160
161fn entity_for_context(
162 candidate: &AuthoredContextDeclarationCandidate,
163 contexts: &BTreeMap<ContextId, ContextEntity>,
164) -> Option<ContextSemanticEntityId> {
165 contexts
166 .values()
167 .find(|entity| {
168 entity.owner.entity_id() == Some(&candidate.owner_component)
169 && candidate.field_name.as_deref() == Some(&entity.name)
170 })
171 .map(|entity| ContextSemanticEntityId::Context(entity.id.clone()))
172}
173fn entity_for_provider(
174 candidate: &AuthoredContextDeclarationCandidate,
175 providers: &BTreeMap<ProviderId, ProviderEntity>,
176) -> Option<ContextSemanticEntityId> {
177 providers
178 .values()
179 .find(|entity| {
180 entity.owner.entity_id() == Some(&candidate.owner_component)
181 && candidate.field_name.as_deref() == Some(&entity.name)
182 })
183 .map(|entity| ContextSemanticEntityId::Provider(entity.id.clone()))
184}
185fn entity_for_consumer(
186 candidate: &AuthoredContextDeclarationCandidate,
187 consumers: &BTreeMap<ConsumerId, ConsumerEntity>,
188) -> Option<ContextSemanticEntityId> {
189 consumers
190 .values()
191 .find(|entity| {
192 entity.owner.entity_id() == Some(&candidate.owner_component)
193 && candidate.field_name.as_deref() == Some(&entity.name)
194 })
195 .map(|entity| ContextSemanticEntityId::Consumer(entity.id.clone()))
196}
197fn violation_rank(violation: &ContextDeclarationViolation) -> u8 {
198 match violation {
199 ContextDeclarationViolation::InvalidDeclarationKind { .. }
200 | ContextDeclarationViolation::ConflictingSemanticDecorators => 0,
201 ContextDeclarationViolation::StaticDeclarationUnsupported => 1,
202 ContextDeclarationViolation::DecoratorArity { .. } => 2,
203 ContextDeclarationViolation::ContextDesignatorUnsupported => 3,
204 ContextDeclarationViolation::UnresolvedContextDesignator => 4,
205 ContextDeclarationViolation::MissingDeclaredType => 5,
206 ContextDeclarationViolation::MissingInitializer
207 | ContextDeclarationViolation::ForbiddenInitializer
208 | ContextDeclarationViolation::UnsupportedInitializer
209 | ContextDeclarationViolation::DefiniteAssignmentRequired => 6,
210 ContextDeclarationViolation::DuplicateProvider => 7,
211 }
212}