1use std::borrow::Cow;
2use std::fmt::Write;
3
4use deser_core::ext::ExtValue;
5use deser_core::hints::Layout;
6use deser_core::ser::{self, SerializeDriver, SerializeRef};
7use deser_core::{Atom, BytesFormat, Error, ErrorKind, Event, Serialize, State};
8
9use crate::document::{Document, Entry, Item, Span, TableKind, Value};
10use deser_core::ext::{Datetime, Number, Timestamp};
11
12#[derive(Debug, Clone, Default, PartialEq, Eq)]
30pub struct SerializerConfig {
31 context: deser_core::Context,
32}
33
34impl SerializerConfig {
35 pub const fn new() -> SerializerConfig {
37 SerializerConfig {
38 context: deser_core::Context::new(),
39 }
40 }
41
42 pub const fn builder() -> SerializerConfigBuilder {
44 SerializerConfigBuilder::new()
45 }
46
47 pub const fn into_builder(self) -> SerializerConfigBuilder {
49 SerializerConfigBuilder { value: self }
50 }
51
52 pub fn set_context(&mut self, context: deser_core::Context) {
60 self.context = context;
61 }
62
63 pub fn context(&self) -> &deser_core::Context {
65 &self.context
66 }
67
68 #[inline]
70 fn apply_context(&self, driver: &mut SerializeDriver<'_>) {
71 if !self.context.is_empty() {
72 driver.set_default_context(self.context.clone());
73 }
74 }
75
76 pub fn to_string<T: Serialize + ?Sized>(&self, value: &T) -> Result<String, Error> {
78 self.to_string_ref(SerializeRef::new(&value))
79 }
80
81 pub fn to_string_with<F, T: Serialize + ?Sized>(
86 &self,
87 value: &T,
88 setup: F,
89 ) -> Result<String, Error>
90 where
91 F: FnOnce(&mut SerializeDriver<'_>),
92 {
93 let mut driver = SerializeDriver::new(&value);
94 setup(&mut driver);
95 self.apply_context(&mut driver);
96 self.serialize_driver(&mut driver)
97 }
98
99 fn to_string_ref(&self, value: SerializeRef<'_>) -> Result<String, Error> {
105 let mut driver = SerializeDriver::from_ref(value);
106 self.apply_context(&mut driver);
107 self.serialize_driver(&mut driver)
108 }
109
110 pub(crate) fn serialize_driver(
112 &self,
113 driver: &mut SerializeDriver<'_>,
114 ) -> Result<String, Error> {
115 let mut builder = Builder {
116 doc: Document::default(),
117 stack: Vec::new(),
118 done: false,
119 bytes: BytesFormat::of(driver.state()),
120 };
121 driver.drive(|event, state| builder.event(event, state))?;
122 if !builder.done {
123 return Err(Error::new(
124 ErrorKind::InvalidState,
125 "no value was serialized",
126 ));
127 }
128 let mut writer = Writer {
129 doc: &builder.doc,
130 out: String::new(),
131 };
132 writer.write_document()?;
133 Ok(writer.out)
134 }
135}
136
137#[derive(Debug, Clone)]
141#[must_use]
142pub struct SerializerConfigBuilder {
143 value: SerializerConfig,
144}
145
146impl SerializerConfigBuilder {
147 pub const fn new() -> SerializerConfigBuilder {
149 SerializerConfigBuilder {
150 value: SerializerConfig::new(),
151 }
152 }
153
154 pub fn context(mut self, context: deser_core::Context) -> SerializerConfigBuilder {
158 self.value.set_context(context);
159 self
160 }
161
162 pub const fn build(self) -> SerializerConfig {
164 let value = unsafe { core::ptr::read(&self.value) };
168 core::mem::forget(self);
169 value
170 }
171}
172
173impl Default for SerializerConfigBuilder {
174 fn default() -> SerializerConfigBuilder {
175 SerializerConfigBuilder::new()
176 }
177}
178
179#[derive(Debug, Clone)]
198pub struct Serializer {
199 config: SerializerConfig,
200 out: String,
201 written: usize,
202}
203
204impl Default for Serializer {
205 fn default() -> Serializer {
206 Serializer::new()
207 }
208}
209
210impl Serializer {
211 pub fn new() -> Serializer {
213 Serializer::with_config(SerializerConfig::new())
214 }
215
216 pub fn with_config(config: SerializerConfig) -> Serializer {
218 Serializer {
219 config,
220 out: String::new(),
221 written: 0,
222 }
223 }
224
225 pub fn serialize<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
229 ser::Serializer::serialize(self, value)
230 }
231
232 pub fn serialize_with<F, T: Serialize + ?Sized>(
237 &mut self,
238 value: &T,
239 setup: F,
240 ) -> Result<(), Error>
241 where
242 F: FnOnce(&mut SerializeDriver<'_>),
243 {
244 ser::Serializer::serialize_with(self, value, setup)
245 }
246
247 pub fn config(&self) -> &SerializerConfig {
249 &self.config
250 }
251
252 pub fn as_str(&self) -> &str {
254 &self.out
255 }
256
257 pub fn finish(self) -> String {
259 self.out
260 }
261}
262
263impl ser::Serializer for Serializer {
264 fn drive(&mut self, driver: &mut SerializeDriver<'_>) -> Result<(), Error> {
265 if !self.config.context.is_empty() {
266 driver.set_default_context(self.config.context.clone());
267 }
268 if self.written > 0 {
269 return Err(Error::new(
270 ErrorKind::InvalidState,
271 "a TOML document holds a single value",
272 ));
273 }
274 let toml = self.config.serialize_driver(driver)?;
275 self.out.push_small(&toml);
276 self.written += 1;
277 Ok(())
278 }
279}
280
281impl ser::StreamSerializer for Serializer {
282 fn output(&self) -> &[u8] {
283 self.out.as_bytes()
284 }
285
286 fn clear_output(&mut self) {
287 self.out.clear();
288 }
289}
290
291#[cfg(feature = "io")]
292impl SerializerConfig {
293 pub fn writer<W: std::io::Write>(&self, writer: W) -> deser_core::io::Writer<W, Serializer> {
299 deser_core::io::Writer::new(writer, Serializer::with_config(self.clone()))
300 }
301
302 pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
306 &self,
307 writer: W,
308 value: &T,
309 ) -> Result<(), Error> {
310 self.writer(writer).write(value)
311 }
312}
313
314#[cfg(feature = "io")]
328pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
329 writer: W,
330 value: &T,
331) -> Result<(), Error> {
332 SerializerConfig::new().to_writer(writer, value)
333}
334
335pub fn to_string<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
351 SerializerConfig::new().to_string(value)
352}
353
354enum Frame {
356 Table(usize, Option<String>),
358 Array(usize),
359}
360
361struct Builder {
363 doc: Document<'static>,
364 stack: Vec<Frame>,
365 done: bool,
366 bytes: BytesFormat,
367}
368
369enum Converted {
371 Value(Value<'static>),
372 Null,
373}
374
375impl Builder {
376 fn event(&mut self, event: Event, state: &State) -> Result<(), Error> {
377 let Some(frame) = self.stack.last_mut() else {
378 if self.done {
379 return Err(Error::new(ErrorKind::InvalidState, "unexpected event"));
380 }
381 return match event {
382 Event::MapStart(_) => {
383 let id = self.doc.new_table(TableKind::Header, Span::default());
384 self.stack.push(Frame::Table(id, None));
385 Ok(())
386 }
387 _ => Err(Error::new(
388 ErrorKind::UnsupportedType,
389 "TOML documents must be tables",
390 )),
391 };
392 };
393
394 match *frame {
395 Frame::Table(_, ref mut key @ None) => match event {
396 Event::Atom(atom) => {
397 *key = Some(key_to_string(atom, self.bytes)?);
398 Ok(())
399 }
400 Event::MapEnd => {
401 self.stack.pop();
402 self.done = self.stack.is_empty();
403 Ok(())
404 }
405 _ => Err(unsupported_key()),
406 },
407 Frame::Table(id, ref mut key @ Some(_)) => {
408 let key = key.take().unwrap();
409 let value = match self.value(event, state)? {
410 Converted::Value(value) => value,
411 Converted::Null => return Ok(()),
413 };
414 let entry = Entry {
415 key: Cow::Owned(key),
416 key_span: Span::default(),
417 item: Item {
418 value,
419 span: Span::default(),
420 },
421 };
422 match self.doc.insert_new(id, entry) {
423 Ok(()) => Ok(()),
424 Err(entry) => Err(Error::new(
425 ErrorKind::DuplicateKey,
426 format!("duplicate key `{}`", entry.key),
427 )),
428 }
429 }
430 Frame::Array(id) => {
431 if event == Event::SeqEnd {
432 self.stack.pop();
433 return Ok(());
434 }
435 match self.value(event, state)? {
436 Converted::Value(value) => {
437 self.doc.arrays[id].items.push(Item {
438 value,
439 span: Span::default(),
440 });
441 Ok(())
442 }
443 Converted::Null => Err(Error::new(
444 ErrorKind::UnsupportedType,
445 "TOML does not support null values in arrays",
446 )),
447 }
448 }
449 }
450 }
451
452 fn value(&mut self, event: Event, state: &State) -> Result<Converted, Error> {
455 match event {
456 Event::Atom(Atom::Bytes(ref bytes)) => Ok(Converted::Value(
457 self.bytes_value(bytes, bytes.fallback.copied().unwrap_or(self.bytes)),
458 )),
459 Event::Atom(atom) => convert_atom(atom, self.bytes),
460 Event::MapStart(_) => {
461 let kind = match Layout::of(state) {
463 Layout::Compact => TableKind::Inline,
464 _ => TableKind::Header,
465 };
466 let id = self.doc.new_table(kind, Span::default());
467 self.stack.push(Frame::Table(id, None));
468 Ok(Converted::Value(Value::Table(id)))
469 }
470 Event::SeqStart(_) => {
471 let of_tables = Layout::of(state) != Layout::Compact;
473 let id = self.doc.new_array(of_tables, Span::default());
474 self.stack.push(Frame::Array(id));
475 Ok(Converted::Value(Value::Array(id)))
476 }
477 Event::MapEnd | Event::SeqEnd => {
478 Err(Error::new(ErrorKind::InvalidState, "unexpected end event"))
479 }
480 }
481 }
482
483 fn bytes_value(&mut self, bytes: &[u8], format: BytesFormat) -> Value<'static> {
485 match format.encode(bytes) {
486 Some(encoded) => Value::Str(Cow::Owned(encoded)),
487 None => {
488 let id = self.doc.new_array(false, Span::default());
489 self.doc.arrays[id]
490 .items
491 .extend(bytes.iter().map(|&byte| Item {
492 value: Value::Int(byte.into()),
493 span: Span::default(),
494 }));
495 Value::Array(id)
496 }
497 }
498 }
499}
500
501fn convert_atom(atom: Atom, bytes: BytesFormat) -> Result<Converted, Error> {
502 Ok(Converted::Value(match atom {
503 Atom::Null => return Ok(Converted::Null),
504 Atom::Bool(value) => Value::Bool(value),
505 Atom::Str(value) | Atom::Lexical(value) => Value::Str(Cow::Owned(value.into_owned())),
506 Atom::Char(value) => Value::Str(Cow::Owned(value.to_string())),
507 Atom::U64(value) => match i64::try_from(value) {
508 Ok(value) => Value::Int(value),
509 Err(_) => Value::UInt(value),
510 },
511 Atom::I64(value) => Value::Int(value),
512 Atom::F64(value) => Value::Float(value),
513 Atom::F32(value) => Value::Float32(value),
514 Atom::Bytes(value) => Value::Str(Cow::Owned(encode_str(&value, value.fallback, bytes))),
517 Atom::Ext(ref ext) => return convert_ext(ext, bytes),
518 Atom::Implicit(value) => return convert_atom(value.value().to_atom(), bytes),
520 _ => return Err(Error::new(ErrorKind::UnsupportedType, "unknown atom")),
521 }))
522}
523
524#[cold]
525fn convert_ext(ext: &ExtValue, bytes: BytesFormat) -> Result<Converted, Error> {
526 if let Some(value) = ext.downcast_ref::<Datetime>() {
527 if !value.is_valid() {
528 return Err(Error::new(ErrorKind::InvalidValue, "invalid datetime"));
529 }
530 return Ok(Converted::Value(Value::Datetime(*value)));
531 }
532 if let Some(value) = ext.downcast_value_ref::<Number>() {
535 return Ok(Converted::Value(if value.is_integer() {
536 Value::Float(value.value())
537 } else {
538 Value::FloatText(Cow::Owned(value.as_str().to_string()))
539 }));
540 }
541 if let Some(value) = ext
543 .downcast_ref::<Timestamp>()
544 .and_then(|x| x.to_datetime())
545 {
546 return Ok(Converted::Value(Value::Datetime(value)));
547 }
548 let out_of_range = || Error::new(ErrorKind::OutOfRange, "integer out of range for TOML");
549 if let Some(&value) = ext.downcast_ref::<u128>() {
550 let value = u64::try_from(value).map_err(|_| out_of_range())?;
551 return convert_atom(Atom::U64(value), bytes);
552 }
553 if let Some(&value) = ext.downcast_ref::<i128>() {
554 return if let Ok(value) = i64::try_from(value) {
555 convert_atom(Atom::I64(value), bytes)
556 } else {
557 let value = u64::try_from(value).map_err(|_| out_of_range())?;
558 convert_atom(Atom::U64(value), bytes)
559 };
560 }
561 match ext.fallback() {
562 Atom::Ext(_) => Err(Error::new(
563 ErrorKind::UnsupportedType,
564 format!("TOML does not support {}", ext.name()),
565 )),
566 fallback => convert_atom(fallback, bytes),
567 }
568}
569
570fn encode_str(value: &[u8], fallback: Option<&BytesFormat>, bytes: BytesFormat) -> String {
574 fallback
575 .copied()
576 .unwrap_or(bytes)
577 .encode(value)
578 .or_else(|| BytesFormat::BASE64.encode(value))
579 .unwrap_or_default()
580}
581
582fn key_to_string(atom: Atom, bytes: BytesFormat) -> Result<String, Error> {
583 Ok(match atom {
584 Atom::Implicit(value) => return key_to_string(value.value().to_atom(), bytes),
585 Atom::Str(value) | Atom::Lexical(value) => value.into_owned(),
586 Atom::Char(value) => value.to_string(),
587 Atom::U64(value) => value.to_string(),
588 Atom::I64(value) => value.to_string(),
589 Atom::Bool(value) => value.to_string(),
590 Atom::Bytes(value) => encode_str(&value, value.fallback, bytes),
591 Atom::Ext(ref ext) => {
592 if let Some(value) = ext.downcast_ref::<u128>() {
593 value.to_string()
594 } else if let Some(value) = ext.downcast_ref::<i128>() {
595 value.to_string()
596 } else {
597 match ext.fallback() {
598 Atom::Ext(_) => return Err(unsupported_key()),
599 fallback => return key_to_string(fallback, bytes),
600 }
601 }
602 }
603 _ => return Err(unsupported_key()),
604 })
605}
606
607#[cold]
608fn unsupported_key() -> Error {
609 Error::new(
610 ErrorKind::UnsupportedType,
611 "TOML only supports strings, integers and booleans as keys",
612 )
613}
614
615#[derive(Clone, Copy, PartialEq, Eq)]
617enum Header {
618 None,
620 Table,
622 ArrayTable,
624}
625
626struct Section<'d> {
628 id: usize,
629 path: Vec<&'d str>,
630 header: Header,
631}
632
633enum InlineFrame {
635 Table(usize, usize),
636 Array(usize, usize),
637}
638
639struct Writer<'d> {
640 doc: &'d Document<'static>,
641 out: String,
642}
643
644impl<'d> Writer<'d> {
645 fn is_section(&self, value: &Value) -> bool {
647 match *value {
648 Value::Table(id) => self.doc.tables[id].kind != TableKind::Inline,
649 Value::Array(id) => self.is_array_of_tables(id),
650 _ => false,
651 }
652 }
653
654 fn is_array_of_tables(&self, id: usize) -> bool {
655 let array = &self.doc.arrays[id];
656 array.of_tables
657 && !array.items.is_empty()
658 && array
659 .items
660 .iter()
661 .all(|x| matches!(x.value, Value::Table(_)))
662 }
663
664 fn write_document(&mut self) -> Result<(), Error> {
665 let doc = self.doc;
666 let mut sections = vec![Section {
667 id: 0,
668 path: Vec::new(),
669 header: Header::None,
670 }];
671
672 while let Some(section) = sections.pop() {
673 let table = &doc.tables[section.id];
674 let has_values = table
675 .entries
676 .iter()
677 .any(|x| !self.is_section(&x.item.value));
678
679 let header = match section.header {
682 Header::Table if !has_values && !table.entries.is_empty() => None,
683 Header::Table => Some(("[", "]")),
684 Header::ArrayTable => Some(("[[", "]]")),
685 Header::None => None,
686 };
687 if let Some((open, close)) = header {
688 if !self.out.is_empty() {
689 self.out.push('\n');
690 }
691 self.out.push_small(open);
692 for (idx, key) in section.path.iter().enumerate() {
693 if idx > 0 {
694 self.out.push('.');
695 }
696 write_key(&mut self.out, key);
697 }
698 self.out.push_small(close);
699 self.out.push('\n');
700 }
701
702 for entry in &table.entries {
703 if !self.is_section(&entry.item.value) {
704 write_key(&mut self.out, &entry.key);
705 self.out.push_small(" = ");
706 self.write_value(&entry.item.value)?;
707 self.out.push('\n');
708 }
709 }
710
711 let first_child = sections.len();
713 for entry in &table.entries {
714 let child_path = || {
715 let mut path = section.path.clone();
716 path.push(&*entry.key);
717 path
718 };
719 match entry.item.value {
720 Value::Table(id) if self.is_section(&entry.item.value) => {
721 sections.push(Section {
722 id,
723 path: child_path(),
724 header: Header::Table,
725 })
726 }
727 Value::Array(id) if self.is_array_of_tables(id) => {
728 for item in &doc.arrays[id].items {
729 if let Value::Table(id) = item.value {
730 sections.push(Section {
731 id,
732 path: child_path(),
733 header: Header::ArrayTable,
734 });
735 }
736 }
737 }
738 _ => {}
739 }
740 }
741 sections[first_child..].reverse();
742 }
743
744 Ok(())
745 }
746
747 fn write_value(&mut self, value: &Value) -> Result<(), Error> {
749 let doc = self.doc;
750 let mut stack = Vec::new();
751 self.write_value_start(value, &mut stack);
752
753 while let Some(frame) = stack.last_mut() {
754 let value = match *frame {
755 InlineFrame::Table(id, ref mut index) => {
756 let table = &doc.tables[id];
757 match table.entries.get(*index) {
758 Some(entry) => {
759 self.out.push_small(if *index == 0 { " " } else { ", " });
760 *index += 1;
761 write_key(&mut self.out, &entry.key);
762 self.out.push_small(" = ");
763 &entry.item.value
764 }
765 None => {
766 self.out
767 .push_small(if table.entries.is_empty() { "}" } else { " }" });
768 stack.pop();
769 continue;
770 }
771 }
772 }
773 InlineFrame::Array(id, ref mut index) => match doc.arrays[id].items.get(*index) {
774 Some(item) => {
775 if *index > 0 {
776 self.out.push_small(", ");
777 }
778 *index += 1;
779 &item.value
780 }
781 None => {
782 self.out.push(']');
783 stack.pop();
784 continue;
785 }
786 },
787 };
788 self.write_value_start(value, &mut stack);
789 }
790
791 Ok(())
792 }
793
794 fn write_value_start(&mut self, value: &Value, stack: &mut Vec<InlineFrame>) {
796 match *value {
797 Value::Str(ref value) => write_string(&mut self.out, value),
798 Value::Int(value) => self.out.push_small(itoa::Buffer::new().format(value)),
799 Value::UInt(value) => self.out.push_small(itoa::Buffer::new().format(value)),
800 Value::Float(value) => write_float(&mut self.out, value),
801 Value::Float32(value) => write_float(&mut self.out, value),
802 Value::FloatText(ref value) => self.out.push_small(value),
803 Value::Bool(value) => self.out.push_small(if value { "true" } else { "false" }),
804 Value::Datetime(ref value) => write!(self.out, "{}", value).unwrap(),
805 Value::Table(id) => {
806 self.out.push('{');
807 stack.push(InlineFrame::Table(id, 0));
808 }
809 Value::Array(id) => {
810 self.out.push('[');
811 stack.push(InlineFrame::Array(id, 0));
812 }
813 }
814 }
815}
816
817fn write_float<F: zmij::Float + Into<f64>>(out: &mut String, value: F) {
821 let wide: f64 = value.into();
822 if wide.is_nan() {
823 out.push_small("nan");
824 } else if wide.is_infinite() {
825 out.push_small(if wide > 0.0 { "inf" } else { "-inf" });
826 } else {
827 out.push_small(zmij::Buffer::new().format_finite(value));
828 }
829}
830
831fn is_bare_key(key: &str) -> bool {
832 !key.is_empty()
833 && key
834 .bytes()
835 .all(|c| c.is_ascii_alphanumeric() || c == b'_' || c == b'-')
836}
837
838fn write_key(out: &mut String, key: &str) {
839 if is_bare_key(key) {
840 out.push_small(key);
841 } else {
842 write_basic_string(out, key);
843 }
844}
845
846fn write_string(out: &mut String, value: &str) {
848 if value.contains('\n') {
849 write_multiline_string(out, value);
850 } else if value.contains(['"', '\\'])
851 && !value
852 .chars()
853 .any(|c| c == '\'' || (c.is_control() && c != '\t'))
854 {
855 out.push('\'');
857 out.push_small(value);
858 out.push('\'');
859 } else {
860 write_basic_string(out, value);
861 }
862}
863
864fn write_control_escape(out: &mut String, c: char) {
868 match c {
869 '\x08' => out.push_small("\\b"),
870 '\t' => out.push_small("\\t"),
871 '\n' => out.push_small("\\n"),
872 '\x0c' => out.push_small("\\f"),
873 '\r' => out.push_small("\\r"),
874 c => write!(out, "\\u{:04X}", c as u32).unwrap(),
875 }
876}
877
878fn is_escaped_control(c: char) -> bool {
879 matches!(c, '\0'..='\x1f' | '\x7f')
880}
881
882fn write_basic_string(out: &mut String, value: &str) {
883 out.push('"');
884 for c in value.chars() {
885 match c {
886 '"' => out.push_small("\\\""),
887 '\\' => out.push_small("\\\\"),
888 c if is_escaped_control(c) => write_control_escape(out, c),
889 c => out.push(c),
890 }
891 }
892 out.push('"');
893}
894
895fn write_multiline_string(out: &mut String, value: &str) {
896 out.push_small("\"\"\"\n");
898 let mut quotes = 0;
899 for c in value.chars() {
900 match c {
901 '"' if quotes == 2 => {
903 out.push_small("\\\"");
904 quotes = 0;
905 continue;
906 }
907 '"' => out.push('"'),
908 '\\' => out.push_small("\\\\"),
909 '\n' | '\t' => out.push(c),
910 c if is_escaped_control(c) => write_control_escape(out, c),
911 c => out.push(c),
912 }
913 quotes = if c == '"' { quotes + 1 } else { 0 };
914 }
915 out.push_small("\"\"\"");
916}
917
918trait PushSmall {
920 fn push_small(&mut self, s: &str);
921}
922
923impl PushSmall for String {
924 #[inline(always)]
925 fn push_small(&mut self, s: &str) {
926 crate::copy::push_str(self, s);
927 }
928}