1use super::{
19 Fragment, Part, Pattern, Seat, Seating, Site, Stamp, StampError, TransportedReach, Visibility,
20};
21use crate::bounded::Overflow;
22use crate::token::{self, GeneratedDelimiter, GeneratedToken};
23
24pub const TRANSCRIBE_ARM: &str = "transcribe";
28
29pub const DECLARED_REACH: &str = "declared_reach";
31
32pub const TRANSPORTED_REACH: &str = "transported_reach";
34
35pub const OPAQUE_REACH_REFUSAL: &str = "this stamp requires visibility tokens at its front door; \
39 an opaque forwarded `vis` fragment cannot be transported one module deeper, and a reach \
40 guessed in its place would publish an item nobody declared visible.";
41
42const OPAQUE_REACH: &str = "opaque_reach";
44
45const VIS_FRAGMENT: &str = "vis";
49
50pub fn definition(stamp: &Stamp) -> Result<Vec<GeneratedToken>, StampError> {
63 let pattern = stamp.pattern();
64 let mut rules: Vec<GeneratedToken> = Vec::new();
65 if pattern.reaches() {
66 for reach in Visibility::ALL {
67 rules.extend(front_arm(stamp, *reach)?);
68 }
69 rules.extend(transcribe_arm(pattern)?);
70 rules.extend(refusing_arm(pattern)?);
71 } else {
72 let expansion = pattern.body().tokens().to_vec();
73 rules.extend(rule(matched(pattern, &[])?, expansion)?);
74 }
75
76 let mut tokens = token::documentation(pattern.note())?;
77 tokens.extend(token::attribute(vec![GeneratedToken::word(
78 "macro_export",
79 )])?);
80 tokens.push(GeneratedToken::word("macro_rules"));
81 tokens.push(GeneratedToken::alone('!'));
82 tokens.push(GeneratedToken::word(stamp.name().spelling()));
83 tokens.push(token::group(GeneratedDelimiter::Brace, rules)?);
84 Ok(tokens)
85}
86
87pub fn invocation(stamp: &Stamp, site: &Site) -> Result<Vec<GeneratedToken>, StampError> {
95 let mut body: Vec<GeneratedToken> = Vec::new();
96 let mut supplied = site.arguments().iter();
97 for part in stamp.pattern().parts() {
98 let written = match part {
99 Part::Literal(material) => material.tokens().to_vec(),
100 Part::Seat(_) => supplied
101 .next()
102 .map_or_else(Vec::new, |argument| argument.tokens().to_vec()),
103 Part::Reach => declared_reach(site.reach())?,
104 };
105 body.extend(written);
106 }
107
108 let mut tokens = stamp_path(site, stamp.name().spelling());
109 tokens.push(GeneratedToken::alone('!'));
110 tokens.push(token::group(GeneratedDelimiter::Brace, body)?);
111 Ok(tokens)
112}
113
114pub fn forwarded(seat: &Seat) -> Result<Vec<GeneratedToken>, StampError> {
120 match seat.seating() {
121 Seating::One(_) => Ok(token::metavariable(seat.name())),
122 Seating::Many(_) => repeated(token::metavariable(seat.name()), Some(',')),
123 Seating::Attributes => {
124 let inner = token::group(
125 GeneratedDelimiter::Bracket,
126 token::metavariable(seat.name()),
127 )?;
128 repeated(vec![GeneratedToken::alone('#'), inner], None)
129 }
130 }
131}
132
133pub fn declared_reach(reach: Visibility) -> Result<Vec<GeneratedToken>, StampError> {
139 Ok(declared_reach_tokens(reach)?)
140}
141
142pub(crate) fn declared_reach_tokens(reach: Visibility) -> Result<Vec<GeneratedToken>, Overflow> {
144 match reach {
145 Visibility::Private => Ok(Vec::new()),
146 Visibility::Module => scoped(vec![GeneratedToken::word("self")]),
147 Visibility::Parent => scoped(vec![GeneratedToken::word("super")]),
148 Visibility::Crate => scoped(vec![GeneratedToken::word("crate")]),
149 Visibility::Public => Ok(vec![GeneratedToken::word("pub")]),
150 }
151}
152
153pub fn transported_reach(reach: TransportedReach) -> Result<Vec<GeneratedToken>, StampError> {
159 Ok(match reach {
160 TransportedReach::Enclosing => scoped(vec![GeneratedToken::word("super")])?,
161 TransportedReach::Ancestor => scoped(vec![
162 GeneratedToken::word("in"),
163 GeneratedToken::word("super"),
164 GeneratedToken::joint(':'),
165 GeneratedToken::alone(':'),
166 GeneratedToken::word("super"),
167 ])?,
168 TransportedReach::Crate => scoped(vec![GeneratedToken::word("crate")])?,
169 TransportedReach::Public => vec![GeneratedToken::word("pub")],
170 })
171}
172
173fn front_arm(stamp: &Stamp, reach: Visibility) -> Result<Vec<GeneratedToken>, StampError> {
177 let pattern = stamp.pattern();
178 let declared = declared_reach(reach)?;
179 let matcher = matched(pattern, &declared)?;
180
181 let mut carried = vec![
182 GeneratedToken::joint('@'),
183 GeneratedToken::word(TRANSCRIBE_ARM),
184 bracketed_reach(transported_reach(reach.transported())?)?,
185 bracketed_reach(declared)?,
186 ];
187 carried.extend(restated(pattern)?);
188
189 let mut expansion = token::twin_path("crate", &[stamp.name().spelling()]);
190 expansion.push(GeneratedToken::alone('!'));
191 expansion.push(token::group(GeneratedDelimiter::Brace, carried)?);
192 rule(matcher, expansion)
193}
194
195fn transcribe_arm(pattern: &Pattern) -> Result<Vec<GeneratedToken>, StampError> {
197 let mut matcher = vec![
198 GeneratedToken::joint('@'),
199 GeneratedToken::word(TRANSCRIBE_ARM),
200 bracketed_reach(fragment_of(TRANSPORTED_REACH, VIS_FRAGMENT))?,
201 bracketed_reach(fragment_of(DECLARED_REACH, VIS_FRAGMENT))?,
202 ];
203 matcher.extend(matched(pattern, &[])?);
204 rule(matcher, pattern.body().tokens().to_vec())
205}
206
207fn refusing_arm(pattern: &Pattern) -> Result<Vec<GeneratedToken>, StampError> {
209 let opaque = fragment_of(OPAQUE_REACH, VIS_FRAGMENT);
210 let matcher = matched(pattern, &opaque)?;
211 let mut expansion = token::absolute_path(&["core", "compile_error"]);
212 expansion.push(GeneratedToken::alone('!'));
213 expansion.push(token::group(
214 GeneratedDelimiter::Parenthesis,
215 vec![GeneratedToken::text(OPAQUE_REACH_REFUSAL)],
216 )?);
217 rule(matcher, expansion)
218}
219
220fn matched(pattern: &Pattern, reach: &[GeneratedToken]) -> Result<Vec<GeneratedToken>, StampError> {
222 let mut tokens: Vec<GeneratedToken> = Vec::new();
223 for part in pattern.parts() {
224 let written = match part {
225 Part::Literal(material) => material.tokens().to_vec(),
226 Part::Seat(seat) => seat_matched(seat)?,
227 Part::Reach => reach.to_vec(),
228 };
229 tokens.extend(written);
230 }
231 Ok(tokens)
232}
233
234fn restated(pattern: &Pattern) -> Result<Vec<GeneratedToken>, StampError> {
238 let mut tokens: Vec<GeneratedToken> = Vec::new();
239 for part in pattern.parts() {
240 let written = match part {
241 Part::Literal(material) => material.tokens().to_vec(),
242 Part::Seat(seat) => forwarded(seat)?,
243 Part::Reach => Vec::new(),
244 };
245 tokens.extend(written);
246 }
247 Ok(tokens)
248}
249
250fn seat_matched(seat: &Seat) -> Result<Vec<GeneratedToken>, StampError> {
252 match seat.seating() {
253 Seating::One(fragment) => Ok(fragment_of(seat.name(), fragment.name())),
254 Seating::Many(fragment) => repeated(fragment_of(seat.name(), fragment.name()), Some(',')),
255 Seating::Attributes => {
256 let inner = token::group(
257 GeneratedDelimiter::Bracket,
258 fragment_of(seat.name(), Fragment::Attribute.name()),
259 )?;
260 repeated(vec![GeneratedToken::alone('#'), inner], None)
261 }
262 }
263}
264
265fn fragment_of(name: &str, kind: &str) -> Vec<GeneratedToken> {
267 let mut tokens = token::metavariable(name);
268 tokens.push(GeneratedToken::alone(':'));
269 tokens.push(GeneratedToken::word(kind));
270 tokens
271}
272
273fn bracketed_reach(mut reach: Vec<GeneratedToken>) -> Result<GeneratedToken, StampError> {
277 reach.push(GeneratedToken::alone(','));
278 Ok(token::group(GeneratedDelimiter::Bracket, reach)?)
279}
280
281fn repeated(
283 inner: Vec<GeneratedToken>,
284 separator: Option<char>,
285) -> Result<Vec<GeneratedToken>, StampError> {
286 let mut tokens = vec![
287 GeneratedToken::joint('$'),
288 token::group(GeneratedDelimiter::Parenthesis, inner)?,
289 ];
290 if let Some(mark) = separator {
291 tokens.push(GeneratedToken::alone(mark));
292 }
293 tokens.push(GeneratedToken::alone('*'));
294 Ok(tokens)
295}
296
297fn scoped(inside: Vec<GeneratedToken>) -> Result<Vec<GeneratedToken>, Overflow> {
299 Ok(vec![
300 GeneratedToken::word("pub"),
301 token::group(GeneratedDelimiter::Parenthesis, inside)?,
302 ])
303}
304
305fn rule(
307 matcher: Vec<GeneratedToken>,
308 expansion: Vec<GeneratedToken>,
309) -> Result<Vec<GeneratedToken>, StampError> {
310 Ok(vec![
311 token::group(GeneratedDelimiter::Parenthesis, matcher)?,
312 GeneratedToken::joint('='),
313 GeneratedToken::alone('>'),
314 token::group(GeneratedDelimiter::Brace, expansion)?,
315 GeneratedToken::alone(';'),
316 ])
317}
318
319fn stamp_path(site: &Site, name: &str) -> Vec<GeneratedToken> {
321 let mut tokens: Vec<GeneratedToken> = Vec::new();
322 for segment in site.root().segments() {
323 if !tokens.is_empty() {
324 tokens.push(GeneratedToken::joint(':'));
325 tokens.push(GeneratedToken::alone(':'));
326 }
327 tokens.push(GeneratedToken::word(segment.as_str()));
328 }
329 tokens.push(GeneratedToken::joint(':'));
330 tokens.push(GeneratedToken::alone(':'));
331 tokens.push(GeneratedToken::word(name));
332 tokens
333}