1use alloc::string::ToString;
9use alloc::vec::Vec;
10
11use crate::document::node::NodeValue;
12use crate::document::{EureDocument, NodeId};
13use crate::identifier::Identifier;
14use crate::path::{ArrayIndexKind, PathSegment};
15use crate::plan::{ArrayForm, Form, LayoutPlan, PlanError};
16use crate::source::{
17 BindSource, BindingSource, EureSource, SectionBody, SectionSource, SourceDocument, SourceId,
18 SourceKey, SourcePath, SourcePathSegment,
19};
20use crate::value::{ObjectKey, PartialObjectKey};
21
22pub fn emit(plan: LayoutPlan) -> SourceDocument {
27 let LayoutPlan {
30 doc,
31 forms,
32 array_forms,
33 order,
34 } = plan;
35 let sources = {
36 let mut ctx = EmitCtx {
37 doc: &doc,
38 forms: &forms,
39 array_forms: &array_forms,
40 order: &order,
41 sources: Vec::new(),
42 record_errors: None,
43 emitted: Vec::new(),
44 };
45 let root = build_root(&mut ctx);
46 debug_assert_eq!(root.0, 0);
47 ctx.sources
48 };
49 SourceDocument::new(doc, sources)
50}
51
52pub fn dry_walk_validate(plan: &LayoutPlan) -> Result<(), PlanError> {
53 let mut errors: Vec<PlanError> = Vec::new();
54 let mut ctx = EmitCtx {
55 doc: &plan.doc,
56 forms: &plan.forms,
57 array_forms: &plan.array_forms,
58 order: &plan.order,
59 sources: Vec::new(),
60 record_errors: Some(&mut errors),
61 emitted: Vec::new(),
62 };
63 build_root(&mut ctx);
64 if let Some(err) = errors.into_iter().next() {
66 return Err(err);
67 }
68 Ok(())
69}
70
71struct EmitCtx<'a> {
76 doc: &'a EureDocument,
77 forms: &'a crate::map::Map<NodeId, Form>,
78 array_forms: &'a crate::map::Map<NodeId, ArrayForm>,
79 order: &'a crate::map::Map<NodeId, Vec<NodeId>>,
80 sources: Vec<EureSource>,
81 record_errors: Option<&'a mut Vec<PlanError>>,
84 emitted: Vec<NodeId>,
85}
86
87impl EmitCtx<'_> {
88 fn is_dry(&self) -> bool {
89 self.record_errors.is_some()
90 }
91
92 fn push_error(&mut self, err: PlanError) {
93 if let Some(buf) = self.record_errors.as_deref_mut() {
94 buf.push(err);
95 }
96 }
97
98 fn record_emission(&mut self, id: NodeId) {
99 if self.emitted.contains(&id) {
100 self.push_error(PlanError::DuplicateEmission { node: id });
101 } else {
102 self.emitted.push(id);
103 }
104 }
105
106 fn reserve_source(&mut self) -> SourceId {
107 let id = SourceId(self.sources.len());
108 self.sources.push(EureSource::default());
109 id
110 }
111
112 fn set_source(&mut self, id: SourceId, src: EureSource) {
113 self.sources[id.0] = src;
114 }
115}
116
117fn build_root(ctx: &mut EmitCtx) -> SourceId {
122 let root_id = ctx.doc.get_root_id();
123 let id = ctx.reserve_source();
124 let root_node = ctx.doc.node(root_id);
125 let root_is_map = matches!(
126 root_node.content,
127 NodeValue::Map(_) | NodeValue::PartialMap(_)
128 );
129 let value = if root_is_map { None } else { Some(root_id) };
130 ctx.record_emission(root_id);
131
132 let mut eure = EureSource {
133 value,
134 ..Default::default()
135 };
136 emit_children(ctx, root_id, &[], &[], root_is_map, true, &mut eure);
137 ctx.set_source(id, eure);
138 id
139}
140
141#[allow(clippy::too_many_arguments)]
152fn emit_children(
153 ctx: &mut EmitCtx,
154 parent_id: NodeId,
155 node_path: &[PathSegment],
156 path_prefix: &[PathSegment],
157 emit_map_fields: bool,
158 allow_sections: bool,
159 dest: &mut EureSource,
160) {
161 let node = ctx.doc.node(parent_id);
162 let mut children: Vec<(PathSegment, NodeId)> = Vec::new();
163
164 for (ident, &cid) in node.extensions.iter() {
165 children.push((PathSegment::Extension(ident.clone()), cid));
166 }
167
168 if emit_map_fields {
169 match &node.content {
170 NodeValue::Map(map) => {
171 for (key, &cid) in map.iter() {
172 children.push((PathSegment::Value(key.clone()), cid));
173 }
174 }
175 NodeValue::PartialMap(pm) => {
176 for (key, &cid) in pm.iter() {
177 children.push((PathSegment::from_partial_object_key(key.clone()), cid));
178 }
179 }
180 _ => {}
181 }
182 }
183
184 let children = apply_order(ctx, parent_id, children);
186
187 for (seg, child_id) in children {
188 let child_node_path = concat_path(node_path, &seg);
189 let child_print_path = concat_path(path_prefix, &seg);
190 emit_child(
191 ctx,
192 child_id,
193 &seg,
194 &child_node_path,
195 &child_print_path,
196 allow_sections,
197 dest,
198 );
199 }
200}
201
202fn apply_order(
203 ctx: &EmitCtx,
204 parent_id: NodeId,
205 children: Vec<(PathSegment, NodeId)>,
206) -> Vec<(PathSegment, NodeId)> {
207 let (extensions, values): (Vec<_>, Vec<_>) = children
211 .into_iter()
212 .partition(|(seg, _)| matches!(seg, PathSegment::Extension(_)));
213 let Some(order) = ctx.order.get(&parent_id) else {
214 let mut out = extensions;
215 out.extend(values);
216 return out;
217 };
218 let mut by_id: Vec<(PathSegment, NodeId)> = values;
219 let mut ordered: Vec<(PathSegment, NodeId)> = extensions;
220 for id in order {
221 if let Some(pos) = by_id.iter().position(|(_, cid)| cid == id) {
222 ordered.push(by_id.remove(pos));
223 }
224 }
225 ordered.extend(by_id);
226 ordered
227}
228
229fn emit_child(
230 ctx: &mut EmitCtx,
231 child_id: NodeId,
232 _seg: &PathSegment,
233 child_node_path: &[PathSegment],
234 child_print_path: &[PathSegment],
235 allow_sections: bool,
236 dest: &mut EureSource,
237) {
238 let kind = ctx.doc.node(child_id).content.value_kind();
239 if matches!(kind, crate::value::ValueKind::Array) {
240 let Some(array_form) = ctx.array_forms.get(&child_id).copied() else {
241 ctx.push_error(PlanError::MissingForm(child_id));
242 return;
243 };
244 emit_array_child(
245 ctx,
246 child_id,
247 child_node_path,
248 child_print_path,
249 allow_sections,
250 array_form,
251 dest,
252 );
253 return;
254 }
255
256 let Some(form) = ctx.forms.get(&child_id).copied() else {
257 ctx.push_error(PlanError::MissingForm(child_id));
258 return;
259 };
260
261 emit_non_array_child(
262 ctx,
263 child_id,
264 child_node_path,
265 child_print_path,
266 allow_sections,
267 form,
268 dest,
269 );
270}
271
272fn emit_non_array_child(
273 ctx: &mut EmitCtx,
274 child_id: NodeId,
275 child_node_path: &[PathSegment],
276 child_print_path: &[PathSegment],
277 allow_sections: bool,
278 form: Form,
279 dest: &mut EureSource,
280) {
281 let is_section = matches!(
282 form,
283 Form::Section | Form::SectionBlock | Form::SectionValueBlock
284 );
285 if is_section && !allow_sections {
286 ctx.push_error(PlanError::SectionInForbiddenContext(child_id));
287 return;
288 }
289
290 match form {
291 Form::Inline => {
292 ctx.record_emission(child_id);
293 if !ctx.is_dry() {
294 dest.bindings.push(BindingSource {
295 trivia_before: Vec::new(),
296 path: to_source_path(child_print_path),
297 bind: BindSource::Value(child_id),
298 trailing_comment: None,
299 });
300 }
301 emit_extensions_only(
304 ctx,
305 child_id,
306 child_node_path,
307 child_print_path,
308 allow_sections,
309 dest,
310 );
311 }
312 Form::BindingBlock => {
313 ctx.record_emission(child_id);
314 let block_id = build_block(ctx, child_id, child_node_path, false);
315 if !ctx.is_dry() {
316 dest.bindings.push(BindingSource {
317 trivia_before: Vec::new(),
318 path: to_source_path(child_print_path),
319 bind: BindSource::Block(block_id),
320 trailing_comment: None,
321 });
322 }
323 }
324 Form::BindingValueBlock => {
325 ctx.record_emission(child_id);
326 let block_id = build_block(ctx, child_id, child_node_path, true);
327 if !ctx.is_dry() {
328 dest.bindings.push(BindingSource {
329 trivia_before: Vec::new(),
330 path: to_source_path(child_print_path),
331 bind: BindSource::Block(block_id),
332 trailing_comment: None,
333 });
334 }
335 }
336 Form::Section => {
337 ctx.record_emission(child_id);
338 let (value, bindings) = build_items(ctx, child_id, child_node_path, false);
339 if !ctx.is_dry() {
340 dest.sections.push(SectionSource {
341 trivia_before: Vec::new(),
342 path: to_source_path(child_print_path),
343 body: SectionBody::Items { value, bindings },
344 trailing_comment: None,
345 });
346 }
347 }
348 Form::SectionBlock => {
349 ctx.record_emission(child_id);
350 let block_id = build_block(ctx, child_id, child_node_path, false);
351 if !ctx.is_dry() {
352 dest.sections.push(SectionSource {
353 trivia_before: Vec::new(),
354 path: to_source_path(child_print_path),
355 body: SectionBody::Block(block_id),
356 trailing_comment: None,
357 });
358 }
359 }
360 Form::SectionValueBlock => {
361 ctx.record_emission(child_id);
362 let (value, bindings) = build_items(ctx, child_id, child_node_path, true);
363 if !ctx.is_dry() {
364 dest.sections.push(SectionSource {
365 trivia_before: Vec::new(),
366 path: to_source_path(child_print_path),
367 body: SectionBody::Items { value, bindings },
368 trailing_comment: None,
369 });
370 }
371 }
372 Form::Flatten => {
373 let is_map = matches!(
375 ctx.doc.node(child_id).content,
376 NodeValue::Map(_) | NodeValue::PartialMap(_)
377 );
378 emit_children(
379 ctx,
380 child_id,
381 child_node_path,
382 child_print_path,
383 is_map,
384 allow_sections,
385 dest,
386 );
387 }
388 }
389}
390
391fn emit_array_child(
392 ctx: &mut EmitCtx,
393 child_id: NodeId,
394 child_node_path: &[PathSegment],
395 child_print_path: &[PathSegment],
396 allow_sections: bool,
397 array_form: ArrayForm,
398 dest: &mut EureSource,
399) {
400 match array_form {
401 ArrayForm::Inline => {
402 ctx.record_emission(child_id);
403 if !ctx.is_dry() {
404 dest.bindings.push(BindingSource {
405 trivia_before: Vec::new(),
406 path: to_source_path(child_print_path),
407 bind: BindSource::Value(child_id),
408 trailing_comment: None,
409 });
410 }
411 emit_extensions_only(
414 ctx,
415 child_id,
416 child_node_path,
417 child_print_path,
418 allow_sections,
419 dest,
420 );
421 }
422 ArrayForm::PerElement(element_form) | ArrayForm::PerElementIndexed(element_form) => {
423 ctx.record_emission(child_id);
424 let NodeValue::Array(arr) = &ctx.doc.node(child_id).content else {
425 return;
426 };
427 let indexed = matches!(array_form, ArrayForm::PerElementIndexed(_));
428 let ids: Vec<NodeId> = arr.iter().copied().collect();
429 for (i, el_id) in ids.into_iter().enumerate() {
430 let mut element_print_path = child_print_path.to_vec();
431 element_print_path.push(PathSegment::ArrayIndex(if indexed {
432 ArrayIndexKind::Specific(i)
433 } else {
434 ArrayIndexKind::Push
435 }));
436 let mut element_node_path = child_node_path.to_vec();
437 element_node_path.push(PathSegment::ArrayIndex(ArrayIndexKind::Specific(i)));
438 emit_non_array_child(
439 ctx,
440 el_id,
441 &element_node_path,
442 &element_print_path,
443 allow_sections,
444 element_form,
445 dest,
446 );
447 }
448 }
449 }
450}
451
452fn emit_extensions_only(
453 ctx: &mut EmitCtx,
454 node_id: NodeId,
455 child_node_path: &[PathSegment],
456 child_print_path: &[PathSegment],
457 allow_sections: bool,
458 dest: &mut EureSource,
459) {
460 let node = ctx.doc.node(node_id);
461 let ext_children: Vec<(Identifier, NodeId)> = node
462 .extensions
463 .iter()
464 .map(|(k, &v)| (k.clone(), v))
465 .collect();
466 for (ident, ext_id) in ext_children {
467 let seg = PathSegment::Extension(ident);
468 let mut ext_node_path = child_node_path.to_vec();
469 ext_node_path.push(seg.clone());
470 let mut ext_print_path = child_print_path.to_vec();
471 ext_print_path.push(seg);
472 emit_child(
473 ctx,
474 ext_id,
475 ext_print_path.last().unwrap(),
476 &ext_node_path,
477 &ext_print_path,
478 allow_sections,
479 dest,
480 );
481 }
482}
483
484fn build_block(
485 ctx: &mut EmitCtx,
486 node_id: NodeId,
487 node_path: &[PathSegment],
488 with_value: bool,
489) -> SourceId {
490 let id = ctx.reserve_source();
491 let value = if with_value { Some(node_id) } else { None };
492 let node_is_map = matches!(
493 ctx.doc.node(node_id).content,
494 NodeValue::Map(_) | NodeValue::PartialMap(_)
495 );
496 let mut inner = EureSource {
497 value,
498 ..Default::default()
499 };
500 emit_children(ctx, node_id, node_path, &[], node_is_map, true, &mut inner);
501 ctx.set_source(id, inner);
502 id
503}
504
505fn build_items(
506 ctx: &mut EmitCtx,
507 node_id: NodeId,
508 node_path: &[PathSegment],
509 with_value: bool,
510) -> (Option<NodeId>, Vec<BindingSource>) {
511 let node_is_map = matches!(
514 ctx.doc.node(node_id).content,
515 NodeValue::Map(_) | NodeValue::PartialMap(_)
516 );
517 let mut inner = EureSource {
518 value: if with_value { Some(node_id) } else { None },
519 ..Default::default()
520 };
521 emit_children(ctx, node_id, node_path, &[], node_is_map, false, &mut inner);
522 let value = inner.value;
523 let bindings = inner.bindings;
524 debug_assert!(inner.sections.is_empty());
525 (value, bindings)
526}
527
528pub(crate) fn to_source_path(path: &[PathSegment]) -> SourcePath {
533 let mut out: Vec<SourcePathSegment> = Vec::new();
534 for seg in path {
535 match seg {
536 PathSegment::Ident(id) => out.push(SourcePathSegment::ident(id.clone())),
537 PathSegment::Extension(id) => out.push(SourcePathSegment::extension(id.clone())),
538 PathSegment::PartialValue(key) => out.push(SourcePathSegment {
539 key: partial_object_key_to_source_key(key),
540 array: None,
541 }),
542 PathSegment::HoleKey(label) => out.push(SourcePathSegment {
543 key: SourceKey::hole(label.clone()),
544 array: None,
545 }),
546 PathSegment::Value(key) => out.push(SourcePathSegment {
547 key: object_key_to_source_key(key),
548 array: None,
549 }),
550 PathSegment::TupleIndex(index) => out.push(SourcePathSegment {
551 key: SourceKey::TupleIndex(*index),
552 array: None,
553 }),
554 PathSegment::ArrayIndex(index) => {
555 if let Some(last) = out.last_mut() {
556 last.array = Some(*index);
557 }
558 }
559 }
560 }
561 out
562}
563
564fn object_key_to_source_key(key: &ObjectKey) -> SourceKey {
565 match key {
566 ObjectKey::String(s) => {
567 if let Ok(id) = s.parse::<Identifier>() {
568 SourceKey::Ident(id)
569 } else {
570 SourceKey::quoted(s.clone())
571 }
572 }
573 ObjectKey::Number(n) => {
574 if let Ok(n64) = i64::try_from(n) {
575 SourceKey::Integer(n64)
576 } else {
577 SourceKey::quoted(n.to_string())
578 }
579 }
580 ObjectKey::Tuple(keys) => {
581 SourceKey::Tuple(keys.iter().map(object_key_to_source_key).collect())
582 }
583 }
584}
585
586fn partial_object_key_to_source_key(key: &PartialObjectKey) -> SourceKey {
587 match key {
588 PartialObjectKey::String(s) => {
589 if let Ok(id) = s.parse::<Identifier>() {
590 SourceKey::Ident(id)
591 } else {
592 SourceKey::quoted(s.clone())
593 }
594 }
595 PartialObjectKey::Number(n) => {
596 if let Ok(n64) = i64::try_from(n) {
597 SourceKey::Integer(n64)
598 } else {
599 SourceKey::quoted(n.to_string())
600 }
601 }
602 PartialObjectKey::Hole(label) => SourceKey::hole(label.clone()),
603 PartialObjectKey::Tuple(keys) => {
604 SourceKey::Tuple(keys.iter().map(partial_object_key_to_source_key).collect())
605 }
606 }
607}
608
609fn concat_path(prefix: &[PathSegment], seg: &PathSegment) -> Vec<PathSegment> {
610 let mut out = Vec::with_capacity(prefix.len() + 1);
611 out.extend_from_slice(prefix);
612 out.push(seg.clone());
613 out
614}