cmd_runner/
parser.rs

1//! 分析命令行文件语法格式
2
3#![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/// 单行命令行语法类型
14#[derive(Debug)]
15pub enum NodeType {
16    /// 赋值语句
17    Assign(String, String),
18    /// 命令语句
19    Command(Vec<String>),
20    /// 注释
21    Comment(String),
22    /// 空行
23    Space,
24}
25
26/// 语法节点
27#[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/// 语法树
40#[derive(Debug)]
41pub struct Parser {
42    nodes: Vec<ParseNode>,
43    pub assign_map: HashMap<String, String>,
44}
45
46impl Parser {
47    /// 从文件中获取每行代码 [去除空格]
48    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}