fuel_indexer_graphql_parser/
format.rs

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