use std::rc::Rc;
use tracing::warn;
use wdl_ast::SyntaxKind;
use wdl_ast::v1::StrippedCommandPart;
use crate::Config;
use crate::PreToken;
use crate::TokenStream;
use crate::Trivia;
use crate::Writable as _;
use crate::element::FormatElement;
use crate::v1::write_sections;
pub fn format_task_definition(
element: &FormatElement,
stream: &mut TokenStream<PreToken>,
config: &Config,
) {
let mut children = element.children().expect("task definition children");
stream.ignore_trailing_blank_lines();
let task_keyword = children.next().expect("task keyword");
assert_eq!(task_keyword.element().kind(), SyntaxKind::TaskKeyword);
(&task_keyword).write(stream, config);
stream.end_word();
let name = children.next().expect("task name");
assert_eq!(name.element().kind(), SyntaxKind::Ident);
(&name).write(stream, config);
stream.end_word();
let open_brace = children.next().expect("open brace");
assert_eq!(open_brace.element().kind(), SyntaxKind::OpenBrace);
(&open_brace).write(stream, config);
stream.end_line();
stream.increment_indent();
stream.end_line();
if config.reorder_sections {
let mut meta_sections = Vec::new();
let mut parameter_meta_sections = Vec::new();
let mut input_sections = Vec::new();
let mut body = Vec::new();
let mut command_sections = Vec::new();
let mut output_sections = Vec::new();
let mut requirements_or_runtime_sections = Vec::new();
let mut hints_sections = Vec::new();
let mut close_brace = None;
for child in children {
match child.element().kind() {
SyntaxKind::InputSectionNode => {
input_sections.push(child);
}
SyntaxKind::MetadataSectionNode => {
meta_sections.push(child);
}
SyntaxKind::ParameterMetadataSectionNode => {
parameter_meta_sections.push(child);
}
SyntaxKind::BoundDeclNode => {
body.push(child);
}
SyntaxKind::CommandSectionNode => {
command_sections.push(child);
}
SyntaxKind::OutputSectionNode => {
output_sections.push(child);
}
SyntaxKind::RequirementsSectionNode | SyntaxKind::RuntimeSectionNode => {
requirements_or_runtime_sections.push(child);
}
SyntaxKind::TaskHintsSectionNode => {
hints_sections.push(child);
}
SyntaxKind::CloseBrace => {
close_brace = Some(child);
}
_ => {
unreachable!(
"unexpected child in task definition: {:?}",
child.element().kind()
);
}
}
}
write_sections(&meta_sections, stream, config);
write_sections(¶meter_meta_sections, stream, config);
write_sections(&input_sections, stream, config);
stream.allow_blank_lines();
let body_empty = body.is_empty();
for child in body {
child.write(stream, config);
}
stream.ignore_trailing_blank_lines();
if !body_empty {
stream.blank_line();
}
write_sections(&command_sections, stream, config);
write_sections(&output_sections, stream, config);
write_sections(&requirements_or_runtime_sections, stream, config);
write_sections(&hints_sections, stream, config);
stream
.trim_while(|t| matches!(t, PreToken::BlankLine | PreToken::Trivia(Trivia::BlankLine)));
stream.decrement_indent();
stream.end_line();
close_brace.expect("task close brace").write(stream, config);
stream.end_line();
} else {
let mut first_written = false;
for child in children {
match child.element().kind() {
SyntaxKind::InputSectionNode
| SyntaxKind::MetadataSectionNode
| SyntaxKind::ParameterMetadataSectionNode
| SyntaxKind::CommandSectionNode
| SyntaxKind::OutputSectionNode
| SyntaxKind::RequirementsSectionNode
| SyntaxKind::RuntimeSectionNode
| SyntaxKind::TaskHintsSectionNode => {
if first_written {
stream.trim_while(|t| {
matches!(t, PreToken::BlankLine | PreToken::Trivia(Trivia::BlankLine))
});
stream.blank_line();
}
(&child).write(stream, config);
stream.blank_line();
}
SyntaxKind::BoundDeclNode => {
stream.allow_blank_lines();
(&child).write(stream, config);
stream.ignore_trailing_blank_lines();
}
SyntaxKind::CloseBrace => {
stream.trim_while(|t| {
matches!(t, PreToken::BlankLine | PreToken::Trivia(Trivia::BlankLine))
});
stream.decrement_indent();
stream.end_line();
(&child).write(stream, config);
stream.end_line();
}
_ => {
unreachable!(
"unexpected child in task definition: {:?}",
child.element().kind()
);
}
}
first_written = true;
}
}
}
pub fn format_command_section(
element: &FormatElement,
stream: &mut TokenStream<PreToken>,
config: &Config,
) {
let mut children = element.children().expect("command section children");
let command_keyword = children.next().expect("command keyword");
assert_eq!(command_keyword.element().kind(), SyntaxKind::CommandKeyword);
(&command_keyword).write(stream, config);
stream.end_word();
let open_delimiter = children.next().expect("open delimiter");
match open_delimiter.element().kind() {
SyntaxKind::OpenBrace if config.upgrade_deprecations => {
stream.push_literal_in_place_of_token(
open_delimiter
.element()
.as_token()
.expect("open brace should be token"),
"<<<".to_string(),
);
}
_ => {
(&open_delimiter).write(stream, config);
}
}
let parts = element
.element()
.as_node()
.expect("command section node")
.as_command_section()
.expect("command section")
.strip_whitespace();
match parts {
None => {
warn!(
"command section with mixed indentation: making a best-effort formatting attempt"
);
for child in children {
match child.element().kind() {
SyntaxKind::CloseBrace => {
stream.push_literal_in_place_of_token(
child
.element()
.as_token()
.expect("close brace should be token"),
">>>".to_string(),
);
}
SyntaxKind::CloseHeredoc => {
(&child).write(stream, config);
}
SyntaxKind::LiteralCommandText | SyntaxKind::PlaceholderNode => {
(&child).write(stream, config);
}
_ => {
unreachable!(
"unexpected child in command section: {:?}",
child.element().kind()
);
}
}
}
}
Some(parts) => {
stream.increment_indent();
stream.end_line();
let mut bash_indent: Option<Rc<String>> = None;
for (part, child) in parts.iter().zip(children.by_ref()) {
match part {
StrippedCommandPart::Text(text) => {
let mut lines = text.lines().enumerate().peekable();
while let Some((i, line)) = lines.next() {
if i > 0 {
bash_indent = None;
stream.end_line();
if lines.peek().is_none() {
bash_indent = Some(
line.chars()
.take_while(|c| matches!(c, ' ' | '\t'))
.collect::<String>()
.into(),
);
}
}
stream.push_literal(line.to_owned(), SyntaxKind::LiteralCommandText);
}
if text.ends_with('\n') {
bash_indent = None;
stream.end_line();
}
}
StrippedCommandPart::Placeholder(_) => {
if let Some(ref temp_indent) = bash_indent
&& !temp_indent.is_empty()
{
stream.push(PreToken::TempIndentStart(temp_indent.clone()));
(&child).write(stream, config);
stream.push(PreToken::TempIndentEnd);
} else {
(&child).write(stream, config);
}
}
}
}
stream.decrement_indent();
stream.end_line();
for child in children {
match child.element().kind() {
SyntaxKind::CloseBrace if config.upgrade_deprecations => {
stream.push_literal_in_place_of_token(
child
.element()
.as_token()
.expect("close brace should be token"),
">>>".to_string(),
);
}
_ => {
(&child).write(stream, config);
}
}
}
}
}
stream.end_line();
}
pub fn format_requirements_item(
element: &FormatElement,
stream: &mut TokenStream<PreToken>,
config: &Config,
) {
let mut children = element.children().expect("requirements item children");
let name = children.next().expect("requirements item name");
assert_eq!(name.element().kind(), SyntaxKind::Ident);
(&name).write(stream, config);
let colon = children.next().expect("requirements item colon");
assert_eq!(colon.element().kind(), SyntaxKind::Colon);
(&colon).write(stream, config);
stream.end_word();
let value = children.next().expect("requirements item value");
(&value).write(stream, config);
}
pub fn format_requirements_section(
element: &FormatElement,
stream: &mut TokenStream<PreToken>,
config: &Config,
) {
let mut children = element.children().expect("requirements section children");
let requirements_keyword = children.next().expect("requirements keyword");
assert_eq!(
requirements_keyword.element().kind(),
SyntaxKind::RequirementsKeyword
);
(&requirements_keyword).write(stream, config);
stream.end_word();
let open_brace = children.next().expect("open brace");
assert_eq!(open_brace.element().kind(), SyntaxKind::OpenBrace);
(&open_brace).write(stream, config);
stream.increment_indent();
stream.end_line();
let mut items = Vec::new();
let mut close_brace = None;
for child in children {
match child.element().kind() {
SyntaxKind::RequirementsItemNode => {
items.push(child.clone());
}
SyntaxKind::CloseBrace => {
close_brace = Some(child.clone());
}
_ => {
unreachable!(
"unexpected child in requirements section: {:?}",
child.element().kind()
);
}
}
}
for item in items {
(&item).write(stream, config);
stream.end_line();
}
stream.decrement_indent();
stream.end_line();
(&close_brace.expect("requirements close brace")).write(stream, config);
stream.end_line();
}
pub fn format_task_hints_item(
element: &FormatElement,
stream: &mut TokenStream<PreToken>,
config: &Config,
) {
let mut children = element.children().expect("task hints item children");
let name = children.next().expect("task hints item name");
assert_eq!(name.element().kind(), SyntaxKind::Ident);
(&name).write(stream, config);
let colon = children.next().expect("task hints item colon");
assert_eq!(colon.element().kind(), SyntaxKind::Colon);
(&colon).write(stream, config);
stream.end_word();
let value = children.next().expect("task hints item value");
(&value).write(stream, config);
assert!(children.next().is_none());
}
pub fn format_runtime_item(
element: &FormatElement,
stream: &mut TokenStream<PreToken>,
config: &Config,
) {
let mut children = element.children().expect("runtime item children");
let name = children.next().expect("runtime item name");
assert_eq!(name.element().kind(), SyntaxKind::Ident);
(&name).write(stream, config);
let colon = children.next().expect("runtime item colon");
assert_eq!(colon.element().kind(), SyntaxKind::Colon);
(&colon).write(stream, config);
stream.end_word();
let value = children.next().expect("runtime item value");
(&value).write(stream, config);
}
pub fn format_runtime_section(
element: &FormatElement,
stream: &mut TokenStream<PreToken>,
config: &Config,
) {
let mut children = element.children().expect("runtime section children");
let runtime_keyword = children.next().expect("runtime keyword");
assert_eq!(runtime_keyword.element().kind(), SyntaxKind::RuntimeKeyword);
(&runtime_keyword).write(stream, config);
stream.end_word();
let open_brace = children.next().expect("open brace");
assert_eq!(open_brace.element().kind(), SyntaxKind::OpenBrace);
(&open_brace).write(stream, config);
stream.increment_indent();
stream.end_line();
let mut items = Vec::new();
let mut close_brace = None;
for child in children {
match child.element().kind() {
SyntaxKind::RuntimeItemNode => {
items.push(child.clone());
}
SyntaxKind::CloseBrace => {
close_brace = Some(child.clone());
}
_ => {
unreachable!(
"unexpected child in runtime section: {:?}",
child.element().kind()
);
}
}
}
for item in items {
(&item).write(stream, config);
stream.end_line();
}
stream.decrement_indent();
stream.end_line();
(&close_brace.expect("runtime close brace")).write(stream, config);
stream.end_line();
}
pub fn format_task_hints_section(
element: &FormatElement,
stream: &mut TokenStream<PreToken>,
config: &Config,
) {
let mut children = element.children().expect("task hints section children");
let hints_keyword = children.next().expect("hints keyword");
assert_eq!(hints_keyword.element().kind(), SyntaxKind::HintsKeyword);
(&hints_keyword).write(stream, config);
stream.end_word();
let open_brace = children.next().expect("open brace");
assert_eq!(open_brace.element().kind(), SyntaxKind::OpenBrace);
(&open_brace).write(stream, config);
stream.increment_indent();
stream.end_line();
let mut items = Vec::new();
let mut close_brace = None;
for child in children {
match child.element().kind() {
SyntaxKind::TaskHintsItemNode => {
items.push(child.clone());
}
SyntaxKind::CloseBrace => {
close_brace = Some(child.clone());
}
_ => {
unreachable!(
"unexpected child in task hints section: {:?}",
child.element().kind()
);
}
}
}
for item in items {
(&item).write(stream, config);
stream.end_line();
}
stream.decrement_indent();
stream.end_line();
(&close_brace.expect("task hints close brace")).write(stream, config);
stream.end_line();
}