use clap::{Parser, Subcommand};
use std::{
fs::File,
io::{stdin, BufRead, BufReader, Write},
path::PathBuf,
};
use sysexits::ExitCode;
#[derive(Subcommand)]
pub enum Action {
Rs2md {
#[arg(long = "inner")]
extract_inner: Option<bool>,
#[arg(long = "outer")]
extract_outer: Option<bool>,
#[arg(short = 'i')]
input_files: Vec<PathBuf>,
#[arg(short = 'o')]
output_file: Option<PathBuf>,
},
}
impl Action {
fn rs2md(
extract_inner: Option<bool>,
extract_outer: Option<bool>,
input_files: &Vec<PathBuf>,
output_file: &Option<PathBuf>,
) -> ExitCode {
let mut lines = Vec::<u8>::new();
if input_files.is_empty() {
loop {
let mut line = String::new();
match stdin().read_line(&mut line) {
Ok(0) => {
break;
}
Ok(_) => lines.append(&mut line.as_bytes().to_vec()),
Err(error) => {
eprintln!("{error}");
return ExitCode::IoErr;
}
}
}
} else {
for file in input_files {
match File::open(file) {
Ok(file) => match BufReader::new(file).fill_buf() {
Ok(string) => {
lines.append(&mut string.to_vec());
}
Err(error) => {
eprintln!("{error}");
return ExitCode::IoErr;
}
},
Err(error) => {
eprintln!("{error}");
return ExitCode::NoInput;
}
}
}
}
match String::from_utf8(lines) {
Ok(lines) => {
let lines = lines
.lines()
.map(str::trim_start)
.filter(|l| {
(extract_inner.unwrap_or(false) && l.starts_with("///"))
|| (extract_outer.unwrap_or(false) && l.starts_with("//!"))
})
.map(|l| {
String::from(l.chars().skip(4).collect::<String>().trim_end())
+ "\n"
})
.collect::<String>();
output_file.as_ref().map_or_else(
|| {
print!("{lines}");
ExitCode::Ok
},
|path| match File::create(path) {
Ok(mut file) => match file.write(lines.as_bytes()) {
Ok(_) => ExitCode::Ok,
Err(error) => {
eprintln!("{error}");
ExitCode::IoErr
}
},
Err(error) => {
eprintln!("{error}");
ExitCode::CantCreat
}
},
)
}
Err(error) => {
eprintln!("{error}");
ExitCode::DataErr
}
}
}
#[must_use]
pub fn run(&self) -> ExitCode {
match self {
Self::Rs2md {
extract_inner,
extract_outer,
input_files,
output_file,
} => {
Self::rs2md(*extract_inner, *extract_outer, input_files, output_file)
}
}
}
}
#[derive(Parser)]
#[clap(about, version)]
pub struct Clap {
#[clap(subcommand)]
action: Action,
}
impl Clap {
#[must_use]
pub const fn action(&self) -> &Action {
&self.action
}
}