Skip to main content

pedant_types/resolution/
builder.rs

1//! The one cross-crate writer for resolution reports.
2//!
3//! A writer states facts and receives handles; identifiers, ordering, and
4//! validation belong to [`ResolutionReportBuilder::finish`]. An unvalidated
5//! report is therefore unrepresentable outside this crate.
6
7use std::sync::Arc;
8
9use crate::Language;
10
11use super::definition::SymbolKind;
12use super::error::ResolutionReportError;
13use super::finish::assemble;
14use super::handle::{
15    BuilderBrand, CandidateInput, DefinitionHandle, ReferenceHandle, ResolutionUnitHandle,
16};
17use super::record::{ResolutionCertainty, ResolutionGap};
18use super::reference::ReferenceKind;
19use super::report::{ResolutionReport, ResolutionTier};
20use super::span::SourceSpan;
21
22/// How many units, definitions, references, and resolution records one report
23/// may contain.
24///
25/// The default is the fixed-width identifier ceiling, so on the writer path the
26/// configured limit and the ceiling are enforced by the same check rather than
27/// by two rules that can disagree. Callers may apply the same limits while
28/// decoding through [`ResolutionReport::deserialize_with_limits`].
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub struct ResolutionReportLimits {
31    /// Maximum number of resolution units.
32    pub max_units: u32,
33    /// Maximum number of definitions.
34    pub max_definitions: u32,
35    /// Maximum number of references.
36    ///
37    /// Decoding applies the same ceiling to resolution records because a valid
38    /// report carries exactly one record per reference.
39    pub max_references: u32,
40}
41
42impl Default for ResolutionReportLimits {
43    fn default() -> Self {
44        Self {
45            max_units: u32::MAX,
46            max_definitions: u32::MAX,
47            max_references: u32::MAX,
48        }
49    }
50}
51
52impl ResolutionReportLimits {
53    /// The index the next unit would take, or the capacity refusal.
54    pub fn admit_unit(&self, count: usize) -> Result<u32, ResolutionReportError> {
55        admit(count, self.max_units, |limit| {
56            ResolutionReportError::UnitCapacityExceeded { limit }
57        })
58    }
59
60    /// The index the next definition would take, or the capacity refusal.
61    pub fn admit_definition(&self, count: usize) -> Result<u32, ResolutionReportError> {
62        admit(count, self.max_definitions, |limit| {
63            ResolutionReportError::DefinitionCapacityExceeded { limit }
64        })
65    }
66
67    /// The index the next reference would take, or the capacity refusal.
68    pub fn admit_reference(&self, count: usize) -> Result<u32, ResolutionReportError> {
69        admit(count, self.max_references, |limit| {
70            ResolutionReportError::ReferenceCapacityExceeded { limit }
71        })
72    }
73}
74
75/// The one capacity rule: `count` entries admit index `count` while it is both
76/// representable and below the limit.
77fn admit(
78    count: usize,
79    limit: u32,
80    exceeded: impl Fn(u32) -> ResolutionReportError,
81) -> Result<u32, ResolutionReportError> {
82    u32::try_from(count)
83        .ok()
84        .filter(|next| *next < limit)
85        .ok_or_else(|| exceeded(limit))
86}
87
88/// A unit a writer has stated but that has no identifier yet.
89pub(crate) struct DraftUnit {
90    pub(crate) language: Language,
91    pub(crate) key: Arc<str>,
92    pub(crate) name: Arc<str>,
93}
94
95/// A definition a writer has stated, pointing at drafts by local index.
96pub(crate) struct DraftDefinition {
97    pub(crate) unit: u32,
98    pub(crate) kind: SymbolKind,
99    pub(crate) name: Arc<str>,
100    pub(crate) span: SourceSpan,
101    pub(crate) parent: Option<u32>,
102}
103
104/// A reference a writer has stated, pointing at drafts by local index.
105pub(crate) struct DraftReference {
106    pub(crate) unit: u32,
107    pub(crate) kind: ReferenceKind,
108    pub(crate) text: Arc<str>,
109    pub(crate) span: SourceSpan,
110    pub(crate) enclosing: Option<u32>,
111}
112
113/// One reference's answer, pointing at draft definitions by local index.
114pub(crate) struct DraftResolution {
115    pub(crate) candidates: Box<[(u32, ResolutionCertainty)]>,
116    pub(crate) gaps: Box<[ResolutionGap]>,
117}
118
119/// The fallible writer every report passes through.
120pub struct ResolutionReportBuilder {
121    brand: Arc<BuilderBrand>,
122    tier: ResolutionTier,
123    limits: ResolutionReportLimits,
124    units: Vec<DraftUnit>,
125    definitions: Vec<DraftDefinition>,
126    references: Vec<DraftReference>,
127    resolutions: Vec<Option<DraftResolution>>,
128}
129
130impl ResolutionReportBuilder {
131    /// An empty builder for one tier under one set of limits.
132    pub fn new(tier: ResolutionTier, limits: ResolutionReportLimits) -> Self {
133        Self {
134            brand: Arc::new(BuilderBrand),
135            tier,
136            limits,
137            units: Vec::new(),
138            definitions: Vec::new(),
139            references: Vec::new(),
140            resolutions: Vec::new(),
141        }
142    }
143
144    /// State one resolution unit.
145    pub fn add_unit(
146        &mut self,
147        language: Language,
148        key: Arc<str>,
149        name: Arc<str>,
150    ) -> Result<ResolutionUnitHandle, ResolutionReportError> {
151        let index = self.limits.admit_unit(self.units.len())?;
152        self.units.push(DraftUnit {
153            language,
154            key,
155            name,
156        });
157        Ok(ResolutionUnitHandle::new(&self.brand, index))
158    }
159
160    /// State one definition inside a unit this builder issued.
161    pub fn add_definition(
162        &mut self,
163        unit: &ResolutionUnitHandle,
164        kind: SymbolKind,
165        name: Arc<str>,
166        span: SourceSpan,
167        parent: Option<&DefinitionHandle>,
168    ) -> Result<DefinitionHandle, ResolutionReportError> {
169        let unit = self.local_unit(unit)?;
170        let parent = self.local_definition_option(parent)?;
171        let index = self.limits.admit_definition(self.definitions.len())?;
172        self.definitions.push(DraftDefinition {
173            unit,
174            kind,
175            name,
176            span,
177            parent,
178        });
179        Ok(DefinitionHandle::new(&self.brand, index))
180    }
181
182    /// State one reference inside a unit this builder issued.
183    pub fn add_reference(
184        &mut self,
185        unit: &ResolutionUnitHandle,
186        kind: ReferenceKind,
187        text: Arc<str>,
188        span: SourceSpan,
189        enclosing_definition: Option<&DefinitionHandle>,
190    ) -> Result<ReferenceHandle, ResolutionReportError> {
191        let unit = self.local_unit(unit)?;
192        let enclosing = self.local_definition_option(enclosing_definition)?;
193        let index = self.limits.admit_reference(self.references.len())?;
194        self.references.push(DraftReference {
195            unit,
196            kind,
197            text,
198            span,
199            enclosing,
200        });
201        self.resolutions.push(None);
202        Ok(ReferenceHandle::new(&self.brand, index))
203    }
204
205    /// State the answer for one reference this builder issued.
206    pub fn set_resolution(
207        &mut self,
208        reference: &ReferenceHandle,
209        candidates: Box<[CandidateInput]>,
210        gaps: Box<[ResolutionGap]>,
211    ) -> Result<(), ResolutionReportError> {
212        let index = reference
213            .resolve(&self.brand)
214            .ok_or(ResolutionReportError::ForeignReferenceHandle)?;
215        let stated = candidates
216            .into_vec()
217            .into_iter()
218            .map(|candidate| {
219                let (definition, certainty) = candidate.into_parts();
220                self.local_definition(&definition)
221                    .map(|definition| (definition, certainty))
222            })
223            .collect::<Result<Vec<_>, _>>()?;
224        let slot = self
225            .resolutions
226            .get_mut(index as usize)
227            .ok_or(ResolutionReportError::ForeignReferenceHandle)?;
228        match slot.is_some() {
229            true => Err(ResolutionReportError::DuplicateResolution { reference: index }),
230            false => {
231                *slot = Some(DraftResolution {
232                    candidates: stated.into_boxed_slice(),
233                    gaps,
234                });
235                Ok(())
236            }
237        }
238    }
239
240    /// Sort, assign identifiers, validate, and return the report.
241    ///
242    /// The drafts move into the report: a writer that has finished stating
243    /// facts owns nothing the report needs to copy.
244    pub fn finish(self) -> Result<ResolutionReport, ResolutionReportError> {
245        assemble(
246            self.tier,
247            self.units,
248            self.definitions,
249            self.references,
250            self.resolutions,
251        )
252    }
253
254    fn local_unit(&self, unit: &ResolutionUnitHandle) -> Result<u32, ResolutionReportError> {
255        unit.resolve(&self.brand)
256            .ok_or(ResolutionReportError::ForeignUnitHandle)
257    }
258
259    fn local_definition(
260        &self,
261        definition: &DefinitionHandle,
262    ) -> Result<u32, ResolutionReportError> {
263        definition
264            .resolve(&self.brand)
265            .ok_or(ResolutionReportError::ForeignDefinitionHandle)
266    }
267
268    fn local_definition_option(
269        &self,
270        definition: Option<&DefinitionHandle>,
271    ) -> Result<Option<u32>, ResolutionReportError> {
272        definition
273            .map(|handle| self.local_definition(handle))
274            .transpose()
275    }
276}