mod compound;
mod formatter;
mod lists;
mod nodes;
mod redirects;
mod words;
use std::cell::Cell;
use formatter::Formatter;
thread_local! {
static REFORMAT_DEPTH: Cell<usize> = const { Cell::new(0) };
}
struct DepthGuard;
impl DepthGuard {
fn enter() -> Option<Self> {
REFORMAT_DEPTH.with(|d| {
let v = d.get();
if v >= 2 {
return None;
}
d.set(v + 1);
Some(Self)
})
}
}
impl Drop for DepthGuard {
fn drop(&mut self) {
REFORMAT_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
}
}
pub fn reformat_bash(source: &str) -> Option<String> {
if source.is_empty() || source.len() > 1000 {
return None;
}
let _guard = DepthGuard::enter()?;
let dominated_by_words = source
.chars()
.all(|c| c.is_alphanumeric() || c == ' ' || c == '_' || c == '-' || c == '.' || c == '/');
if dominated_by_words {
return None;
}
let mut lexer = crate::lexer::Lexer::new(source, false);
lexer.set_mode(crate::lexer::LexerMode::Cmdsub);
let mut parser = crate::parser::Parser::new(lexer);
let nodes = parser.parse_all().ok()?;
if nodes.is_empty() {
return Some(String::new());
}
let mut f = Formatter::new();
for (i, node) in nodes.iter().enumerate() {
if i > 0 {
f.write_char('\n');
}
f.format_node(node);
}
Some(f.finish())
}