#![deny(unused_must_use)]
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
use clap::{Parser, arg};
use std::error::Error;
use std::fs::File;
use std::io::{ErrorKind, IsTerminal};
use std::io::{Read, Write};
use std::path::PathBuf;
use std::process::ExitCode;
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = include_str!("long-about.txt"))]
pub struct TwasArgs {
#[arg[short='i', long="include"]]
includes: Vec<PathBuf>,
#[arg(short = 's', long = "seed")]
seed: Option<u64>,
#[arg[short='o', long="output"]]
output: Option<PathBuf>,
#[arg[short='f', long="file"]]
input: Vec<PathBuf>,
pub target_text: Vec<String>,
}
pub fn main() -> ExitCode {
let args = TwasArgs::parse();
match run(args) {
Ok(_) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("Failed due to the following error:\n{}", e);
ExitCode::FAILURE
},
}
}
pub fn run(args: TwasArgs) -> Result<(), Box<dyn Error>> {
let mut inst = match args.seed {
None => twas::Interpreter::new(),
Some(seed) => twas::Interpreter::from_seed(seed),
};
for inc in args.includes {
inst.load_file(inc)?
}
let stdin = std::io::stdin();
let mut targets = args.target_text;
for filepath in args.input {
targets.push(std::fs::read_to_string(filepath)?);
}
if !stdin.is_terminal() {
targets.push(read_stdin(&stdin)?)
}
let mut fout: Option<File> = match args.output {
None => None,
Some(outfile) => Some(File::create(outfile)?),
};
for target in targets {
let result = inst.eval(target.as_str())?;
println!("{}", result);
println!();
match &mut fout {
Some(f) => {
write!(f, "{}\n\n", result)?;
},
None => {},
}
}
Ok(())
}
fn read_stdin(stdin: &std::io::Stdin) -> Result<String, std::io::Error> {
let mut input = Vec::new();
let mut handle = stdin.lock();
handle.read_to_end(&mut input)?;
String::from_utf8(input).map_err(|utf_err| std::io::Error::new(ErrorKind::InvalidData, utf_err))
}