1extern crate ansi_term;
28extern crate nix;
29
30mod props;
32mod imiop;
33
34use ansi_term::Colour;
35use std::path::{Path, PathBuf};
36use std::thread::sleep;
37use std::time::{Duration};
38
39use crate::config;
41use props::RuntimeProps;
43use crate::shell::{Shell, ShellState};
45use crate::shell::unixsignal::UnixSignal;
46use crate::translator::ioprocessor::IOProcessor;
48use crate::translator::lang::Language;
49use crate::translator::new_translator;
50use crate::utils::console;
52use crate::utils::file;
53
54pub fn run_interactive(language: Language, config: config::Config, shell: Option<String>, history_file: Option<PathBuf>) -> u8 {
61 let mut props: RuntimeProps = RuntimeProps::new(true, config, language);
63 let processor: IOProcessor = IOProcessor::new(language, new_translator(language));
64 let (shell, args): (String, Vec<String>) = resolve_shell(&props.config, shell);
66 let mut shell: Shell = match Shell::start(shell, args, &props.config.prompt_config) {
68 Ok(sh) => sh,
69 Err(err) => {
70 print_err(
71 String::from(format!("Could not start shell: {}", err)),
72 props.config.output_config.translate_output,
73 &processor,
74 );
75 return 255;
76 }
77 };
78 if let Some(history_file) = history_file.clone() {
80 match file::read_lines(history_file.clone()) {
81 Ok(lines) => shell.history.load(lines),
82 Err(err) => print_err(
83 String::from(format!("Could not load history from '{}': {}", history_file.display(), err)),
84 props.config.output_config.translate_output,
85 &processor,
86 )
87 }
88 };
89 while props.get_last_state() != ShellState::Terminated {
91 let current_state: ShellState = shell.get_state();
93 if current_state != props.get_last_state() {
94 props.update_state(current_state);
95 }
96 if props.get_state_changed() && current_state == ShellState::Shell {
97 shell.refresh_env();
99 console::print(format!("{} ", shell.get_promptline(&processor)));
101 props.report_state_changed_notified(); } else if props.get_state_changed() {
103 props.report_state_changed_notified(); }
105 if let Some(ev) = console::read() {
107 props.handle_input_event(ev, &mut shell);
108 };
109 let new_state = shell.get_state(); if new_state != props.get_last_state() {
112 props.update_state(new_state);
113 }
114 read_from_shell(&mut shell, &props.config, &processor);
116 sleep(Duration::from_nanos(100)); } if let Some(history_file) = history_file {
121 let lines: Vec<String> = shell.history.dump();
122 if let Err(err) = file::write_lines(history_file.clone(), lines) {
123 print_err(
124 String::from(format!("Could not write history to '{}': {}", history_file.display(), err)),
125 props.config.output_config.translate_output,
126 &processor,
127 );
128 }
129 };
130 match shell.stop() {
132 Ok(rc) => rc,
133 Err(err) => {
134 print_err(format!("Could not stop shell: {}", err), props.config.output_config.translate_output, &processor);
135 255
136 }
137 }
138}
139
140pub fn run_command(mut command: String, language: Language, config: config::Config, shell: Option<String>) -> u8 {
144 let mut props: RuntimeProps = RuntimeProps::new(false, config, language);
146 let processor: IOProcessor = IOProcessor::new(language, new_translator(language));
147 let (shell, args): (String, Vec<String>) = resolve_shell(&props.config, shell);
149 let mut shell: Shell = match Shell::start(shell, args, &props.config.prompt_config) {
151 Ok(sh) => sh,
152 Err(err) => {
153 print_err(
154 String::from(format!("Could not start shell: {}", err)),
155 props.config.output_config.translate_output,
156 &processor,
157 );
158 return 255;
159 }
160 };
161 while command.ends_with('\n') {
163 command.pop();
164 }
165 while command.ends_with(';') {
166 command.pop();
167 }
168 command.push_str("; exit $?\n");
170 if let Err(err) = shell.write(command) {
172 print_err(
173 String::from(format!("Could not start shell: {}", err)),
174 props.config.output_config.translate_output,
175 &processor,
176 );
177 return 255;
178 }
179 let _ = shell.write(String::from("\n"));
180 loop { if let Some(ev) = console::read() {
184 props.handle_input_event(ev, &mut shell);
185 };
186 read_from_shell(&mut shell, &props.config, &processor);
188 if shell.get_state() == ShellState::Terminated {
190 break;
191 }
192 sleep(Duration::from_nanos(100)); } match shell.stop() {
196 Ok(rc) => rc,
197 Err(err) => {
198 print_err(format!("Could not stop shell: {}", err), props.config.output_config.translate_output, &processor);
199 255
200 }
201 }
202}
203
204pub fn run_file(file: String, language: Language, config: config::Config, shell: Option<String>) -> u8 {
208 let file_path: &Path = Path::new(file.as_str());
209 let processor: IOProcessor = IOProcessor::new(language, new_translator(language));
210 let lines: Vec<String> = match file::read_lines(file_path) {
211 Ok(lines) => lines,
212 Err(_) => {
213 print_err(format!("{}: No such file or directory", file), config.output_config.translate_output, &processor);
214 return 255
215 }
216 };
217 let command: String = script_lines_to_string(&lines);
219 run_command(command, language, config, shell)
221}
222
223fn read_from_shell(shell: &mut Shell, config: &config::Config, processor: &IOProcessor) {
229 if let Ok((out, err)) = shell.read() {
230 if out.is_some() {
231 print_out(out.unwrap(), config.output_config.translate_output, &processor);
233 }
234 if err.is_some() {
235 print_err(err.unwrap().to_string(), config.output_config.translate_output, &processor);
237 }
238 }
239}
240
241fn resolve_shell(config: &config::Config, shellopt: Option<String>) -> (String, Vec<String>) {
245 match shellopt {
246 Some(sh) => (sh, vec![]),
247 None => (config.shell_config.exec.clone(), config.shell_config.args.clone()) }
249}
250
251fn script_lines_to_string(lines: &Vec<String>) -> String {
255 let mut command: String = String::new();
256 for line in lines.iter() {
257 if line.starts_with("#") {
258 continue;
259 }
260 if line.len() == 0 {
261 continue;
262 }
263 command.push_str(line);
264 if ! line.ends_with(";") {
266 command.push(';');
267 }
268 }
269 command
270}
271
272fn resolve_command(argv: &mut Vec<String>, config: &config::Config) {
277 match config.get_alias(&argv[0]) {
279 Some(resolved) => argv[0] = resolved,
280 None => {}
281 };
282}
283
284fn print_err(err: String, to_cyrillic: bool, processor: &IOProcessor) {
304 match to_cyrillic {
305 true => eprintln!("{}", Colour::Red.paint(processor.text_to_cyrillic(&err))),
306 false => eprintln!("{}", Colour::Red.paint(err)),
307 };
308}
309
310fn print_out(out: String, to_cyrillic: bool, processor: &IOProcessor) {
315 match to_cyrillic {
316 true => console::println(format!("{}", processor.text_to_cyrillic(&out))),
317 false => console::println(format!("{}", out)),
318 };
319}
320
321fn console_fmt(out: String, to_cyrillic: bool, processor: &IOProcessor) -> String {
326 match to_cyrillic {
327 true => format!("{}", processor.text_to_cyrillic(&out)),
328 false => format!("{}", out)
329 }
330}
331
332#[allow(dead_code)]
336fn shellsignal_to_signal(sig: u8) -> Option<UnixSignal> {
337 match sig {
338 3 => Some(UnixSignal::Sigint),
339 26 => Some(UnixSignal::Sigstop),
340 _ => None
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347
348 use crate::config::Config;
349
350 use crate::translator::ioprocessor::IOProcessor;
351 use crate::translator::new_translator;
352 use crate::translator::lang::Language;
353
354 use std::collections::HashMap;
355 use std::time::Duration;
356 use std::thread::sleep;
357
358 #[test]
359 fn test_runtime_read_from_shell() {
360 let mut cfg: Config = Config::default();
361 cfg.output_config.translate_output = true;
362 let iop: IOProcessor = IOProcessor::new(Language::Russian, new_translator(Language::Russian));
363 let mut shell: Shell = Shell::start(String::from("sh"), vec![], &cfg.prompt_config).unwrap();
364 sleep(Duration::from_millis(500)); let _ = shell.write(String::from("echo 4\n"));
367 sleep(Duration::from_millis(100));
368 read_from_shell(&mut shell, &cfg, &iop);
370 cfg.output_config.translate_output = false;
372 let _ = shell.write(String::from("echo 5\n"));
373 sleep(Duration::from_millis(100));
374 read_from_shell(&mut shell, &cfg, &iop);
375 cfg.output_config.translate_output = true;
377 let _ = shell.write(String::from("poropero\n"));
378 sleep(Duration::from_millis(100));
379 read_from_shell(&mut shell, &cfg, &iop);
380 cfg.output_config.translate_output = false;
382 let _ = shell.write(String::from("poropero\n"));
383 sleep(Duration::from_millis(100));
384 read_from_shell(&mut shell, &cfg, &iop);
385 sleep(Duration::from_millis(500)); assert!(shell.stop().is_ok());
388 sleep(Duration::from_millis(500)); }
390
391 #[test]
392 fn test_runtime_resolve_shell() {
393 let mut cfg: Config = Config::default();
394 cfg.shell_config.args = vec![String::from("-i")];
395 assert_eq!(resolve_shell(&cfg, None), (String::from("bash"), vec![String::from("-i")]));
397 assert_eq!(resolve_shell(&cfg, Some(String::from("fish"))), (String::from("fish"), vec![]));
399 }
400
401 #[test]
402 fn test_runtime_script_lines_to_command() {
403 let lines: Vec<String> = vec![String::from("#!/bin/bash"), String::from(""), String::from("echo 4"), String::from("#this is a comment"), String::from("cat /tmp/output;")];
404 assert_eq!(script_lines_to_string(&lines), String::from("echo 4;cat /tmp/output;"));
405 }
406
407 #[test]
408 fn test_runtime_resolve_command() {
409 let mut alias_cfg: HashMap<String, String> = HashMap::new();
410 alias_cfg.insert(String::from("ll"), String::from("ls -l"));
411 let cfg: Config = Config {
412 language: String::from(""),
413 shell_config: config::ShellConfig::default(),
414 alias: alias_cfg,
415 output_config: config::OutputConfig::default(),
416 prompt_config: config::PromptConfig::default()
417 };
418 let mut argv: Vec<String> = vec![String::from("ll"), String::from("/tmp/")];
420 resolve_command(&mut argv, &cfg);
421 assert_eq!(*argv.get(0).unwrap(), String::from("ls -l"));
422
423 let mut argv: Vec<String> = vec![String::from("du"), String::from("-hs")];
425 resolve_command(&mut argv, &cfg);
426 assert_eq!(*argv.get(0).unwrap(), String::from("du"));
427 }
428
429 #[test]
430 fn test_runtime_print() {
431 let iop: IOProcessor = IOProcessor::new(Language::Russian, new_translator(Language::Russian));
432 print_out(String::from("Hello"), true, &iop);
434 print_out(String::from("Hello"), false, &iop);
435 print_err(String::from("Hello"), true, &iop);
437 print_err(String::from("Hello"), false, &iop);
438 }
439
440 #[test]
441 fn test_runtime_console_fmt() {
442 let iop: IOProcessor = IOProcessor::new(Language::Russian, new_translator(Language::Russian));
443 assert_eq!(console_fmt(String::from("Hello"), true, &iop), String::from("Хелло"));
445 assert_eq!(console_fmt(String::from("Hello"), false, &iop), String::from("Hello"));
446 }
447
448 #[test]
449 fn test_runtime_shellsignal() {
450 assert_eq!(shellsignal_to_signal(3).unwrap(), UnixSignal::Sigint);
451 assert_eq!(shellsignal_to_signal(26).unwrap(), UnixSignal::Sigstop);
452 assert!(shellsignal_to_signal(255).is_none());
453 }
454
455}