use super::types::Redirection;
use tree_sitter::Node;
fn parse_fd(s: &str) -> Option<u32> {
s.parse().ok()
}
fn check_file_redirect(node: Node, source: &[u8]) -> Option<Redirection> {
let mut fd_text: Option<String> = None;
let mut operator = "";
let mut dest = String::new();
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "file_descriptor" {
fd_text = child.utf8_text(source).ok().map(str::to_string);
} else if child.is_named() {
dest = child.utf8_text(source).unwrap_or("").to_string();
} else {
let k = child.kind();
if matches!(
k,
">" | ">>"
| ">|"
| "&>"
| "&>>"
| ">&"
| "<"
| "<>"
| "<<<"
| "<<"
| "<<-"
| "<&"
| ">&-"
| "<&-"
) {
operator = k;
}
}
}
let fd = fd_text.as_deref().and_then(parse_fd);
if operator == "<>" {
return Some(Redirection {
operator: "<>",
fd,
target: dest,
});
}
if matches!(
operator,
"" | "<" | "<<<" | "<<" | "<<-" | "<&" | ">&-" | "<&-"
) {
if operator == "<" {
let text = node.utf8_text(source).unwrap_or("");
if text.contains("<>") {
return Some(Redirection {
operator: "<>",
fd,
target: dest,
});
}
}
return None;
}
if matches!(operator, "&>" | "&>>") {
if dest == "/dev/null" {
return None;
}
let op: &'static str = if operator == "&>" { "&>" } else { "&>>" };
return Some(Redirection {
operator: op,
fd,
target: dest,
});
}
if operator == ">&" {
if matches!(dest.as_str(), "0" | "1" | "2") && fd_text.is_none() {
return None;
}
if fd_text.is_some() && matches!(dest.as_str(), "0" | "1" | "2") {
return None;
}
return Some(Redirection {
operator: ">&",
fd,
target: dest,
});
}
if matches!(operator, ">" | ">>" | ">|") {
if dest == "/dev/null" {
return None;
}
let op: &'static str = match operator {
">>" => ">>",
">|" => ">|",
_ => ">",
};
return Some(Redirection {
operator: op,
fd,
target: dest,
});
}
None
}
pub(super) fn detect_redirections(node: Node, source: &[u8]) -> Option<Redirection> {
if node.kind() == "file_redirect" {
return check_file_redirect(node, source);
}
if node.kind() == "heredoc_body" {
return None;
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if let Some(r) = detect_redirections(child, source) {
return Some(r);
}
}
None
}