use std::fmt::{self, Display};
use std::fs;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use clap::Parser as ClapParser;
use yaml_rust::YamlLoader;
use crate::error::{self, VestiErr, VestiUtilErrKind};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LatexEngineType {
RawTexCode,
LaTeX,
PdfLaTeX,
XeLaTeX,
LuaLaTeX,
#[cfg(feature = "tectonic-backend")]
Tectonic,
Invalid,
}
impl FromStr for LatexEngineType {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"raw" => Self::RawTexCode,
"latex" => Self::LaTeX,
"pdflatex" => Self::PdfLaTeX,
"xelatex" => Self::XeLaTeX,
"lualatex" => Self::LuaLaTeX,
#[cfg(feature = "tectonic-backend")]
"tectonic" => Self::Tectonic,
_ => Self::Invalid,
})
}
}
impl Display for LatexEngineType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::LaTeX => write!(f, "latex"),
Self::PdfLaTeX => write!(f, "pdflatex"),
Self::XeLaTeX => write!(f, "xelatex"),
Self::LuaLaTeX => write!(f, "lualatex"),
#[cfg(feature = "tectonic-backend")]
Self::Tectonic => write!(f, "tectonic"),
_ => write!(f, ""),
}
}
}
#[derive(ClapParser)]
#[command(author, version, about)]
pub enum VestiOpt {
Init {
#[clap(name = "PROJECT_NAME")]
project_name: Option<String>,
},
Clear,
Compile {
#[clap(value_name = "FILE")]
file_name: Vec<PathBuf>,
#[clap(short, long)]
all: bool,
#[arg(short = 's', long = "has-sub")]
has_sub_vesti: bool,
#[arg(short = 'e', long = "emit-tex")]
emit_tex_only: bool,
#[arg(
short = 'L',
long = "latex",
conflicts_with_all(["is_pdflatex", "is_xelatex", "is_lualatex", "is_tectonic"]),
)]
is_latex: bool,
#[arg(
short = 'p',
long = "pdflatex",
conflicts_with_all(["is_latex", "is_xelatex", "is_lualatex", "is_tectonic"]),
)]
is_pdflatex: bool,
#[arg(
short = 'x',
long = "xelatex",
conflicts_with_all(["is_latex", "is_pdflatex", "is_lualatex", "is_tectonic"]),
)]
is_xelatex: bool,
#[arg(
short = 'l',
long = "lualatex",
conflicts_with_all(["is_latex", "is_pdflatex", "is_xelatex", "is_tectonic"]),
)]
is_lualatex: bool,
#[arg(
short = 'T',
long = "tectonic",
conflicts_with_all(["is_latex", "is_pdflatex", "is_xelatex", "is_lualatex"]),
)]
is_tectonic: bool,
#[arg(long = "lim")]
compile_limit: Option<usize>,
},
}
impl VestiOpt {
pub fn take_filename(&self) -> error::Result<Vec<PathBuf>> {
let mut output: Vec<PathBuf> = Vec::new();
if let Self::Compile {
all,
has_sub_vesti,
file_name,
..
} = self
{
if !all {
return Ok(file_name.clone());
}
if *all && !has_sub_vesti {
return Err(VestiErr::make_util_err(
VestiUtilErrKind::CompileAllWithoutHasSubVesti,
));
}
assert_eq!(file_name.len(), 1);
let file_dir = file_name[0].ancestors().nth(1);
let current_dir = if file_dir == Some(Path::new("")) {
Path::new(".").to_path_buf()
} else if let Some(path) = file_dir {
path.to_path_buf()
} else {
return Err(VestiErr::make_util_err(
VestiUtilErrKind::NoFilenameInputErr,
));
};
for path in walkdir::WalkDir::new(current_dir) {
match path {
Ok(dir) => {
if let Some(ext) = dir.path().extension() {
if ext == "ves" {
output.push(dir.into_path())
}
}
}
Err(_) => {
return Err(VestiErr::make_util_err(VestiUtilErrKind::TakeFilesErr));
}
}
}
output.sort();
}
Ok(output)
}
pub fn get_latex_type(&self) -> error::Result<LatexEngineType> {
let default_engine = read_config()?;
if let Self::Compile {
is_latex,
is_xelatex,
is_pdflatex,
is_lualatex,
is_tectonic,
..
} = self
{
let bitmask = (*is_latex as u8)
| (*is_pdflatex as u8) << 1
| (*is_xelatex as u8) << 2
| (*is_lualatex as u8) << 3
| (*is_tectonic as u8) << 4;
Ok(match bitmask {
1 => LatexEngineType::LaTeX,
2 => LatexEngineType::PdfLaTeX,
4 => LatexEngineType::XeLaTeX,
8 => LatexEngineType::LuaLaTeX,
#[cfg(feature = "tectonic-backend")]
16 => LatexEngineType::Tectonic,
_ => default_engine,
})
} else {
Ok(LatexEngineType::Invalid)
}
}
}
fn read_config() -> error::Result<LatexEngineType> {
let mut dir = dirs::config_dir().unwrap();
dir.push("vesti/config.yaml");
let contents = fs::read_to_string(dir).unwrap_or_default();
let docs = YamlLoader::load_from_str(&contents)?;
let doc = docs.get(0);
let main_engine = if let Some(d) = doc {
if d["engine"]["main"].is_badvalue() {
"pdflatex"
} else {
d["engine"]["main"].as_str().unwrap()
}
} else {
"pdflatex"
};
let main_engine = main_engine.to_lowercase();
Ok(main_engine.parse().unwrap())
}