1use std::default::Default;
3
4use crate::common::Directive;
5
6#[derive(Debug, PartialEq)]
7pub(crate) struct Formatter<'a> {
8 buf: String,
9 style: &'a Style,
10 indent: u32,
11}
12
13#[derive(Debug, PartialEq, Clone)]
18pub struct Style {
19 indent: u32,
20}
21
22impl Default for Style {
23 fn default() -> Style {
24 Style { indent: 2 }
25 }
26}
27
28impl Style {
29 pub fn indent(&mut self, indent: u32) -> &mut Self {
31 self.indent = indent;
32 self
33 }
34}
35
36pub(crate) trait Displayable {
37 fn display(&self, f: &mut Formatter);
38}
39
40impl<'a> Formatter<'a> {
41 pub fn new(style: &Style) -> Formatter {
42 Formatter {
43 buf: String::with_capacity(1024),
44 style,
45 indent: 0,
46 }
47 }
48
49 pub fn indent(&mut self) {
50 for _ in 0..self.indent {
51 self.buf.push(' ');
52 }
53 }
54
55 pub fn endline(&mut self) {
56 self.buf.push('\n');
57 }
58
59 pub fn start_block(&mut self) {
60 self.buf.push('{');
61 self.endline();
62 self.indent += self.style.indent;
63 }
64
65 pub fn end_block(&mut self) {
66 self.indent = self
67 .indent
68 .checked_sub(self.style.indent)
69 .expect("negative indent");
70 self.indent();
71 self.buf.push('}');
72 self.endline();
73 }
74
75 pub fn margin(&mut self) {
76 if !self.buf.is_empty() {
77 self.buf.push('\n');
78 }
79 }
80
81 pub fn write(&mut self, s: &str) {
82 self.buf.push_str(s);
83 }
84
85 pub fn into_string(self) -> String {
86 self.buf
87 }
88
89 pub fn write_quoted(&mut self, s: &str) {
90 let mut has_newline = false;
91 let mut has_nonprintable = false;
92 for c in s.chars() {
93 match c {
94 '\n' => has_newline = true,
95 '\r' | '\t' | '\u{0020}'..='\u{FFFF}' => {}
96 _ => has_nonprintable = true,
97 }
98 }
99 if !has_newline || has_nonprintable {
100 use std::fmt::Write;
101 self.buf.push('"');
102 for c in s.chars() {
103 match c {
104 '\r' => self.write(r"\r"),
105 '\n' => self.write(r"\n"),
106 '\t' => self.write(r"\t"),
107 '"' => self.write("\\\""),
108 '\\' => self.write(r"\\"),
109 '\u{0020}'..='\u{FFFF}' => self.buf.push(c),
110 _ => write!(&mut self.buf, "\\u{:04}", c as u32).unwrap(),
111 }
112 }
113 self.buf.push('"');
114 } else {
115 self.buf.push_str(r#"""""#);
116 self.endline();
117 self.indent += self.style.indent;
118 for line in s.lines() {
119 if !line.trim().is_empty() {
120 self.indent();
121 self.write(&line.replace(r#"""""#, r#"\""""#));
122 }
123 self.endline();
124 }
125 self.indent -= self.style.indent;
126 self.indent();
127 self.buf.push_str(r#"""""#);
128 }
129 }
130}
131
132pub(crate) fn format_directives(dirs: &[Directive], f: &mut Formatter) {
133 for dir in dirs {
134 f.write(" ");
135 dir.display(f);
136 }
137}
138
139macro_rules! impl_display {
140 ($( $typ: ident, )+) => {
141 $(
142 impl fmt::Display for $typ {
143 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 f.write_str(&to_string(self))
145 }
146 }
147 )+
148 };
149}