1pub mod config;
4pub mod element;
5mod token;
6pub mod v1;
7
8use std::fmt::Write;
9
10pub use config::Config;
11pub use token::*;
12use wdl_ast::Element;
13use wdl_ast::Node as AstNode;
14
15use crate::element::FormatElement;
16
17#[cfg(windows)]
19pub const NEWLINE: &str = "\r\n";
20#[cfg(not(windows))]
22pub const NEWLINE: &str = "\n";
23pub const SPACE: &str = " ";
25pub const TAB: &str = "\t";
27
28pub trait Writable {
30 fn write(&self, stream: &mut TokenStream<PreToken>);
32}
33
34impl Writable for &FormatElement {
35 fn write(&self, stream: &mut TokenStream<PreToken>) {
36 match self.element() {
37 Element::Node(node) => match node {
38 AstNode::AccessExpr(_) => v1::expr::format_access_expr(self, stream),
39 AstNode::AdditionExpr(_) => v1::expr::format_addition_expr(self, stream),
40 AstNode::ArrayType(_) => v1::decl::format_array_type(self, stream),
41 AstNode::Ast(_) => v1::format_ast(self, stream),
42 AstNode::BoundDecl(_) => v1::decl::format_bound_decl(self, stream),
43 AstNode::CallAfter(_) => v1::workflow::call::format_call_after(self, stream),
44 AstNode::CallAlias(_) => v1::workflow::call::format_call_alias(self, stream),
45 AstNode::CallExpr(_) => v1::expr::format_call_expr(self, stream),
46 AstNode::CallInputItem(_) => {
47 v1::workflow::call::format_call_input_item(self, stream)
48 }
49 AstNode::CallStatement(_) => {
50 v1::workflow::call::format_call_statement(self, stream)
51 }
52 AstNode::CallTarget(_) => v1::workflow::call::format_call_target(self, stream),
53 AstNode::CommandSection(_) => v1::task::format_command_section(self, stream),
54 AstNode::ConditionalStatement(_) => {
55 v1::workflow::format_conditional_statement(self, stream)
56 }
57 AstNode::ConditionalStatementClause(_) => {
58 v1::workflow::format_conditional_statement_clause(self, stream)
59 }
60 AstNode::DefaultOption(_) => v1::expr::format_default_option(self, stream),
61 AstNode::DivisionExpr(_) => v1::expr::format_division_expr(self, stream),
62 AstNode::EqualityExpr(_) => v1::expr::format_equality_expr(self, stream),
63 AstNode::ExponentiationExpr(_) => {
64 v1::expr::format_exponentiation_expr(self, stream)
65 }
66 AstNode::GreaterEqualExpr(_) => v1::expr::format_greater_equal_expr(self, stream),
67 AstNode::GreaterExpr(_) => v1::expr::format_greater_expr(self, stream),
68 AstNode::IfExpr(_) => v1::expr::format_if_expr(self, stream),
69 AstNode::ImportAlias(_) => v1::import::format_import_alias(self, stream),
70 AstNode::ImportStatement(_) => v1::import::format_import_statement(self, stream),
71 AstNode::IndexExpr(_) => v1::expr::format_index_expr(self, stream),
72 AstNode::InequalityExpr(_) => v1::expr::format_inequality_expr(self, stream),
73 AstNode::InputSection(_) => v1::format_input_section(self, stream),
74 AstNode::LessEqualExpr(_) => v1::expr::format_less_equal_expr(self, stream),
75 AstNode::LessExpr(_) => v1::expr::format_less_expr(self, stream),
76 AstNode::LiteralArray(_) => v1::expr::format_literal_array(self, stream),
77 AstNode::LiteralBoolean(_) => v1::expr::format_literal_boolean(self, stream),
78 AstNode::LiteralFloat(_) => v1::expr::format_literal_float(self, stream),
79 AstNode::LiteralHints(_) => v1::format_literal_hints(self, stream),
80 AstNode::LiteralHintsItem(_) => v1::format_literal_hints_item(self, stream),
81 AstNode::LiteralInput(_) => v1::format_literal_input(self, stream),
82 AstNode::LiteralInputItem(_) => v1::format_literal_input_item(self, stream),
83 AstNode::LiteralInteger(_) => v1::expr::format_literal_integer(self, stream),
84 AstNode::LiteralMap(_) => v1::expr::format_literal_map(self, stream),
85 AstNode::LiteralMapItem(_) => v1::expr::format_literal_map_item(self, stream),
86 AstNode::LiteralNone(_) => v1::expr::format_literal_none(self, stream),
87 AstNode::LiteralNull(_) => v1::meta::format_literal_null(self, stream),
88 AstNode::LiteralObject(_) => v1::expr::format_literal_object(self, stream),
89 AstNode::LiteralObjectItem(_) => v1::expr::format_literal_object_item(self, stream),
90 AstNode::LiteralOutput(_) => v1::format_literal_output(self, stream),
91 AstNode::LiteralOutputItem(_) => v1::format_literal_output_item(self, stream),
92 AstNode::LiteralPair(_) => v1::expr::format_literal_pair(self, stream),
93 AstNode::LiteralString(_) => v1::expr::format_literal_string(self, stream),
94 AstNode::LiteralStruct(_) => v1::r#struct::format_literal_struct(self, stream),
95 AstNode::LiteralStructItem(_) => {
96 v1::r#struct::format_literal_struct_item(self, stream)
97 }
98 AstNode::LogicalAndExpr(_) => v1::expr::format_logical_and_expr(self, stream),
99 AstNode::LogicalNotExpr(_) => v1::expr::format_logical_not_expr(self, stream),
100 AstNode::LogicalOrExpr(_) => v1::expr::format_logical_or_expr(self, stream),
101 AstNode::MapType(_) => v1::decl::format_map_type(self, stream),
102 AstNode::MetadataArray(_) => v1::meta::format_metadata_array(self, stream),
103 AstNode::MetadataObject(_) => v1::meta::format_metadata_object(self, stream),
104 AstNode::MetadataObjectItem(_) => {
105 v1::meta::format_metadata_object_item(self, stream)
106 }
107 AstNode::MetadataSection(_) => v1::meta::format_metadata_section(self, stream),
108 AstNode::ModuloExpr(_) => v1::expr::format_modulo_expr(self, stream),
109 AstNode::MultiplicationExpr(_) => {
110 v1::expr::format_multiplication_expr(self, stream)
111 }
112 AstNode::NameRefExpr(_) => v1::expr::format_name_ref_expr(self, stream),
113 AstNode::NegationExpr(_) => v1::expr::format_negation_expr(self, stream),
114 AstNode::OutputSection(_) => v1::format_output_section(self, stream),
115 AstNode::PairType(_) => v1::decl::format_pair_type(self, stream),
116 AstNode::ObjectType(_) => v1::decl::format_object_type(self, stream),
117 AstNode::ParameterMetadataSection(_) => {
118 v1::meta::format_parameter_metadata_section(self, stream)
119 }
120 AstNode::ParenthesizedExpr(_) => v1::expr::format_parenthesized_expr(self, stream),
121 AstNode::Placeholder(_) => v1::expr::format_placeholder(self, stream),
122 AstNode::PrimitiveType(_) => v1::decl::format_primitive_type(self, stream),
123 AstNode::RequirementsItem(_) => v1::task::format_requirements_item(self, stream),
124 AstNode::RequirementsSection(_) => {
125 v1::task::format_requirements_section(self, stream)
126 }
127 AstNode::RuntimeItem(_) => v1::task::format_runtime_item(self, stream),
128 AstNode::RuntimeSection(_) => v1::task::format_runtime_section(self, stream),
129 AstNode::ScatterStatement(_) => {
130 v1::workflow::format_scatter_statement(self, stream)
131 }
132 AstNode::SepOption(_) => v1::expr::format_sep_option(self, stream),
133 AstNode::StructDefinition(_) => {
134 v1::r#struct::format_struct_definition(self, stream)
135 }
136 AstNode::SubtractionExpr(_) => v1::expr::format_subtraction_expr(self, stream),
137 AstNode::TaskDefinition(_) => v1::task::format_task_definition(self, stream),
138 AstNode::TaskHintsItem(_) => v1::task::format_task_hints_item(self, stream),
139 AstNode::TaskHintsSection(_) => v1::task::format_task_hints_section(self, stream),
140 AstNode::TrueFalseOption(_) => v1::expr::format_true_false_option(self, stream),
141 AstNode::TypeRef(_) => v1::decl::format_type_ref(self, stream),
142 AstNode::UnboundDecl(_) => v1::decl::format_unbound_decl(self, stream),
143 AstNode::VersionStatement(_) => v1::format_version_statement(self, stream),
144 AstNode::WorkflowDefinition(_) => {
145 v1::workflow::format_workflow_definition(self, stream)
146 }
147 AstNode::WorkflowHintsArray(_) => {
148 v1::workflow::format_workflow_hints_array(self, stream)
149 }
150 AstNode::WorkflowHintsItem(_) => {
151 v1::workflow::format_workflow_hints_item(self, stream)
152 }
153 AstNode::WorkflowHintsObject(_) => {
154 v1::workflow::format_workflow_hints_object(self, stream)
155 }
156 AstNode::WorkflowHintsObjectItem(_) => {
157 v1::workflow::format_workflow_hints_object_item(self, stream)
158 }
159 AstNode::WorkflowHintsSection(_) => {
160 v1::workflow::format_workflow_hints_section(self, stream)
161 }
162 },
163 Element::Token(token) => {
164 stream.push_ast_token(token);
165 }
166 }
167 }
168}
169
170#[derive(Debug, Default)]
172pub struct Formatter {
173 config: Config,
175}
176
177impl Formatter {
178 pub fn new(config: Config) -> Self {
180 Self { config }
181 }
182
183 pub fn config(&self) -> &Config {
185 &self.config
186 }
187
188 pub fn format<W: Writable>(&self, element: W) -> std::result::Result<String, std::fmt::Error> {
190 let mut result = String::new();
191
192 for token in self.to_stream(element) {
193 write!(result, "{token}", token = token.display(self.config()))?;
194 }
195
196 Ok(result)
197 }
198
199 fn to_stream<W: Writable>(&self, element: W) -> TokenStream<PostToken> {
201 let mut stream = TokenStream::default();
202 element.write(&mut stream);
203
204 let mut postprocessor = Postprocessor::default();
205 postprocessor.run(stream, self.config())
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use wdl_ast::Document;
212 use wdl_ast::Node;
213
214 use crate::Formatter;
215 use crate::element::node::AstNodeFormatExt as _;
216
217 #[test]
218 fn smoke() {
219 let (document, diagnostics) = Document::parse(
220 "## WDL
221version 1.2 # This is a comment attached to the version.
222
223# This is a comment attached to the task keyword.
224task foo # This is an inline comment on the task ident.
225{
226
227} # This is an inline comment on the task close brace.
228
229# This is a comment attached to the workflow keyword.
230workflow bar # This is an inline comment on the workflow ident.
231{
232 # This is attached to the call keyword.
233 call foo {}
234} # This is an inline comment on the workflow close brace.",
235 );
236
237 assert!(diagnostics.is_empty());
238 let document = Node::Ast(document.ast().into_v1().unwrap()).into_format_element();
239 let formatter = Formatter::default();
240 let result = formatter.format(&document);
241 match result {
242 Ok(s) => {
243 print!("{s}");
244 }
245 Err(err) => {
246 panic!("failed to format document: {err}");
247 }
248 }
249 }
250}