graphql_parser/
format.rs

1//! Formatting graphql
2use std::default::Default;
3
4use crate::common::Directive;
5
6
7#[derive(Debug, PartialEq)]
8pub(crate) struct Formatter<'a> {
9    buf: String,
10    style: &'a Style,
11    indent: u32,
12}
13
14/// A configuration of formatting style
15///
16/// Currently we only have indentation configured, other things might be
17/// added later (such as minification).
18#[derive(Debug, PartialEq, Clone)]
19pub struct Style {
20    indent: u32,
21    multiline_arguments: bool,
22}
23
24impl Default for Style {
25    fn default() -> Style {
26        Style {
27            indent: 2,
28            multiline_arguments: false,
29        }
30    }
31}
32
33impl Style {
34    /// Change the number of spaces used for indentation
35    pub fn indent(&mut self, indent: u32) -> &mut Self {
36        self.indent = indent;
37        self
38    }
39
40    /// Set whether to add new lines between arguments
41    pub fn multiline_arguments(&mut self, multiline_arguments: bool) -> &mut Self {
42        self.multiline_arguments = multiline_arguments;
43        self
44    }
45}
46
47pub(crate) trait Displayable {
48    fn display(&self, f: &mut Formatter);
49}
50
51impl<'a> Formatter<'a> {
52    pub fn new(style: &Style) -> Formatter {
53        Formatter {
54            buf: String::with_capacity(1024),
55            style,
56            indent: 0,
57        }
58    }
59
60    pub fn indent(&mut self) {
61        for _ in 0..self.indent {
62            self.buf.push(' ');
63        }
64    }
65
66    pub fn endline(&mut self) {
67        self.buf.push('\n');
68    }
69
70    pub fn start_argument_block(&mut self, open_char: char) {
71        self.buf.push(open_char);
72        if self.style.multiline_arguments {
73            self.inc_indent();
74        }
75    }
76
77    pub fn end_argument_block(&mut self, close_char: char) {
78        if self.style.multiline_arguments {
79            self.endline();
80            self.dec_indent();
81            self.indent();
82        }
83        self.buf.push(close_char);
84    }
85
86    pub fn start_argument(&mut self) {
87        if self.style.multiline_arguments {
88            self.endline();
89            self.indent();
90        }
91    }
92
93    pub fn deliniate_argument(&mut self) {
94        self.buf.push(',');
95        if !self.style.multiline_arguments {
96            self.buf.push(' ');
97        }
98    }
99
100    pub fn start_block(&mut self) {
101        self.buf.push('{');
102        self.endline();
103        self.inc_indent();
104    }
105
106    pub fn end_block(&mut self) {
107        self.dec_indent();
108        self.indent();
109        self.buf.push('}');
110        self.endline();
111    }
112
113    pub fn margin(&mut self) {
114        if !self.buf.is_empty() {
115            self.buf.push('\n');
116        }
117    }
118
119    pub fn write(&mut self, s: &str) {
120        self.buf.push_str(s);
121    }
122
123    pub fn into_string(self) -> String {
124        self.buf
125    }
126
127    pub fn write_quoted(&mut self, s: &str) {
128        let mut has_newline = false;
129        let mut has_nonprintable = false;
130        for c in s.chars() {
131            match c {
132                '\n' => has_newline = true,
133                '\r' | '\t' | '\u{0020}'..='\u{FFFF}' => {}
134                _ => has_nonprintable = true,
135            }
136        }
137        if !has_newline || has_nonprintable {
138            use std::fmt::Write;
139            self.buf.push('"');
140            for c in s.chars() {
141                match c {
142                    '\r' => self.write(r"\r"),
143                    '\n' => self.write(r"\n"),
144                    '\t' => self.write(r"\t"),
145                    '"' => self.write("\\\""),
146                    '\\' => self.write(r"\\"),
147                    '\u{0020}'..='\u{FFFF}' => self.buf.push(c),
148                    _ => write!(&mut self.buf, "\\u{:04}", c as u32).unwrap(),
149                }
150            }
151            self.buf.push('"');
152        } else {
153            self.buf.push_str(r#"""""#);
154            self.endline();
155            self.indent += self.style.indent;
156            for line in s.lines() {
157                if !line.trim().is_empty() {
158                    self.indent();
159                    self.write(&line.replace(r#"""""#, r#"\""""#));
160                }
161                self.endline();
162            }
163            self.indent -= self.style.indent;
164            self.indent();
165            self.buf.push_str(r#"""""#);
166        }
167    }
168
169    fn inc_indent(&mut self) {
170        self.indent += self.style.indent;
171    }
172
173    fn dec_indent(&mut self) {
174        self.indent = self.indent.checked_sub(self.style.indent)
175            .expect("negative indent");
176    }
177}
178
179pub(crate) fn format_directives<'a, T>(dirs: &[Directive<'a, T>], f: &mut Formatter) 
180    where T: crate::common::Text<'a>,
181{
182    for dir in dirs {
183        f.write(" ");
184        dir.display(f);
185    }
186}
187
188macro_rules! impl_display {
189    ($( $typ: ident, )+) => {
190        $(
191            impl fmt::Display for $typ {
192                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
193                    f.write_str(&to_string(self))
194                }
195            }
196        )+
197    };
198
199    ('a $($typ: ident, )+) => {
200        $(
201            impl<'a, T> fmt::Display for $typ<'a, T> 
202                where T: Text<'a>,
203            {
204                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205                    f.write_str(&to_string(self))
206                }
207            }
208        )+
209    };
210}