use crate::common::*;
use clap::{self, arg, ArgAction, Command};
use std::error::Error;
use std::{fs, io::prelude::*};
impl State {
pub fn run_cli(&mut self) -> Result<(), Box<dyn Error>> {
let matches = Command::new("autosway")
.about("System automation program")
.arg(arg!(-v --verbose "Set verbose mode").action(ArgAction::SetTrue))
.subcommand_required(true)
.subcommand(Command::new("list").about("List knifes"))
.subcommand(Command::new("api").about("Display API documentation as HTML in browser"))
.subcommand(Command::new("doc").about("Display documentation as HTML in browser"))
.subcommand(Command::new("repl").about("Run a Lua REPL"))
.subcommand(
Command::new("run")
.about("Run a knife")
.arg(arg!(<KNIFE> "The knife to run"))
.arg_required_else_help(true),
)
.subcommand(
Command::new("script")
.about("Run a Lua source file")
.arg(arg!(<FILE> "The script to run"))
.arg_required_else_help(true),
)
.get_matches();
self.options.verbose = matches.get_flag("verbose");
let cfg_dir = if let Some(p) = get_config_dir(ConfigDir::RootDir) {
p
} else {
log::log!([fatal] "Failed to get config directory");
};
fs::create_dir_all(&cfg_dir)?;
match matches.subcommand() {
Some(("list", _)) => {
self.knifes.par_iter().for_each(|knife| {
if !self.options.verbose {
println!("{}: {}", knife.name, knife.path.display());
} else {
println!("{}: {} - {}", knife.name, knife.path.display(), knife.init);
}
});
}
Some(("api", _)) => {
let html = markdown::to_html(include_str!("../API.md"));
if self.options.verbose {
println!("{}", &html);
}
let path = std::env::temp_dir().join("autosway_api.html");
let mut file = fs::File::create(&path)?;
file.write_all(html.as_bytes())?;
open::that(&path)?;
}
Some(("doc", _)) => {
let html = markdown::to_html(include_str!("../DOC.md"));
if self.options.verbose {
println!("{}", &html);
}
let path = std::env::temp_dir().join("autosway_doc.html");
let mut file = fs::File::create(&path)?;
file.write_all(html.as_bytes())?;
open::that(&path)?;
}
Some(("run", sm)) => {
let s = match sm.get_one::<String>("KNIFE") {
Some(s) => s,
None => {
log::log!([fatal] "No argument supplied");
}
};
if let Err(e) = self.run_knife(s) {
log::log!([error] "Failed to run knife: {e}");
}
}
Some(("script", sm)) => {
let s = match sm.get_one::<String>("FILE") {
Some(s) => s,
None => {
log::log!([fatal] "No argument supplied");
}
};
let mut file = fs::File::open(s)?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
if let Err(e) = self.lua.load(buf).exec() {
log::log!([error] "Failed to run script: {e}");
}
}
Some(("repl", _)) => {
use rustyline::{error::ReadlineError, DefaultEditor};
let history_path = &cfg_dir.join("repl_history");
let mut rl = DefaultEditor::new()?;
let _ = rl.load_history(&history_path);
'repl: loop {
match rl.readline("lua> ") {
Ok(line) => {
rl.add_history_entry(line.as_str())?;
if let Err(e) = self.lua.load(line).exec() {
log::log!([error] "Failed to run code: {e}");
}
}
Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break 'repl,
Err(e) => {
log::log!([error] "Readline error: {e}");
}
}
}
rl.save_history(&history_path)?;
}
_ => {}
}
Ok(())
}
}