1#![allow(dead_code)]
4
5use std::{
6 collections::HashMap,
7 fs::File,
8 io::{prelude::*, BufReader},
9 path::Path,
10 process::Command,
11};
12
13#[derive(Debug)]
15pub enum NodeType {
16 Assign(String, String),
18 Command(Vec<String>),
20 Comment(String),
22 Space,
24}
25
26#[derive(Debug)]
28pub struct ParseNode {
29 node_type: NodeType,
30 line: usize,
31}
32
33impl ParseNode {
34 pub fn new(node_type: NodeType, line: usize) -> Self {
35 Self { node_type, line }
36 }
37}
38
39#[derive(Debug)]
41pub struct Parser {
42 nodes: Vec<ParseNode>,
43 pub assign_map: HashMap<String, String>,
44}
45
46impl Parser {
47 pub fn load<P: AsRef<Path>>(file_path: P) -> Self {
49 let file = File::open(file_path);
50
51 if let Ok(file) = file {
52 let codes = BufReader::new(file)
53 .lines()
54 .map(|line| line.unwrap().trim().to_string())
55 .collect();
56 Self::parse_lines(codes)
57 } else {
58 println!("Load file error: {:?}", file.err().unwrap());
59 std::process::exit(0);
60 }
61 }
62
63 pub fn parse_line(s: &String) -> NodeType {
64 if s.len() == 0 {
65 return NodeType::Space;
66 }
67
68 if s.starts_with("#") {
69 return NodeType::Comment(s[1..].trim().to_string());
70 }
71
72 let s: Vec<&str> = s.split(" ").filter(|s| s.len() > 0).collect();
73
74 if s.len() >= 3 && s[0].starts_with("$") && s[1] == "=" {
75 return NodeType::Assign(s[0].to_string(), s[2..].join(" "));
76 }
77
78 NodeType::Command(s.into_iter().map(|s| s.to_string()).collect())
79 }
80
81 pub fn parse_lines(codes: Vec<String>) -> Self {
82 let mut nodes = Vec::new();
83 let mut assign_map = HashMap::new();
84 for (line, code) in codes.into_iter().enumerate() {
85 match Self::parse_line(&code) {
86 node @ NodeType::Space => {
87 nodes.push(ParseNode::new(node, line + 1));
88 continue;
89 }
90
91 node @ NodeType::Comment(_) => {
92 nodes.push(ParseNode::new(node, line + 1));
93 continue;
94 }
95
96 node @ NodeType::Command(_) => {
97 nodes.push(ParseNode::new(node, line + 1));
98 continue;
99 }
100
101 NodeType::Assign(k, v) => {
102 assign_map.insert(k.to_string(), v.to_string());
103 nodes.push(ParseNode::new(NodeType::Assign(k, v), line + 1));
104 continue;
105 }
106 }
107 }
108
109 Self { nodes, assign_map }
110 }
111
112 pub fn run_line(assign_map: &HashMap<String, String>, node: &NodeType, line: usize) {
113 match node {
114 NodeType::Command(ref v) => {
115 let mut cmd = v[0].to_string();
116 let mut command = Command::new(&v[0]);
117 if v.len() > 1 {
118 for s in &v[1..] {
119 let mut arg = s;
120 if s.starts_with("$") && assign_map.get(s).is_some() {
121 arg = assign_map.get(s).unwrap();
122 }
123 cmd.push_str(format!(" {}", arg).as_str());
124 command.arg(arg);
125 }
126 }
127
128 println!("Running {:?} at line {}...", cmd, line);
129 let output = command
130 .output()
131 .expect(&format!("{:?} causes an error at line[{}]", cmd, line));
132
133 println!("{}", String::from_utf8(output.stdout).unwrap());
134 }
135 _ => {}
136 }
137 }
138
139 pub fn run(&self) {
140 for node in &self.nodes {
141 Self::run_line(&self.assign_map, &node.node_type, node.line);
142 }
143 }
144}