prison_architect_savefile/
format.rs1use std::fmt::{self, Write};
2
3use crate::Node;
4
5impl fmt::Display for Node {
6 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7 Writer { f, depth: 0 }.write_node(self)
8 }
9}
10
11struct Writer<F> {
12 f: F,
13 depth: u32,
14}
15
16impl<F> Writer<F>
17where
18 F: Write,
19{
20 fn write_node(&mut self, node: &Node) -> fmt::Result {
21 let mut first = true;
22 for (key, value) in node.properties() {
23 if !first {
24 self.write_newline()?;
25 }
26 first = false;
27
28 self.write_property(key, value)?;
29 }
30
31 for (key, value) in node.children() {
32 if !first {
33 self.write_newline()?;
34 }
35 first = false;
36
37 self.f.write_str("BEGIN ")?;
38 self.write_str(key)?;
39
40 if value.children.is_empty() {
41 self.f.write_char(' ')?;
42 for (key, value) in value.properties() {
43 self.write_property(key, value)?;
44 self.f.write_char(' ')?;
45 }
46 self.f.write_char(' ')?;
47 } else {
48 self.depth += 1;
49 self.write_newline()?;
50
51 self.write_node(value)?;
52
53 self.depth -= 1;
54 self.write_newline()?;
55 }
56
57 self.f.write_str("END")?;
58 }
59
60 Ok(())
61 }
62
63 fn write_property(&mut self, key: &str, value: &str) -> fmt::Result {
64 self.write_str(key)?;
65 self.f.write_char(' ')?;
66 self.write_str(value)?;
67 Ok(())
68 }
69
70 fn write_str(&mut self, value: &str) -> fmt::Result {
71 let mut special_chars = value.match_indices(&[' ', '"', '\n']).peekable();
72 if special_chars.peek().is_some() {
73 self.f.write_char('"')?;
74 let mut pos = 0;
75 for (start, ch) in special_chars {
76 self.f.write_str(&value[pos..start])?;
77 match ch {
78 " " => self.f.write_char(' ')?,
79 "\"" => self.f.write_str("\\\"")?,
80 "\n" => self.f.write_str("\\n")?,
81 _ => unreachable!(),
82 }
83 pos = start + ch.len();
84 }
85 self.f.write_str(&value[pos..])?;
86 self.f.write_char('"')?;
87 Ok(())
88 } else {
89 self.f.write_str(value)
90 }
91 }
92
93 fn write_newline(&mut self) -> fmt::Result {
94 self.f.write_char('\n')?;
95 for _ in 0..self.depth {
96 self.f.write_str(" ")?;
97 }
98 Ok(())
99 }
100}
101
102#[test]
103fn test() {
104 use std::collections::HashMap;
105
106 let node = Node {
107 properties: HashMap::from_iter([("foo".to_owned(), vec!["hello, world!".to_owned()])]),
108 children: HashMap::from_iter([(
109 "bar".to_owned(),
110 vec![Node {
111 properties: HashMap::from_iter([(
112 "baz".to_owned(),
113 vec!["one\n\"two\"".to_owned()],
114 )]),
115 children: HashMap::default(),
116 }],
117 )]),
118 };
119
120 assert_eq!(
121 node.to_string(),
122 "foo \"hello, world!\"\nBEGIN bar baz \"one\\n\\\"two\\\"\" END"
123 )
124}