1use rable::Node;
2
3use crate::error::RippyError;
4
5pub struct BashParser;
7
8impl BashParser {
9 pub const fn new() -> Result<Self, RippyError> {
15 Ok(Self)
16 }
17
18 pub fn parse(&mut self, source: &str) -> Result<Vec<Node>, RippyError> {
24 rable::parse(source, false).map_err(|e| RippyError::Parse(format!("parse error: {e}")))
25 }
26}
27
28#[cfg(test)]
29#[allow(clippy::unwrap_used)]
30mod tests {
31 use rable::NodeKind;
32
33 use super::*;
34
35 #[test]
36 fn parse_simple_command() {
37 let mut parser = BashParser::new().unwrap();
38 let nodes = parser.parse("echo hello").unwrap();
39 assert!(!nodes.is_empty());
40 assert!(matches!(nodes[0].kind, NodeKind::Command { .. }));
41 }
42
43 #[test]
44 fn parse_pipeline() {
45 let mut parser = BashParser::new().unwrap();
46 let nodes = parser.parse("cat file | grep pattern").unwrap();
47 assert!(matches!(nodes[0].kind, NodeKind::Pipeline { .. }));
48 }
49
50 #[test]
51 fn parse_list() {
52 let mut parser = BashParser::new().unwrap();
53 let nodes = parser.parse("cd /tmp && ls").unwrap();
54 assert!(matches!(nodes[0].kind, NodeKind::List { .. }));
55 }
56
57 #[test]
58 fn parse_redirect() {
59 let mut parser = BashParser::new().unwrap();
60 let nodes = parser.parse("echo foo > output.txt").unwrap();
61 assert!(
62 matches!(&nodes[0].kind, NodeKind::Command { redirects, .. } if !redirects.is_empty())
63 );
64 }
65
66 #[test]
67 fn parse_command_substitution() {
68 let mut parser = BashParser::new().unwrap();
69 let nodes = parser.parse("echo $(whoami)").unwrap();
70 assert!(!nodes.is_empty());
71 assert!(crate::ast::has_expansions(&nodes[0]));
72 }
73
74 #[test]
75 fn parse_if_statement() {
76 let mut parser = BashParser::new().unwrap();
77 let nodes = parser.parse("if true; then echo yes; fi").unwrap();
78 assert!(matches!(nodes[0].kind, NodeKind::If { .. }));
79 }
80
81 #[test]
82 fn parse_for_loop() {
83 let mut parser = BashParser::new().unwrap();
84 let nodes = parser.parse("for i in 1 2 3; do echo $i; done").unwrap();
85 assert!(matches!(nodes[0].kind, NodeKind::For { .. }));
86 }
87
88 #[test]
89 fn parse_subshell() {
90 let mut parser = BashParser::new().unwrap();
91 let nodes = parser.parse("(echo hello)").unwrap();
92 assert!(matches!(nodes[0].kind, NodeKind::Subshell { .. }));
93 }
94}