gurkle_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}
21
22impl Default for Style {
23    fn default() -> Style {
24        Style { indent: 2 }
25    }
26}
27
28impl Style {
29    /// Change the number of spaces used for indentation
30    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}