idl/formatting/
idl.rs

1use super::*;
2use crate::idl::{keywords::Keywords, parser::*};
3
4// TODO Split lines with more than 80 characters.
5
6// The only stuff that this formatter reorders are the the library name and imports,
7// since they must appear first. Anything else, only the comments, spaces and indentantion are fixed.
8pub fn format_document(parser: &Parser) -> Option<String> {
9    let mut body = String::new();
10    let mut library = String::new();
11    let mut imports = String::new();
12
13    let mut is_comment = false;
14
15    for node in &parser.nodes {
16        match node {
17            ParserNode::Imports(value) => {
18                imports = format!("{} {{", Keywords::Import);
19                // Only imports can span an entire line without being separated by a new line.
20                imports +=
21                    split_if_longer_than(value.as_slice(), MAX_LENGTH - imports.len() - 1).as_str();
22                imports += CLOSE_NEW_LINE;
23            }
24            ParserNode::Library(value) => {
25                library = format!("{} ", Keywords::Library);
26                library += value.as_str();
27                library += NEW_LINE;
28            }
29            ParserNode::Comment(value) => {
30                if is_comment {
31                    body += "\n";
32                }
33
34                for comment in value {
35                    let list_comment = format!("{}{}\n", COMMENT_START, comment.as_str());
36                    body += list_comment.as_str();
37                }
38                is_comment = true;
39            }
40            nodw => {
41                if is_comment {
42                    body += "\n";
43                    is_comment = false;
44                }
45                match nodw {
46                    ParserNode::Const(value) => {
47                        if !value.comment.is_empty() {
48                            push_comment(&mut body, &value.comment);
49                        }
50
51                        let type_body = format!(
52                            "{} {} = {};{}",
53                            Keywords::Const,
54                            value.ident,
55                            value.value.to_owned(),
56                            NEW_LINE
57                        );
58                        body += type_body.as_str();
59                    }
60                    ParserNode::Enum(value) => {
61                        if !value.comment.is_empty() {
62                            push_comment(&mut body, &value.comment);
63                        }
64
65                        let type_body =
66                            format!("{} {}{}", Keywords::Enum, value.ident, OPEN_NEW_LINE);
67                        body += type_body.as_str();
68
69                        for enum_node in value.fields.iter() {
70                            match enum_node {
71                                EnumNode::Comment(comment) => {
72                                    push_field_comment(&mut body, comment)
73                                }
74                                EnumNode::EnumField(field) => {
75                                    let field_str = format!("{}{},\n", INDENT, field);
76                                    body += field_str.as_str();
77                                }
78                                EnumNode::TypeEnumField(field) => {
79                                    let field_str = format!("{}{},\n", INDENT, field);
80                                    body += field_str.as_str();
81                                }
82                            }
83                        }
84                        body += CLOSE_NEW_LINE;
85                    }
86                    ParserNode::Interface(value) => {
87                        if !value.comment.is_empty() {
88                            push_comment(&mut body, &value.comment);
89                        }
90
91                        let type_body =
92                            format!("{} {}{}", Keywords::Interface, value.ident, OPEN_NEW_LINE);
93                        body += type_body.as_str();
94
95                        for interface_node in value.fields.iter() {
96                            match interface_node {
97                                InterfaceNode::Comment(comment) => {
98                                    push_field_comment(&mut body, comment)
99                                }
100                                InterfaceNode::InterfaceField(field) => {
101                                    let field_str = split_interface_field(field);
102                                    body += field_str.as_str();
103                                }
104                            }
105                        }
106                        body += CLOSE_NEW_LINE;
107                    }
108                    ParserNode::Struct(value) => {
109                        if !value.comment.is_empty() {
110                            push_comment(&mut body, &value.comment);
111                        }
112
113                        let type_body =
114                            format!("{} {}{}", Keywords::Struct, value.ident, OPEN_NEW_LINE);
115                        body += type_body.as_str();
116
117                        for struct_node in value.fields.iter() {
118                            match struct_node {
119                                StructNode::Comment(comment) => {
120                                    push_field_comment(&mut body, comment)
121                                }
122                                StructNode::StructField(field) => {
123                                    let field_str = format!("{}{},\n", INDENT, field);
124                                    body += field_str.as_str();
125                                }
126                            }
127                        }
128                        body += CLOSE_NEW_LINE;
129                    }
130                    _ => {}
131                }
132            }
133        }
134    }
135
136    library += imports.as_str();
137    library += body.as_str();
138
139    Some(library.trim().to_string())
140}
141
142fn push_field_comment(body: &mut String, comments: &[String]) {
143    for comment in comments {
144        let comment_str = format!("{}{}{}\n", INDENT, COMMENT_START, comment.as_str());
145        body.push_str(comment_str.as_str());
146    }
147}
148
149fn push_comment(body: &mut String, comments: &[String]) {
150    for comment in comments {
151        let list_comment = format!("{}{}\n", COMMENT_START, comment.as_str());
152        body.push_str(list_comment.as_str());
153    }
154}
155
156// If it fits in a line, a list with space in both sides,
157// otherwise in each line, separated by a comma.
158fn split_if_longer_than(txt: &[String], max_length: usize) -> String {
159    let mut result = String::from(" ");
160
161    for value in txt.iter() {
162        if result != " " {
163            result += ", ";
164        }
165        result += value;
166    }
167
168    result += " ";
169
170    if result.len() > max_length {
171        result = String::from("\n");
172
173        for value in txt.iter() {
174            let r = format!("{}{},\n", INDENT, value);
175            result += r.as_str();
176        }
177    }
178
179    result
180}
181
182fn split_interface_field(interface_field: &InterfaceField) -> String {
183    // TODO
184    let split_tuple = |indent: usize, ty: &[TupleEntry]| {
185        let mut result = String::new();
186
187        if ty.is_empty() {
188            return "()".to_owned();
189        }
190
191        for (index, t) in ty.into_iter().enumerate() {
192            let st = format!(
193                "{}{}: {}{}",
194                if index == 0 {
195                    "(".to_owned()
196                } else {
197                    " ".repeat(indent)
198                },
199                t.ident,
200                t.ty,
201                if index != ty.len() - 1 { ",\n" } else { ")" }
202            );
203            result += st.as_str()
204        }
205
206        result
207    };
208
209    let tryln = format!(
210        "{}:{}{},",
211        INDENT,
212        interface_field.ident.to_owned(),
213        interface_field.ty.to_string()
214    );
215
216    if tryln.len() > MAX_LENGTH {
217        let indent_len = format!("{}:{}(", INDENT, interface_field.ident.to_owned()).len();
218        let ty_field = match &*interface_field.ty {
219            Type::Tuple(tuple) => split_tuple(indent_len, &tuple.fields),
220            Type::Function(function) => {
221                let tt = match &*function.args {
222                    Type::Tuple(value) => value.clone(),
223                    _ => panic!("Not a tuple"),
224                };
225
226                format!(
227                    "{} -> {}",
228                    split_tuple(indent_len, &*tt.fields),
229                    function.ret_ty.to_string()
230                )
231            }
232            ty => ty.to_string(),
233        };
234
235        format!(
236            "{}:{}{},\n",
237            INDENT,
238            interface_field.ident.to_owned(),
239            ty_field
240        )
241    } else {
242        tryln + "\n"
243    }
244}