1pub mod token;
2pub mod parser;
3
4use std::{env, fs};
5use std::error::Error;
6use std::fs::read_to_string;
7use std::io::{stdin, stdout, Write};
8use std::path::Path;
9use std::process::{Child, Command, Stdio};
10use logos::{Logos, Lexer};
11use subst::substitute;
12use crate::parser::Parser;
13use crate::token::Token;
14
15
16pub fn run(config_file: Option<&str>) {
19 match config_file {
20 None => {
21 match fs::read("../../.clararc") {
22 Ok(_) => {
23 setup(".clararc").unwrap()
24 }
25 Err(_) => {
26 fs::write("../../.clararc", "").unwrap();
27 }
28 }
29 }
30 Some(file) => {
31 setup(file).unwrap();
32 }
33 }
34
35 loop {
36 print!("> ");
37 stdout().flush().expect("Error while pushing to stdout");
38
39 let mut input = String::new();
40 stdin().read_line(&mut input).unwrap();
41
42 let mut san_input = String::new();
43 for i in input.split(" ") {
44 if i.contains("$") {
45
46 match substitute(i, &subst::Env) {
47 Ok(sub) => {
48 san_input.push_str(sub.as_str());
49 san_input.push_str(" ");
50 }
51
52 Err(err) => {
53 println!("{}", err )
54 }
55 }
56
57 } else {
58 san_input.push_str(i);
59 san_input.push_str(" ");
60 }
61 }
62
63 let mut commands = san_input.trim().split(" | ").peekable();
65 let mut previous_command = None;
66
67 while let Some(command) = commands.next() {
68 let mut parts = command.trim().split_whitespace();
69
70 let command = match parts.next() {
71 Some(data) => data,
72 None => break,
73 };
74 let args = parts;
75
76 match command {
77 "cd" => {
78 let new_dir = args.peekable().peek().map_or("/", |x| *x);
79 let root = Path::new(new_dir);
80 if let Err(e) = env::set_current_dir(&root) {
81 eprintln!("{}", e);
82 }
83
84 previous_command = None;
85 }
86 "exit" => return,
87 command => {
88 let stdin = previous_command.map_or(Stdio::inherit(), |output: Child| {
89 Stdio::from(output.stdout.unwrap())
90 });
91
92 let stdout = if commands.peek().is_some() {
93 Stdio::piped()
96 } else {
97 Stdio::inherit()
100 };
101
102 let output = Command::new(command)
103 .args(args)
104 .stdin(stdin)
105 .stdout(stdout)
106 .spawn();
107
108 match output {
109 Ok(output) => {
110 previous_command = Some(output);
111 }
112 Err(e) => {
113 previous_command = None;
114 eprintln!("{}", e);
115 }
116 };
117 }
118 }
119 }
120
121 if let Some(mut final_command) = previous_command {
122 final_command.wait().expect(format!("Error while waiting for task {:?} with PID {}",
124 final_command,
125 final_command.id()).as_str());
126 }
127 }
128}
129
130
131fn setup(config_name: &str) -> Result<(), Box<dyn Error>> {
132 match read_to_string(config_name) {
133 Ok(data) => {
134 let lex: Lexer<Token> = Token::lexer(data.as_str());
135 let mut token_list = vec![];
136
137 for i in lex {
138 if i != Token::Error {
139 token_list.push(i);
140 }
141 }
142
143
144 let mut parser = Parser::new(token_list);
145 parser.run().expect("TODO: Error message");
146
147 Ok(())
148 }
149 Err(_) => {
150 Err(Box::from("Error: config file not found"))
151 }
152 }
153}
154
155
156#[cfg(test)]
157mod tests {
158 use serial_test::serial;
159 use std::fs::{remove_file, write};
160 use super::*;
161 #[serial(".clararc_temp")]
162 #[test]
163 fn test_empty_config() {
164 write(".clararc_temp", "").unwrap();
165 setup(".clararc_temp").unwrap();
166 remove_file(".clararc_temp").unwrap();
167 }
168
169 #[serial(".clararc_temp")]
170 #[test]
171 fn test_simple_config() {
172 write(".clararc_temp", "export TEST=\"test\"").unwrap();
173 setup(".clararc_temp").unwrap();
174 remove_file(".clararc_temp").unwrap();
175 }
176}