1mod normalization;
2mod partition;
3mod reprojection;
4#[cfg(test)]
5pub(crate) mod test_data;
6#[cfg(test)]
7#[path = "emit_tests.rs"]
8mod tests;
9
10pub(crate) use normalization::NormalizedTable;
11pub(crate) use reprojection::reproject;
12pub(crate) use reprojection::reproject_with_span_identity;
13
14use crate::Array;
15use crate::Table;
16use crate::arena::Arena;
17use crate::item::{ArrayStyle, Item, Key, TableStyle, Value};
18use crate::span::Span;
19use std::io::Write;
20use std::mem::MaybeUninit;
21
22#[derive(Clone, Copy)]
25struct Prefix<'a, 'de> {
26 name: &'de str,
27 key_span: Span,
28 parent: Option<&'a Prefix<'a, 'de>>,
29}
30
31#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35pub enum Indent {
36 Spaces(u8),
38 Tab,
40}
41
42impl Default for Indent {
43 fn default() -> Self {
44 Indent::Spaces(4)
45 }
46}
47
48impl Indent {
49 fn write(&self, out: &mut Vec<u8>, depth: u32) {
50 match self {
51 Indent::Spaces(n) => {
52 let total = depth as usize * *n as usize;
53 for _ in 0..total {
54 out.push(b' ');
55 }
56 }
57 Indent::Tab => {
58 for _ in 0..depth {
59 out.push(b'\t');
60 }
61 }
62 }
63 }
64
65 fn width(&self) -> usize {
66 match self {
67 Indent::Spaces(n) => *n as usize,
68 Indent::Tab => 1,
69 }
70 }
71}
72
73#[derive(Default)]
80pub(crate) struct EmitConfig<'a> {
81 pub projected_source_text: &'a str,
82 pub projected_source_items: &'a [&'a Item<'a>],
83 pub indent: Indent,
84}
85
86struct Emitter<'a, 'b> {
87 arena: &'b Arena,
88 src: &'a [u8],
89 src_items: &'a [&'a Item<'a>],
90 indent: Indent,
91}
92
93fn trim_trailing_newline(buf: &mut Vec<u8>) {
94 if buf.last() == Some(&b'\n') {
95 buf.pop();
96 if buf.last() == Some(&b'\r') {
97 buf.pop();
98 }
99 }
100}
101
102pub(crate) fn emit_with_config(
108 table: &NormalizedTable<'_>,
109 config: &EmitConfig<'_>,
110 arena: &Arena,
111 buf: &mut Vec<u8>,
112) {
113 let table = table.table();
114 let emit = Emitter {
115 arena,
116 src: config.projected_source_text.as_bytes(),
117 src_items: config.projected_source_items,
118 indent: config.indent,
119 };
120
121 if !emit.src.is_empty() {
122 let mut cursor = 0usize;
123 emit_ordered(table, None, None, &emit, buf, &mut cursor);
124 emit_gap(&emit, cursor, emit.src.len(), buf);
125 if !emit.src.ends_with(b"\n") {
126 trim_trailing_newline(buf);
127 }
128 } else {
129 emit_formatted(table, None, &emit, buf);
130 }
131}
132
133enum EmitOp<'a, 'b, 'de> {
134 Body(&'a Key<'de>, &'a Item<'de>, Option<&'b Prefix<'b, 'de>>),
135 Header(&'a Table<'de>, &'a Item<'de>, &'b Prefix<'b, 'de>),
136 AotElement(&'a Table<'de>, &'a Item<'de>, &'b Prefix<'b, 'de>),
137}
138
139fn is_reordered_aot(op: &EmitOp<'_, '_, '_>) -> bool {
140 if let EmitOp::AotElement(_, entry, _) = op {
141 entry.meta.array_reordered()
142 } else {
143 false
144 }
145}
146
147fn segment_index(sort_pos: u32, index: usize, is_body: bool) -> u64 {
148 let sub_bit = if is_body { 0 } else { 1u64 << 63 };
149 sub_bit | ((sort_pos as u64) << 32) | (index as u64)
150}
151
152struct Segment<'a, 'b, 'de> {
154 sort_pos: u32,
156 source_start: u32,
158 is_body: bool,
161 op: EmitOp<'a, 'b, 'de>,
163}
164
165fn alloc_prefix<'b, 'de>(
166 arena: &'b Arena,
167 name: &'de str,
168 key_span: Span,
169 parent: Option<&'b Prefix<'b, 'de>>,
170) -> &'b Prefix<'b, 'de> {
171 unsafe {
178 let ptr = arena
179 .alloc(std::mem::size_of::<Prefix<'b, 'de>>())
180 .cast::<Prefix<'b, 'de>>()
181 .as_ptr();
182 std::ptr::write(
183 ptr,
184 Prefix {
185 name,
186 key_span,
187 parent,
188 },
189 );
190 &*ptr
191 }
192}
193
194fn build_segment_order(segments: &[Segment<'_, '_, '_>], ignore_source_order: bool) -> Vec<u64> {
195 let mut order = Vec::with_capacity(segments.len());
196 for (i, seg) in segments.iter().enumerate() {
197 order.push(segment_index(seg.sort_pos, i, seg.is_body));
198 }
199 if !ignore_source_order {
200 sort_index(&mut order);
201 }
202 order
203}
204
205fn emit_segment_prefix(
206 seg: &Segment<'_, '_, '_>,
207 emit: &Emitter<'_, '_>,
208 out: &mut Vec<u8>,
209 cursor: &mut usize,
210) {
211 let ss = seg.source_start;
212 let is_header = matches!(seg.op, EmitOp::Header(..) | EmitOp::AotElement(..));
213
214 if ss != u32::MAX {
215 let target = ss as usize;
216 if target >= *cursor {
217 let pre_len = out.len();
218 emit_gap(emit, *cursor, target, out);
219 if out.len() == pre_len && is_header && !out.is_empty() && out.last() != Some(&b'\n') {
220 out.push(b'\n');
221 }
222 *cursor = target;
223 } else if is_header && !is_reordered_aot(&seg.op) {
224 out.push(b'\n');
225 }
226 } else if is_header && !is_reordered_aot(&seg.op) {
227 out.push(b'\n');
228 }
229}
230
231fn emit_ordered<'a, 'b, 'de: 'b>(
234 table: &'a Table<'de>,
235 dotted_prefix: Option<&'b Prefix<'b, 'de>>,
236 section_prefix: Option<&'b Prefix<'b, 'de>>,
237 emit: &Emitter<'b, 'de>,
238 out: &mut Vec<u8>,
239 cursor: &mut usize,
240) {
241 let mut segments: Vec<Segment<'a, 'b, 'de>> = Vec::new();
242 let mut last_projected: u32 = 0;
243
244 collect_segments(
245 table,
246 dotted_prefix,
247 section_prefix,
248 emit,
249 &mut segments,
250 &mut last_projected,
251 0, );
253
254 let order = build_segment_order(&segments, table.meta.ignore_source_order());
255
256 for &entry in &order {
258 let seg = &segments[(entry & 0xFFFF_FFFF) as usize];
259 emit_segment_prefix(seg, emit, out, cursor);
260
261 match &seg.op {
262 EmitOp::Body(key, item, dotted) => {
263 emit_body_entry(key, item, *dotted, emit, out, cursor);
264 }
265 EmitOp::Header(sub_table, item, node) => {
266 emit_header_body(sub_table, item, node, emit, out, cursor);
267 }
268 EmitOp::AotElement(sub_table, arr_entry, node) => {
269 let pre_cursor = *cursor;
270 emit_aot_element(sub_table, arr_entry, node, emit, out, cursor);
271 *cursor = (*cursor).max(pre_cursor);
275 }
276 }
277 }
278}
279
280fn collect_segments<'a, 'b, 'de: 'b>(
289 table: &'a Table<'de>,
290 dotted_prefix: Option<&'b Prefix<'b, 'de>>,
291 section_prefix: Option<&'b Prefix<'b, 'de>>,
292 emit: &Emitter<'b, 'de>,
293 segments: &mut Vec<Segment<'a, 'b, 'de>>,
294 last_projected: &mut u32,
295 mode: u8,
296) {
297 const BODY_ONLY: u8 = 1;
298 const SUBS_ONLY: u8 = 2;
299 for entry in table.entries() {
302 let key = &entry.0;
303 let item = &entry.1;
304
305 if item.has_dotted_bit() {
306 let Some(sub_table) = item.as_table() else {
307 continue;
308 };
309 let dotted_node = alloc_prefix(emit.arena, key.name, key.span, dotted_prefix);
310 let sec_node = alloc_prefix(emit.arena, key.name, key.span, section_prefix);
311 if !key.span.is_empty() {
313 *last_projected = key.span.start;
314 }
315 collect_segments(
316 sub_table,
317 Some(dotted_node),
318 Some(sec_node),
319 emit,
320 segments,
321 last_projected,
322 mode,
323 );
324 continue;
325 }
326
327 if item.is_implicit_table() {
328 let Some(sub_table) = item.as_table() else {
329 continue;
330 };
331 if !key.span.is_empty() {
333 *last_projected = key.span.start;
334 }
335 let sec_node = alloc_prefix(emit.arena, key.name, key.span, section_prefix);
336 collect_segments(
337 sub_table,
338 None,
339 Some(sec_node),
340 emit,
341 segments,
342 last_projected,
343 mode,
344 );
345 continue;
346 }
347
348 if item.has_header_bit() {
349 if mode == BODY_ONLY {
350 continue;
351 }
352 let Some(sub_table) = item.as_table() else {
353 continue;
354 };
355 let node = alloc_prefix(emit.arena, key.name, key.span, section_prefix);
356 let sort_key_opt = projected_span(item, emit).map(|s| s.start);
357 let source_start = sort_key_opt.unwrap_or(u32::MAX);
358
359 let sp = pack_sort_pos(sort_key_opt, last_projected);
360 segments.push(Segment {
361 sort_pos: sp,
362 source_start,
363 is_body: false,
364 op: EmitOp::Header(sub_table, item, node),
365 });
366
367 collect_segments(
368 sub_table,
369 None,
370 Some(node),
371 emit,
372 segments,
373 last_projected,
374 SUBS_ONLY,
375 );
376 continue;
377 }
378
379 if item.is_aot() {
380 if mode == BODY_ONLY {
381 continue;
382 }
383 let Some(arr) = item.as_array() else {
384 continue;
385 };
386 let node = alloc_prefix(emit.arena, key.name, key.span, section_prefix);
387 if !key.span.is_empty() {
389 *last_projected = key.span.start;
390 }
391 let mut prev_aot_pos: u32 = 0;
392 for arr_entry in arr {
393 let Some(sub_table) = arr_entry.as_table() else {
394 continue;
395 };
396 let elem_sort = if arr_entry.meta.array_reordered() {
397 None
398 } else {
399 projected_span(arr_entry, emit).map(|s| s.start)
400 };
401 let elem_source_start = elem_sort.unwrap_or(u32::MAX);
402 let mut sp = pack_sort_pos(elem_sort, last_projected);
403 if sp < prev_aot_pos {
407 sp = prev_aot_pos;
408 }
409 prev_aot_pos = sp;
410
411 segments.push(Segment {
412 sort_pos: sp,
413 source_start: elem_source_start,
414 is_body: false,
415 op: EmitOp::AotElement(sub_table, arr_entry, node),
416 });
417 }
418 continue;
419 }
420
421 if mode == SUBS_ONLY {
423 continue;
424 }
425 let sort_key_opt = if !key.span.is_empty() {
428 Some(key.span.start)
429 } else {
430 None
431 };
432 let ls = if let Some(pos) = sort_key_opt {
433 line_start_of(emit.src, pos as usize) as u32
434 } else {
435 u32::MAX
436 };
437 let sp = pack_sort_pos(sort_key_opt, last_projected);
438
439 segments.push(Segment {
440 sort_pos: sp,
441 source_start: ls,
442 is_body: true,
443 op: EmitOp::Body(key, item, dotted_prefix),
444 });
445 }
446}
447
448fn emit_header_body<'a, 'b, 'de: 'b>(
452 table: &'a Table<'de>,
453 item: &'a Item<'de>,
454 prefix: &'b Prefix<'b, 'de>,
455 emit: &Emitter<'b, 'de>,
456 out: &mut Vec<u8>,
457 cursor: &mut usize,
458) {
459 if emit_projected_header_line(item, false, emit, out, cursor) {
460 emit_body_ordered(table, emit, out, cursor);
461 return;
462 }
463
464 write_section_header(prefix, emit, out);
466 emit_body_ordered(table, emit, out, cursor);
467}
468
469fn emit_body_ordered<'a, 'b, 'de: 'b>(
473 table: &'a Table<'de>,
474 emit: &Emitter<'b, 'de>,
475 out: &mut Vec<u8>,
476 cursor: &mut usize,
477) {
478 let mut segments: Vec<Segment<'a, 'b, 'de>> = Vec::new();
479 let mut last_projected: u32 = 0;
480
481 collect_segments(
482 table,
483 None,
484 None,
485 emit,
486 &mut segments,
487 &mut last_projected,
488 1, );
490
491 let order = build_segment_order(&segments, table.meta.ignore_source_order());
492
493 for &entry in &order {
494 let seg = &segments[(entry & 0xFFFF_FFFF) as usize];
495 let ss = seg.source_start;
496 if ss != u32::MAX {
497 let target = ss as usize;
498 if target >= *cursor {
499 emit_gap(emit, *cursor, target, out);
500 *cursor = target;
501 }
502 }
503 if let EmitOp::Body(key, item, dotted) = &seg.op {
504 emit_body_entry(key, item, *dotted, emit, out, cursor);
505 }
506 }
507}
508
509fn emit_projected_header_line(
510 item: &Item<'_>,
511 include_comment_prefix: bool,
512 emit: &Emitter<'_, '_>,
513 out: &mut Vec<u8>,
514 cursor: &mut usize,
515) -> bool {
516 let Some(src_span) = projected_span(item, emit) else {
517 return false;
518 };
519 let hdr_start = src_span.start as usize;
520 if hdr_start >= emit.src.len() || emit.src[hdr_start] != b'[' {
521 return false;
522 }
523 if include_comment_prefix {
524 let (pstart, pend) = find_comment_prefix(emit.src, hdr_start);
525 if pstart != pend {
526 out.extend_from_slice(&emit.src[pstart..pend]);
527 }
528 }
529 let hdr_line_end = line_end_of(emit.src, hdr_start);
530 let hdr_slice = &emit.src[hdr_start..hdr_line_end];
531 out.extend_from_slice(hdr_slice);
532 if !hdr_slice.ends_with(b"\n") {
533 out.push(b'\n');
534 }
535 *cursor = hdr_line_end;
536 true
537}
538
539fn emit_aot_element<'a, 'b, 'de: 'b>(
541 sub_table: &'a Table<'de>,
542 entry: &'a Item<'de>,
543 prefix: &'b Prefix<'b, 'de>,
544 emit: &Emitter<'b, 'de>,
545 out: &mut Vec<u8>,
546 cursor: &mut usize,
547) {
548 if emit_projected_header_line(entry, entry.meta.array_reordered(), emit, out, cursor) {
549 emit_ordered(sub_table, None, Some(prefix), emit, out, cursor);
550 return;
551 }
552
553 write_aot_header(prefix, emit, out);
555 emit_ordered(sub_table, None, Some(prefix), emit, out, cursor);
556}
557
558fn pack_sort_pos(source_pos: Option<u32>, last_projected: &mut u32) -> u32 {
563 if let Some(pos) = source_pos {
564 *last_projected = pos;
565 pos
566 } else {
567 *last_projected
568 }
569}
570
571fn emit_body_entry(
574 key: &Key<'_>,
575 item: &Item<'_>,
576 dotted_prefix: Option<&Prefix<'_, '_>>,
577 emit: &Emitter<'_, '_>,
578 out: &mut Vec<u8>,
579 cursor: &mut usize,
580) {
581 if !key.span.is_empty() && projected_span(item, emit).is_some() {
583 let key_start = key.span.start as usize;
584 let line_start = line_start_of(emit.src, key_start);
585 let ahead = line_start >= *cursor;
586 if ahead {
587 emit_gap(emit, *cursor, line_start, out);
588 }
589 let mut ws_len = 0;
595 for &b in &emit.src[line_start..] {
596 if b != b' ' && b != b'\t' {
597 break;
598 }
599 ws_len += 1;
600 }
601 if ws_len > 0 {
602 out.extend_from_slice(&emit.src[line_start..line_start + ws_len]);
603 }
604 if let Some(line_end) = try_emit_entry_from_source(key, item, dotted_prefix, emit, out) {
605 *cursor = if ahead {
606 line_end
607 } else {
608 (*cursor).max(line_end)
609 };
610 return;
611 }
612 }
614
615 write_dotted_key(dotted_prefix, key, emit, out);
617 out.extend_from_slice(b" = ");
618 format_value(item, emit, out);
619 out.push(b'\n');
620}
621
622fn emit_formatted(
629 table: &Table<'_>,
630 section_prefix: Option<&Prefix<'_, '_>>,
631 emit: &Emitter<'_, '_>,
632 out: &mut Vec<u8>,
633) {
634 emit_formatted_body(table, None, emit, out);
635 emit_formatted_subsections(table, section_prefix, emit, out);
636}
637
638fn emit_formatted_body(
641 table: &Table<'_>,
642 dotted_prefix: Option<&Prefix<'_, '_>>,
643 emit: &Emitter<'_, '_>,
644 out: &mut Vec<u8>,
645) {
646 for (key, item) in table {
647 if item.has_dotted_bit() {
648 let Some(sub_table) = item.as_table() else {
649 continue;
650 };
651 let node = Prefix {
652 name: key.name,
653 key_span: key.span,
654 parent: dotted_prefix,
655 };
656 emit_formatted_body(sub_table, Some(&node), emit, out);
657 continue;
658 }
659 if item.has_header_bit() || item.is_implicit_table() || item.is_aot() {
660 continue;
661 }
662
663 write_dotted_key(dotted_prefix, key, emit, out);
664 out.extend_from_slice(b" = ");
665 format_value(item, emit, out);
666 out.push(b'\n');
667 }
668}
669
670fn emit_formatted_subsections(
672 table: &Table<'_>,
673 prefix: Option<&Prefix<'_, '_>>,
674 emit: &Emitter<'_, '_>,
675 out: &mut Vec<u8>,
676) {
677 for (key, item) in table {
678 let node = Prefix {
679 name: key.name,
680 key_span: key.span,
681 parent: prefix,
682 };
683 if item.has_header_bit() {
684 let Some(sub_table) = item.as_table() else {
685 continue;
686 };
687 if !out.is_empty() {
688 out.push(b'\n');
689 }
690 write_section_header(&node, emit, out);
691 emit_formatted(sub_table, Some(&node), emit, out);
692 } else if item.is_implicit_table() || item.has_dotted_bit() {
693 let Some(sub_table) = item.as_table() else {
694 continue;
695 };
696 emit_formatted_subsections(sub_table, Some(&node), emit, out);
697 } else if item.is_aot() {
698 let Some(arr) = item.as_array() else {
699 continue;
700 };
701 for entry in arr {
702 let Some(sub_table) = entry.as_table() else {
703 continue;
704 };
705 if !out.is_empty() {
706 out.push(b'\n');
707 }
708 write_aot_header(&node, emit, out);
709 emit_formatted(sub_table, Some(&node), emit, out);
710 }
711 }
712 }
713}
714
715fn write_section_header(prefix: &Prefix<'_, '_>, emit: &Emitter<'_, '_>, out: &mut Vec<u8>) {
716 out.push(b'[');
717 write_prefix_path(prefix, emit, out);
718 out.extend_from_slice(b"]\n");
719}
720
721fn write_aot_header(prefix: &Prefix<'_, '_>, emit: &Emitter<'_, '_>, out: &mut Vec<u8>) {
722 out.extend_from_slice(b"[[");
723 write_prefix_path(prefix, emit, out);
724 out.extend_from_slice(b"]]\n");
725}
726
727fn write_prefix_path(node: &Prefix<'_, '_>, emit: &Emitter<'_, '_>, out: &mut Vec<u8>) {
728 if let Some(parent) = node.parent {
729 write_prefix_path(parent, emit, out);
730 out.push(b'.');
731 }
732 emit_key(node.name, node.key_span, emit, out);
733}
734
735fn write_dotted_key(
737 prefix: Option<&Prefix<'_, '_>>,
738 key: &Key<'_>,
739 emit: &Emitter<'_, '_>,
740 out: &mut Vec<u8>,
741) {
742 if let Some(node) = prefix {
743 write_prefix_path(node, emit, out);
744 out.push(b'.');
745 }
746 emit_key(key.name, key.span, emit, out);
747}
748
749fn line_start_of(src: &[u8], pos: usize) -> usize {
751 let mut i = pos;
752 while i > 0 && src[i - 1] != b'\n' {
753 i -= 1;
754 }
755 i
756}
757
758fn find_comment_prefix(src: &[u8], header_pos: usize) -> (usize, usize) {
763 let header_line_start = line_start_of(src, header_pos);
764 let mut prefix_start = header_line_start;
765 let mut cursor = header_line_start;
766 while cursor > 0 {
767 let prev_line_start = line_start_of(src, cursor - 1);
768 let line = &src[prev_line_start..cursor];
769 let mut first_non_ws = None;
770 for &b in line {
771 if b != b' ' && b != b'\t' {
772 first_non_ws = Some(b);
773 break;
774 }
775 }
776 match first_non_ws {
777 Some(b'#') | Some(b'\n') | Some(b'\r') | None => {
778 prefix_start = prev_line_start;
779 cursor = prev_line_start;
780 }
781 _ => break,
782 }
783 }
784 (prefix_start, header_line_start)
785}
786
787fn contains_byte(slice: &[u8], needle: u8) -> bool {
788 for &b in slice {
789 if b == needle {
790 return true;
791 }
792 }
793 false
794}
795
796fn is_all_whitespace(slice: &[u8]) -> bool {
797 for &b in slice {
798 if b != b' ' && b != b'\t' {
799 return false;
800 }
801 }
802 true
803}
804
805fn line_end_of(src: &[u8], pos: usize) -> usize {
808 let mut i = pos;
809 while i < src.len() && src[i] != b'\n' {
810 i += 1;
811 }
812 if i < src.len() {
813 i + 1 } else {
815 i
816 }
817}
818
819fn emit_gap(emit: &Emitter<'_, '_>, start: usize, end: usize, out: &mut Vec<u8>) {
825 let mut i = start;
826 while i < end {
827 let line_end = line_end_of(emit.src, i).min(end);
828 let mut first_non_ws = None;
830 for &b in &emit.src[i..line_end] {
831 if b != b' ' && b != b'\t' {
832 first_non_ws = Some(b);
833 break;
834 }
835 }
836 match first_non_ws {
837 Some(b'#') | Some(b'\n') | Some(b'\r') | None => {
839 out.extend_from_slice(&emit.src[i..line_end]);
840 }
841 _ => {}
843 }
844 i = line_end;
845 }
846}
847
848fn has_trailing_comma(trailing: &[u8]) -> bool {
851 for &b in trailing {
852 if b == b'#' {
853 return false;
854 }
855 if b == b',' {
856 return true;
857 }
858 }
859 false
860}
861
862fn projected_span(item: &Item<'_>, emit: &Emitter<'_, '_>) -> Option<Span> {
864 let span = projected_source(item, emit)?.span_unchecked();
865 if span.is_empty() {
866 return None;
867 }
868 Some(span)
869}
870
871fn projected_source<'a>(item: &Item<'_>, emit: &Emitter<'a, '_>) -> Option<&'a Item<'a>> {
873 if emit.src.is_empty() {
874 return None;
875 }
876 item.projected(emit.src_items)
877}
878
879fn try_emit_entry_from_source(
886 key: &Key<'_>,
887 item: &Item<'_>,
888 dotted_prefix: Option<&Prefix<'_, '_>>,
889 emit: &Emitter<'_, '_>,
890 out: &mut Vec<u8>,
891) -> Option<usize> {
892 if emit.src.is_empty() || key.span.is_empty() {
893 return None;
894 }
895 let val_span = projected_span(item, emit)?;
896
897 let key_end = key.span.end as usize;
898 let val_start = val_span.start as usize;
899 let val_end = val_span.end as usize;
900 if key_end > val_start || val_end > emit.src.len() {
901 return None;
902 }
903
904 write_dotted_key(dotted_prefix, key, emit, out);
906 out.extend_from_slice(&emit.src[key_end..val_start]);
908 format_value(item, emit, out);
910 let line_end = line_end_of(emit.src, val_end);
912 let trailing = &emit.src[val_end..line_end];
913 out.extend_from_slice(trailing);
914 if !trailing.ends_with(b"\n") {
917 out.push(b'\n');
918 }
919 Some(line_end)
920}
921
922fn emit_preserved_with_comma(
932 emit: &Emitter<'_, '_>,
933 from: usize,
934 val_end: usize,
935 out: &mut Vec<u8>,
936) {
937 out.extend_from_slice(&emit.src[from..val_end]);
938 let le = line_end_of(emit.src, val_end);
939 let trailing = &emit.src[val_end..le];
940 if !has_trailing_comma(trailing) {
941 out.push(b',');
942 }
943 out.extend_from_slice(trailing);
944}
945
946fn try_emit_array_partial(
947 dest: &Array<'_>,
948 arr_span: Span,
949 emit: &Emitter<'_, '_>,
950 out: &mut Vec<u8>,
951) -> bool {
952 let arr_start = arr_span.start as usize;
953 let arr_end = arr_span.end as usize;
954 if arr_end == 0 || arr_end > emit.src.len() || emit.src[arr_end - 1] != b']' {
955 return false;
956 }
957
958 if !contains_byte(&emit.src[arr_start..arr_end], b'\n') {
960 return false;
961 }
962
963 let dest_slice = dest.as_slice();
964 if dest_slice.is_empty() {
965 return false;
966 }
967
968 let indent = 'indent: {
970 for elem in dest_slice {
971 if let Some(sp) = projected_span(elem, emit) {
972 let elem_start = sp.start as usize;
973 let candidate = &emit.src[line_start_of(emit.src, elem_start)..elem_start];
974 if !is_all_whitespace(candidate) {
977 return false;
978 }
979 break 'indent candidate;
980 }
981 }
982 return false; };
984
985 out.extend_from_slice(&emit.src[arr_start..line_end_of(emit.src, arr_start)]);
987
988 for elem in dest_slice {
989 if is_fully_projected(elem, emit) {
990 let val_span = projected_span(elem, emit).unwrap();
991 emit_preserved_with_comma(
992 emit,
993 line_start_of(emit.src, val_span.start as usize),
994 val_span.end as usize,
995 out,
996 );
997 } else {
998 out.extend_from_slice(indent);
999 let depth = (indent.len() / emit.indent.width()) as u32;
1000 format_value_at(elem, emit, out, depth, true);
1001 out.extend_from_slice(b",\n");
1002 }
1003 }
1004
1005 let bracket = arr_end - 1;
1007 out.extend_from_slice(&emit.src[line_start_of(emit.src, bracket)..arr_end]);
1008 true
1009}
1010
1011fn try_emit_inline_table_partial(
1018 dest: &Table<'_>,
1019 tab_span: Span,
1020 emit: &Emitter<'_, '_>,
1021 out: &mut Vec<u8>,
1022) -> bool {
1023 let tab_start = tab_span.start as usize;
1024 let tab_end = tab_span.end as usize;
1025 if tab_end == 0 || tab_end > emit.src.len() || emit.src[tab_end - 1] != b'}' {
1026 return false;
1027 }
1028
1029 if !contains_byte(&emit.src[tab_start..tab_end], b'\n') {
1031 return false;
1032 }
1033
1034 let entries = dest.entries();
1035 if entries.is_empty() {
1036 return false;
1037 }
1038
1039 let indent = 'indent: {
1041 for (key, val) in entries {
1042 if !key.span.is_empty() && projected_span(val, emit).is_some() {
1043 let k = key.span.start as usize;
1044 let candidate = &emit.src[line_start_of(emit.src, k)..k];
1045 if !is_all_whitespace(candidate) {
1048 return false;
1049 }
1050 break 'indent candidate;
1051 }
1052 }
1053 return false; };
1055
1056 let (leaves, order) = collect_and_sort_leaves(dest, emit.arena);
1060
1061 out.extend_from_slice(&emit.src[tab_start..line_end_of(emit.src, tab_start)]);
1063
1064 for &entry in &order {
1065 let leaf = &leaves[(entry & 0xFFFF_FFFF) as usize];
1066 let key = leaf.key;
1067 let val = leaf.item;
1068 if !key.span.is_empty() && is_fully_projected(val, emit) {
1069 let val_span = projected_span(val, emit).unwrap();
1070 emit_preserved_with_comma(
1071 emit,
1072 line_start_of(emit.src, key.span.start as usize),
1073 val_span.end as usize,
1074 out,
1075 );
1076 } else {
1077 out.extend_from_slice(indent);
1078 let depth = (indent.len() / emit.indent.width()) as u32;
1079 write_inline_leaf_key(leaf, emit, out);
1080 out.extend_from_slice(b" = ");
1081 format_value_at(val, emit, out, depth, true);
1082 out.extend_from_slice(b",\n");
1083 }
1084 }
1085
1086 let brace = tab_end - 1;
1088 out.extend_from_slice(&emit.src[line_start_of(emit.src, brace)..tab_end]);
1089 true
1090}
1091
1092fn emit_key(name: &str, span: Span, emit: &Emitter<'_, '_>, out: &mut Vec<u8>) {
1094 if !span.is_empty() && !emit.src.is_empty() {
1095 out.extend_from_slice(&emit.src[span.range()]);
1096 } else {
1097 format_key(name, out);
1098 }
1099}
1100
1101fn projected_text<'a>(item: &Item<'_>, emit: &Emitter<'a, '_>) -> Option<&'a [u8]> {
1104 if emit.src.is_empty() {
1105 return None;
1106 }
1107 let src_item = item.projected(emit.src_items)?;
1108 let span = src_item.span_unchecked();
1109 if span.is_empty() {
1110 return None;
1111 }
1112 Some(&emit.src[span.range()])
1113}
1114
1115fn is_fully_projected(item: &Item<'_>, emit: &Emitter<'_, '_>) -> bool {
1119 if emit.src_items.is_empty() {
1120 return false;
1121 }
1122 item.is_reprojected_full_match()
1123}
1124
1125fn format_value(item: &Item<'_>, emit: &Emitter<'_, '_>, out: &mut Vec<u8>) {
1126 format_value_at(item, emit, out, 0, false);
1127}
1128
1129fn format_value_at(
1130 item: &Item<'_>,
1131 emit: &Emitter<'_, '_>,
1132 out: &mut Vec<u8>,
1133 depth: u32,
1134 inline: bool,
1135) {
1136 match item.value() {
1137 Value::Array(arr) => {
1138 if let Some(src_item) = projected_source(item, emit) {
1139 if let Some(src_arr) = src_item.as_array() {
1140 if src_arr.style() == ArrayStyle::Inline {
1143 let span = src_item.span_unchecked();
1144 if is_fully_projected(item, emit) {
1145 out.extend_from_slice(&emit.src[span.range()]);
1146 return;
1147 }
1148 if try_emit_array_partial(arr, span, emit, out) {
1150 return;
1151 }
1152 }
1153 }
1154 }
1155 if arr.is_expanded() {
1156 format_expanded_array(arr, emit, out, depth);
1157 } else {
1158 format_array(arr, emit, out);
1159 }
1160 }
1161 Value::Table(tab) => {
1162 if let Some(src_item) = projected_source(item, emit) {
1163 if let Some(src_tab) = src_item.as_table() {
1164 if src_tab.style() == TableStyle::Inline {
1167 let span = src_item.span_unchecked();
1168 if is_fully_projected(item, emit) {
1169 out.extend_from_slice(&emit.src[span.range()]);
1170 return;
1171 }
1172 if try_emit_inline_table_partial(tab, span, emit, out) {
1174 return;
1175 }
1176 }
1177 }
1178 }
1179 format_inline_table(tab, emit, out, depth);
1180 }
1181 _ => {
1182 if let Some(text) = projected_text(item, emit) {
1183 out.extend_from_slice(text);
1184 return;
1185 }
1186 format_scalar(item, inline, out);
1187 }
1188 }
1189}
1190
1191fn format_scalar(item: &Item<'_>, inline: bool, out: &mut Vec<u8>) {
1192 match item.value() {
1193 Value::String(s) => format_string(s, inline, out),
1194 Value::Integer(i) => {
1195 let _ = write!(out, "{i}");
1196 }
1197 Value::Float(f) => format_float(*f, out),
1198 Value::Boolean(b) => out.extend_from_slice(if *b { b"true" } else { b"false" }),
1199 Value::DateTime(dt) => {
1200 let mut buf = MaybeUninit::uninit();
1201 out.extend_from_slice(dt.format(&mut buf).as_bytes());
1202 }
1203 _ => {}
1204 }
1205}
1206
1207#[derive(Clone, Copy)]
1209struct InlineLeaf<'a, 'de> {
1210 key: &'a Key<'de>,
1211 item: &'a Item<'de>,
1212 prefix: &'a [(&'de str, Span)],
1214 sort_pos: u32,
1216}
1217
1218fn arena_extend_prefix<'a, 'de>(
1220 arena: &'a Arena,
1221 prefix: &[(&'de str, Span)],
1222 name: &'de str,
1223 span: Span,
1224) -> &'a [(&'de str, Span)] {
1225 let new_len = prefix.len() + 1;
1226 let byte_size = new_len * std::mem::size_of::<(&str, Span)>();
1227 let ptr = arena.alloc(byte_size);
1228 let slice_ptr = ptr.as_ptr() as *mut (&'de str, Span);
1229 unsafe {
1237 std::ptr::copy_nonoverlapping(prefix.as_ptr(), slice_ptr, prefix.len());
1238 std::ptr::write(slice_ptr.add(prefix.len()), (name, span));
1239 std::slice::from_raw_parts(slice_ptr as *const (&'de str, Span), new_len)
1240 }
1241}
1242
1243fn collect_inline_leaves<'a, 'de>(
1245 table: &'a Table<'de>,
1246 prefix_chain: &'a [(&'de str, Span)],
1247 leaves: &mut Vec<InlineLeaf<'a, 'de>>,
1248 last_pos: &mut u32,
1249 arena: &'a Arena,
1250) {
1251 for (key, val) in table {
1252 if let Some(sub) = val.as_table() {
1253 if val.has_dotted_bit() || sub.style() == TableStyle::Dotted {
1254 let chain = arena_extend_prefix(arena, prefix_chain, key.name, key.span);
1255 if !key.span.is_empty() {
1256 *last_pos = key.span.start;
1257 }
1258 collect_inline_leaves(sub, chain, leaves, last_pos, arena);
1259 continue;
1260 }
1261 }
1262 if !key.span.is_empty() {
1263 *last_pos = key.span.start;
1264 }
1265 leaves.push(InlineLeaf {
1266 key,
1267 item: val,
1268 prefix: prefix_chain,
1269 sort_pos: *last_pos,
1270 });
1271 }
1272}
1273
1274fn write_inline_leaf_key(leaf: &InlineLeaf<'_, '_>, emit: &Emitter<'_, '_>, out: &mut Vec<u8>) {
1276 let mut first = true;
1277 for &(name, span) in leaf.prefix {
1278 if !first {
1279 out.push(b'.');
1280 }
1281 first = false;
1282 emit_key(name, span, emit, out);
1283 }
1284 if !leaf.prefix.is_empty() {
1285 out.push(b'.');
1286 }
1287 emit_key(leaf.key.name, leaf.key.span, emit, out);
1288}
1289
1290fn sort_index(order: &mut [u64]) {
1294 order.sort_unstable();
1295}
1296
1297fn collect_and_sort_leaves<'a, 'de>(
1300 table: &'a Table<'de>,
1301 arena: &'a Arena,
1302) -> (Vec<InlineLeaf<'a, 'de>>, Vec<u64>) {
1303 let mut leaves = Vec::new();
1304 let mut last_pos = 0u32;
1305 collect_inline_leaves(table, &[], &mut leaves, &mut last_pos, arena);
1306 let mut order: Vec<u64> = Vec::with_capacity(leaves.len());
1307 let mut i = 0u64;
1308 for leaf in &leaves {
1309 order.push(((leaf.sort_pos as u64) << 32) | i);
1310 i += 1;
1311 }
1312 sort_index(&mut order);
1313 (leaves, order)
1314}
1315
1316fn format_inline_table(tab: &Table<'_>, emit: &Emitter<'_, '_>, out: &mut Vec<u8>, depth: u32) {
1317 if tab.is_empty() {
1318 out.extend_from_slice(b"{}");
1319 return;
1320 }
1321
1322 if !emit.src_items.is_empty() && !tab.meta.ignore_source_order() {
1323 let mut needs_sort = false;
1324 for (k, v) in tab.entries() {
1325 if v.has_dotted_bit() || !k.span.is_empty() {
1326 needs_sort = true;
1327 break;
1328 }
1329 if let Some(t) = v.as_table() {
1330 if t.style() == TableStyle::Dotted {
1331 needs_sort = true;
1332 break;
1333 }
1334 }
1335 }
1336 if needs_sort {
1337 let (leaves, order) = collect_and_sort_leaves(tab, emit.arena);
1338 out.extend_from_slice(b"{ ");
1339 let mut first = true;
1340 for &entry in &order {
1341 let leaf = &leaves[(entry & 0xFFFF_FFFF) as usize];
1342 if !first {
1343 out.extend_from_slice(b", ");
1344 }
1345 first = false;
1346 write_inline_leaf_key(leaf, emit, out);
1347 out.extend_from_slice(b" = ");
1348 format_value_at(leaf.item, emit, out, depth, true);
1349 }
1350 out.extend_from_slice(b" }");
1351 return;
1352 }
1353 }
1354
1355 out.extend_from_slice(b"{ ");
1356 let mut first = true;
1357 for (key, val) in tab {
1358 if let Some(sub) = val.as_table() {
1359 if val.has_dotted_bit() || sub.style() == TableStyle::Dotted {
1360 let node = Prefix {
1361 name: key.name,
1362 key_span: key.span,
1363 parent: None,
1364 };
1365 format_inline_dotted_kv(sub, &node, emit, out, &mut first, depth);
1366 continue;
1367 }
1368 }
1369 if !first {
1370 out.extend_from_slice(b", ");
1371 }
1372 first = false;
1373 emit_key(key.name, key.span, emit, out);
1374 out.extend_from_slice(b" = ");
1375 format_value_at(val, emit, out, depth, true);
1376 }
1377 out.extend_from_slice(b" }");
1378}
1379
1380fn format_inline_dotted_kv(
1381 table: &Table<'_>,
1382 prefix: &Prefix<'_, '_>,
1383 emit: &Emitter<'_, '_>,
1384 out: &mut Vec<u8>,
1385 first: &mut bool,
1386 depth: u32,
1387) {
1388 for (key, val) in table {
1389 if let Some(sub) = val.as_table() {
1390 if val.has_dotted_bit() || sub.style() == TableStyle::Dotted {
1391 let node = Prefix {
1392 name: key.name,
1393 key_span: key.span,
1394 parent: Some(prefix),
1395 };
1396 format_inline_dotted_kv(sub, &node, emit, out, first, depth);
1397 continue;
1398 }
1399 }
1400 if !*first {
1401 out.extend_from_slice(b", ");
1402 }
1403 *first = false;
1404 write_prefix_path(prefix, emit, out);
1405 out.push(b'.');
1406 emit_key(key.name, key.span, emit, out);
1407 out.extend_from_slice(b" = ");
1408 format_value_at(val, emit, out, depth, true);
1409 }
1410}
1411
1412fn format_expanded_array(arr: &Array<'_>, emit: &Emitter<'_, '_>, out: &mut Vec<u8>, depth: u32) {
1413 if arr.is_empty() {
1414 out.extend_from_slice(b"[]");
1415 return;
1416 }
1417 out.extend_from_slice(b"[\n");
1418 let child = depth + 1;
1419 for elem in arr {
1420 emit.indent.write(out, child);
1421 format_value_at(elem, emit, out, child, false);
1422 out.extend_from_slice(b",\n");
1423 }
1424 emit.indent.write(out, depth);
1425 out.push(b']');
1426}
1427
1428fn format_array(arr: &Array<'_>, emit: &Emitter<'_, '_>, out: &mut Vec<u8>) {
1429 if arr.is_empty() {
1430 out.extend_from_slice(b"[]");
1431 return;
1432 }
1433 out.push(b'[');
1434 let mut first = true;
1435 for elem in arr {
1436 if !first {
1437 out.extend_from_slice(b", ");
1438 }
1439 first = false;
1440 format_value_at(elem, emit, out, 0, true);
1441 }
1442 out.push(b']');
1443}
1444
1445fn format_key(name: &str, out: &mut Vec<u8>) {
1446 if is_bare_key(name) {
1447 out.extend_from_slice(name.as_bytes());
1448 } else {
1449 if can_use_literal(name) {
1450 format_literal_string(name, out);
1451 } else {
1452 format_basic_string(name, out);
1453 }
1454 }
1455}
1456
1457fn is_bare_key(name: &str) -> bool {
1458 if name.is_empty() {
1459 return false;
1460 }
1461 for b in name.bytes() {
1462 if !b.is_ascii_alphanumeric() && b != b'-' && b != b'_' {
1463 return false;
1464 }
1465 }
1466 true
1467}
1468
1469fn format_string(s: &str, inline: bool, out: &mut Vec<u8>) {
1470 if !inline && can_use_multiline_literal(s) {
1471 format_multiline_literal_string(s, out);
1472 } else if can_use_literal(s) {
1473 format_literal_string(s, out);
1474 } else {
1475 format_basic_string(s, out);
1476 }
1477}
1478
1479fn can_use_multiline_literal(s: &str) -> bool {
1480 let mut has_newline = false;
1481 let mut consecutive_quotes = 0u8;
1482 for &b in s.as_bytes() {
1483 if b == b'\'' {
1484 consecutive_quotes += 1;
1485 if consecutive_quotes == 3 {
1486 return false;
1487 }
1488 } else {
1489 consecutive_quotes = 0;
1490 match b {
1491 b'\n' => has_newline = true,
1492 b'\t' => {}
1493 b'\r' => return false,
1494 b if b < 0x20 || b == 0x7F => return false,
1495 _ => {}
1496 }
1497 }
1498 }
1499 has_newline
1500}
1501
1502fn can_use_literal(s: &str) -> bool {
1503 let mut has_escapable = false;
1504 for ch in s.chars() {
1505 match ch {
1506 '\'' => return false,
1507 '"' | '\\' => has_escapable = true,
1508 c if c < '\x20' || c == '\x7F' => return false,
1509 _ => {}
1510 }
1511 }
1512 has_escapable
1513}
1514
1515fn format_multiline_literal_string(s: &str, out: &mut Vec<u8>) {
1516 out.extend_from_slice(b"'''\n");
1517 out.extend_from_slice(s.as_bytes());
1518 out.extend_from_slice(b"'''");
1519}
1520
1521fn format_literal_string(s: &str, out: &mut Vec<u8>) {
1522 out.push(b'\'');
1523 out.extend_from_slice(s.as_bytes());
1524 out.push(b'\'');
1525}
1526
1527fn format_basic_string(s: &str, out: &mut Vec<u8>) {
1528 out.push(b'"');
1529 for ch in s.chars() {
1530 match ch {
1531 '"' => out.extend_from_slice(b"\\\""),
1532 '\\' => out.extend_from_slice(b"\\\\"),
1533 '\n' => out.extend_from_slice(b"\\n"),
1534 '\t' => out.extend_from_slice(b"\\t"),
1535 '\r' => out.extend_from_slice(b"\\r"),
1536 '\u{0008}' => out.extend_from_slice(b"\\b"),
1537 '\u{000C}' => out.extend_from_slice(b"\\f"),
1538 c if c < '\x20' || c == '\x7F' => {
1539 let val = c as u32;
1540 let hex = b"0123456789ABCDEF";
1541 out.extend_from_slice(&[
1542 b'\\',
1543 b'u',
1544 b'0',
1545 b'0',
1546 hex[(val >> 4) as usize & 0xF],
1547 hex[val as usize & 0xF],
1548 ]);
1549 }
1550 c => {
1551 let mut buf = [0u8; 4];
1552 out.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
1553 }
1554 }
1555 }
1556 out.push(b'"');
1557}
1558
1559fn format_float(f: f64, out: &mut Vec<u8>) {
1560 if f.is_nan() {
1561 out.extend_from_slice(if f.is_sign_positive() {
1562 b"nan"
1563 } else {
1564 b"-nan"
1565 });
1566 } else if f.is_infinite() {
1567 out.extend_from_slice(if f > 0.0 { b"inf" } else { b"-inf" });
1568 } else {
1569 let mut buffer = zmij::Buffer::new();
1570 out.extend_from_slice(buffer.format(f).as_bytes());
1571 }
1572}