macroonz_compiler/codec/
type_guard.rs1use super::super::bank::RESERVED_BINDINGS;
7use super::{
8 AssemblyPosture, CODEC_ISSUE_LIMIT, Cardinality, CodecAssembly, CodecError, CodecIssue,
9 CodecMember, CodecMemberShape, CodecShape, CodecTypePath, ModuleSpelling, PathRooting,
10};
11use crate::bounded::{Capped, Capping, NonEmpty, NonEmptyError, Overflow};
12use std::collections::BTreeSet;
13
14impl CodecTypePath {
15 pub fn spelled(rooting: PathRooting, segments: Vec<String>) -> Result<Self, CodecError> {
22 for segment in &segments {
23 if !rendered_name(segment) {
24 return Err(CodecError::of(CodecIssue::SegmentNotAnIdentifier {
25 segment: segment.clone(),
26 }));
27 }
28 }
29 let admitted = NonEmpty::new(segments).map_err(|refused| match refused {
30 NonEmptyError::Empty(_) => CodecError::of(CodecIssue::PathSegmentsAbsent),
31 NonEmptyError::Overflow(overflow) => {
32 let (bound, observed) = counted(overflow);
33 CodecError::of(CodecIssue::PathSegmentsUnbounded { bound, observed })
34 }
35 })?;
36 Ok(Self {
37 rooting,
38 segments: admitted,
39 })
40 }
41
42 #[must_use]
44 pub const fn rooting(&self) -> PathRooting {
45 self.rooting
46 }
47
48 pub fn segments(&self) -> impl Iterator<Item = &str> {
50 self.segments.iter().map(String::as_str)
51 }
52
53 #[must_use]
55 pub fn count(&self) -> usize {
56 self.segments.count()
57 }
58}
59
60impl ModuleSpelling {
61 pub fn spelled(spelling: &str) -> Result<Self, CodecError> {
67 if rendered_name(spelling) {
68 Ok(Self {
69 spelling: spelling.to_owned(),
70 })
71 } else {
72 Err(CodecError::of(CodecIssue::ModuleSpellingNotAnIdentifier {
73 spelling: spelling.to_owned(),
74 }))
75 }
76 }
77
78 #[must_use]
80 pub fn spelling(&self) -> &str {
81 self.spelling.as_str()
82 }
83}
84
85impl CodecMember {
86 pub fn declared(
93 spelling: &str,
94 held_as: CodecTypePath,
95 shape: CodecMemberShape,
96 cardinality: Cardinality,
97 ) -> Result<Self, CodecError> {
98 if spelling.is_empty() {
99 return Err(CodecError::of(CodecIssue::MemberSpellingAbsent));
100 }
101 if !rendered_name(spelling) {
102 return Err(CodecError::of(CodecIssue::MemberSpellingNotAnIdentifier {
103 spelling: spelling.to_owned(),
104 }));
105 }
106 Ok(Self {
107 spelling: spelling.to_owned(),
108 held_as,
109 shape,
110 cardinality,
111 })
112 }
113
114 #[must_use]
116 pub fn spelling(&self) -> &str {
117 self.spelling.as_str()
118 }
119
120 #[must_use]
122 pub const fn held_as(&self) -> &CodecTypePath {
123 &self.held_as
124 }
125
126 #[must_use]
128 pub const fn shape(&self) -> CodecMemberShape {
129 self.shape
130 }
131
132 #[must_use]
134 pub const fn cardinality(&self) -> Cardinality {
135 self.cardinality
136 }
137}
138
139impl CodecAssembly {
140 pub fn stated(road: &str, posture: AssemblyPosture) -> Result<Self, CodecError> {
146 if road.is_empty() {
147 return Err(CodecError::of(CodecIssue::AssemblyRoadAbsent));
148 }
149 if !rendered_name(road) {
150 return Err(CodecError::of(CodecIssue::AssemblyRoadNotAnIdentifier {
151 spelling: road.to_owned(),
152 }));
153 }
154 Ok(Self {
155 road: road.to_owned(),
156 posture,
157 })
158 }
159
160 #[must_use]
162 pub fn road(&self) -> &str {
163 self.road.as_str()
164 }
165
166 #[must_use]
168 pub const fn posture(&self) -> &AssemblyPosture {
169 &self.posture
170 }
171}
172
173impl CodecShape {
174 pub fn declared(
180 owner: CodecTypePath,
181 refusal: &str,
182 assembly: CodecAssembly,
183 members: Vec<CodecMember>,
184 ) -> Result<Self, CodecError> {
185 if !rendered_name(refusal) {
186 return Err(CodecError::of(CodecIssue::RefusalSpellingNotAnIdentifier {
187 spelling: refusal.to_owned(),
188 }));
189 }
190 if let Some((first, rest)) = established(member_issues(&members)) {
191 return Err(CodecError::over(first, rest));
192 }
193 let admitted = NonEmpty::new(members).map_err(|refused| match refused {
194 NonEmptyError::Empty(_) => CodecError::of(CodecIssue::MembersAbsent),
195 NonEmptyError::Overflow(overflow) => {
196 let (bound, observed) = counted(overflow);
197 CodecError::of(CodecIssue::MembersUnbounded { bound, observed })
198 }
199 })?;
200 Ok(Self {
201 owner,
202 refusal: refusal.to_owned(),
203 assembly,
204 members: admitted,
205 })
206 }
207
208 #[must_use]
210 pub const fn owner(&self) -> &CodecTypePath {
211 &self.owner
212 }
213
214 #[must_use]
216 pub fn refusal(&self) -> &str {
217 self.refusal.as_str()
218 }
219
220 #[must_use]
222 pub const fn assembly(&self) -> &CodecAssembly {
223 &self.assembly
224 }
225
226 pub fn members(&self) -> impl Iterator<Item = &CodecMember> {
232 self.members.iter()
233 }
234
235 #[must_use]
237 pub fn count(&self) -> usize {
238 self.members.count()
239 }
240}
241
242impl CodecError {
243 pub fn of(issue: CodecIssue) -> Self {
245 Self {
246 body: Capped::all(NonEmpty::one(issue)),
247 }
248 }
249
250 pub fn over(first: CodecIssue, rest: Vec<CodecIssue>) -> Self {
254 Self {
255 body: Capped::first_n(first, rest.into_iter()),
256 }
257 }
258
259 #[must_use]
261 pub fn first_issue(&self) -> &CodecIssue {
262 self.body.items().first()
263 }
264
265 #[must_use]
267 pub fn issues(&self) -> &NonEmpty<CodecIssue, CODEC_ISSUE_LIMIT> {
268 self.body.items()
269 }
270
271 #[must_use]
273 pub const fn capping(&self) -> Capping {
274 self.body.capping()
275 }
276}
277
278pub use crate::token::{rendered_identifier, rendered_name};
279
280fn member_issues(members: &[CodecMember]) -> Vec<CodecIssue> {
285 let mut issues: Vec<CodecIssue> = Vec::new();
286 let mut seen: BTreeSet<&str> = BTreeSet::new();
287 let mut reported: BTreeSet<&str> = BTreeSet::new();
288 for member in members {
289 let standing = member_standing(member, &mut seen, &mut reported);
290 if let Some(spelling) = standing.doubled {
291 issues.push(CodecIssue::MemberSpellingDoubled {
292 spelling: spelling.to_owned(),
293 });
294 }
295 if let Some((spelling, binding)) = standing.shadowed {
296 issues.push(CodecIssue::MemberShadowsBinding {
297 spelling: spelling.to_owned(),
298 binding,
299 });
300 }
301 }
302 issues
303}
304
305struct MemberStanding<'member> {
307 doubled: Option<&'member str>,
308 shadowed: Option<(&'member str, &'static str)>,
309}
310
311fn member_standing<'member>(
313 member: &'member CodecMember,
314 seen: &mut BTreeSet<&'member str>,
315 reported: &mut BTreeSet<&'member str>,
316) -> MemberStanding<'member> {
317 let spelling = member.spelling();
318 let doubled = (!seen.insert(spelling) && reported.insert(spelling)).then_some(spelling);
319 let shadowed = RESERVED_BINDINGS
320 .into_iter()
321 .find(|binding| spelling == *binding)
322 .map(|binding| (spelling, binding));
323 MemberStanding { doubled, shadowed }
324}
325
326fn established(issues: Vec<CodecIssue>) -> Option<(CodecIssue, Vec<CodecIssue>)> {
328 let mut walk = issues.into_iter();
329 let first = walk.next()?;
330 Some((first, walk.collect()))
331}
332
333fn counted(overflow: Overflow) -> (u64, u64) {
335 (
336 u64::try_from(overflow.capacity).unwrap_or(u64::MAX),
337 u64::try_from(overflow.offered).unwrap_or(u64::MAX),
338 )
339}