#![allow(dead_code)]
use std::{
collections::HashMap,
fs::File,
io::{prelude::*, BufReader},
path::Path,
process::Command,
};
#[derive(Debug)]
pub enum NodeType {
Assign(String, String),
Command(Vec<String>),
Comment(String),
Space,
}
#[derive(Debug)]
pub struct ParseNode {
node_type: NodeType,
line: usize,
}
impl ParseNode {
pub fn new(node_type: NodeType, line: usize) -> Self {
Self { node_type, line }
}
}
#[derive(Debug)]
pub struct Parser {
nodes: Vec<ParseNode>,
pub assign_map: HashMap<String, String>,
}
impl Parser {
pub fn load<P: AsRef<Path>>(file_path: P) -> Self {
let file = File::open(file_path);
if let Ok(file) = file {
let codes = BufReader::new(file)
.lines()
.map(|line| line.unwrap().trim().to_string())
.collect();
Self::parse_lines(codes)
} else {
println!("Load file error: {:?}", file.err().unwrap());
std::process::exit(0);
}
}
pub fn parse_line(s: &String) -> NodeType {
if s.len() == 0 {
return NodeType::Space;
}
if s.starts_with("#") {
return NodeType::Comment(s[1..].trim().to_string());
}
let s: Vec<&str> = s.split(" ").filter(|s| s.len() > 0).collect();
if s.len() >= 3 && s[0].starts_with("$") && s[1] == "=" {
return NodeType::Assign(s[0].to_string(), s[2..].join(" "));
}
NodeType::Command(s.into_iter().map(|s| s.to_string()).collect())
}
pub fn parse_lines(codes: Vec<String>) -> Self {
let mut nodes = Vec::new();
let mut assign_map = HashMap::new();
for (line, code) in codes.into_iter().enumerate() {
match Self::parse_line(&code) {
node @ NodeType::Space => {
nodes.push(ParseNode::new(node, line + 1));
continue;
}
node @ NodeType::Comment(_) => {
nodes.push(ParseNode::new(node, line + 1));
continue;
}
node @ NodeType::Command(_) => {
nodes.push(ParseNode::new(node, line + 1));
continue;
}
NodeType::Assign(k, v) => {
assign_map.insert(k.to_string(), v.to_string());
nodes.push(ParseNode::new(NodeType::Assign(k, v), line + 1));
continue;
}
}
}
Self { nodes, assign_map }
}
pub fn run_line(assign_map: &HashMap<String, String>, node: &NodeType, line: usize) {
match node {
NodeType::Command(ref v) => {
let mut cmd = v[0].to_string();
let mut command = Command::new(&v[0]);
if v.len() > 1 {
for s in &v[1..] {
let mut arg = s;
if s.starts_with("$") && assign_map.get(s).is_some() {
arg = assign_map.get(s).unwrap();
}
cmd.push_str(format!(" {}", arg).as_str());
command.arg(arg);
}
}
println!("Running {:?} at line {}...", cmd, line);
let output = command
.output()
.expect(&format!("{:?} causes an error at line[{}]", cmd, line));
println!("{}", String::from_utf8(output.stdout).unwrap());
}
_ => {}
}
}
pub fn run(&self) {
for node in &self.nodes {
Self::run_line(&self.assign_map, &node.node_type, node.line);
}
}
}