pedant_core/resolution/rust/resolve/
target.rs1use std::collections::BTreeMap;
11use std::sync::Arc;
12
13use pedant_types::{
14 ResolutionReport, ResolutionUnit, ResolutionUnitId, SourceSpan, SymbolDefinition,
15 SymbolReference,
16};
17
18use crate::resolution::rust::identity::{PackageId, TargetId, position};
19use crate::resolution::rust::snapshot::{
20 RustResolutionSnapshot, RustResolutionUnit, RustSnapshotUnitId,
21};
22use crate::resolution::rust::warning;
23
24use super::coordinates::LineIndex;
25use super::error::RustResolutionError;
26
27pub(super) fn unit_key(unit: &RustResolutionUnit) -> Arc<str> {
29 warning::unit_key(unit)
30}
31
32pub(super) fn crate_name(unit: &RustResolutionUnit) -> Arc<str> {
34 Arc::from(unit.name().replace('-', "_"))
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct RustUnitBinding {
40 unit: ResolutionUnitId,
41 snapshot_unit: RustSnapshotUnitId,
42 package: PackageId,
43 target: TargetId,
44}
45
46impl RustUnitBinding {
47 pub fn unit(&self) -> ResolutionUnitId {
49 self.unit
50 }
51
52 pub fn snapshot_unit(&self) -> RustSnapshotUnitId {
54 self.snapshot_unit
55 }
56
57 pub fn package(&self) -> PackageId {
59 self.package
60 }
61
62 pub fn target(&self) -> TargetId {
64 self.target
65 }
66}
67
68#[derive(Debug, Clone)]
70pub struct RustTargetResolution {
71 root_target: TargetId,
72 units: Box<[RustUnitBinding]>,
73 report: Arc<ResolutionReport>,
74}
75
76impl RustTargetResolution {
77 pub fn try_new(
79 snapshot: &RustResolutionSnapshot,
80 report: ResolutionReport,
81 ) -> Result<Self, RustResolutionError> {
82 let units = bind_units(snapshot, report.units())?;
83 validate_sites(snapshot, &report)?;
84 Ok(Self {
85 root_target: snapshot.root_target(),
86 units,
87 report: Arc::new(report),
88 })
89 }
90
91 pub fn root_target(&self) -> TargetId {
93 self.root_target
94 }
95
96 pub fn units(&self) -> &[RustUnitBinding] {
98 &self.units
99 }
100
101 pub fn unit(&self, unit: ResolutionUnitId) -> Option<&RustUnitBinding> {
103 self.units.get(usize::try_from(unit.index()).ok()?)
104 }
105
106 pub fn report(&self) -> &ResolutionReport {
108 &self.report
109 }
110
111 pub fn shared_report(&self) -> Arc<ResolutionReport> {
113 Arc::clone(&self.report)
114 }
115}
116
117fn bind_units(
118 snapshot: &RustResolutionSnapshot,
119 units: &[ResolutionUnit],
120) -> Result<Box<[RustUnitBinding]>, RustResolutionError> {
121 if units.len() != snapshot.units().len() {
122 return Err(RustResolutionError::UnitMapping {
123 unit: position(units.len()),
124 reason: Box::from("the report and the snapshot hold different unit counts"),
125 });
126 }
127 let keyed = keyed_units(snapshot);
128 units.iter().map(|unit| bind_unit(&keyed, unit)).collect()
129}
130
131fn keyed_units(snapshot: &RustResolutionSnapshot) -> BTreeMap<Arc<str>, &RustResolutionUnit> {
134 snapshot
135 .units()
136 .iter()
137 .map(|unit| (unit_key(unit), unit))
138 .collect()
139}
140
141fn bind_unit(
142 keyed: &BTreeMap<Arc<str>, &RustResolutionUnit>,
143 unit: &ResolutionUnit,
144) -> Result<RustUnitBinding, RustResolutionError> {
145 let found = keyed
146 .get(unit.key())
147 .ok_or_else(|| RustResolutionError::UnitMapping {
148 unit: unit.id().index(),
149 reason: Box::from("no snapshot unit carries this key"),
150 })?;
151 Ok(RustUnitBinding {
152 unit: unit.id(),
153 snapshot_unit: found.id(),
154 package: found.package(),
155 target: found.target(),
156 })
157}
158
159fn validate_sites(
165 snapshot: &RustResolutionSnapshot,
166 report: &ResolutionReport,
167) -> Result<(), RustResolutionError> {
168 let lines = indexed_sources(snapshot);
169 for definition in report.definitions() {
170 validate_span(snapshot, &lines, SymbolDefinition::span(definition))?;
171 }
172 for reference in report.references() {
173 validate_span(snapshot, &lines, SymbolReference::span(reference))?;
174 }
175 Ok(())
176}
177
178fn indexed_sources(snapshot: &RustResolutionSnapshot) -> BTreeMap<&str, LineIndex> {
179 snapshot
180 .sources()
181 .iter()
182 .map(|source| (source.path(), LineIndex::new(source.text())))
183 .collect()
184}
185
186fn validate_span(
187 snapshot: &RustResolutionSnapshot,
188 lines: &BTreeMap<&str, LineIndex>,
189 span: &SourceSpan,
190) -> Result<(), RustResolutionError> {
191 let stated = snapshot
192 .source(span.file())
193 .zip(lines.get(span.file()))
194 .ok_or_else(|| RustResolutionError::UnknownFile {
195 file: Box::from(span.file()),
196 })?;
197 let (source, index) = stated;
198 for at in [span.start(), span.end()] {
199 if !index.holds(source.text(), at) {
200 return Err(RustResolutionError::InvalidCoordinate {
201 file: Box::from(span.file()),
202 line: at.line(),
203 column: at.column(),
204 });
205 }
206 }
207 Ok(())
208}