use std::{
env, fs,
io::{self, BufRead as _},
path::{Path, PathBuf},
};
use crate::util;
pub const ZR_WORKSPACE_DIR: &str = "ZR_WORKSPACE_DIR";
pub const ZR_SOURCE_DIR: &str = "ZR_SOURCE_DIR";
pub const ZR_TARGET_DIR: &str = "ZR_TARGET_DIR";
pub const ZR_CACHE_DIR: &str = "ZR_CACHE_DIR";
pub const ZR_REQUEST: &str = "ZR_REQUEST";
pub const ZR_REQUEST_DD: &str = "ZR_REQUEST_DD";
pub const ZR_TARGET: &str = "ZR_TARGET";
pub const ZR_TARGET_DD: &str = "ZR_TARGET_DD";
pub const ZR_FINAL: &str = "ZR_FINAL";
pub const ZR_HELP: &str = "ZR_HELP";
pub const ZR_APP_ID: &str = "ZR_APP_ID";
pub const ZR_APP: &str = "ZR_APP";
pub const ZR_ORG: &str = "ZR_ORG";
pub const ZR_VERSION: &str = "ZR_VERSION";
pub const ZR_DESCRIPTION: &str = "ZR_DESCRIPTION";
pub const ZR_HOMEPAGE: &str = "ZR_HOMEPAGE";
pub const ZR_LICENSE: &str = "ZR_LICENSE";
pub const ZR_PKG_NAME: &str = "ZR_PKG_NAME";
pub const ZR_PKG_AUTHORS: &str = "ZR_PKG_AUTHORS";
pub const ZR_CRATE_NAME: &str = "ZR_CRATE_NAME";
pub const ZR_QUALIFIER: &str = "ZR_QUALIFIER";
pub fn help(help: &str) {
if env::var(ZR_HELP).is_ok() {
println!("{help}");
std::process::exit(0);
};
}
pub fn path(var: &str) -> PathBuf {
env::var(var).unwrap_or_else(|_| panic!("missing {var}")).into()
}
pub fn display_path(p: &Path) -> String {
let base = path(ZR_WORKSPACE_DIR);
let r = if let Ok(local) = p.strip_prefix(base) {
local.display().to_string()
} else {
p.display().to_string()
};
#[cfg(windows)]
return r.replace('\\', "/");
#[cfg(not(windows))]
r
}
fn read_line(path: &Path, expected: &str) -> io::Result<String> {
match read_lines(path).next() {
Some(r) => r.map(|(_, l)| l),
None => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("expected {expected} in tool file content"),
)),
}
}
fn read_lines(path: &Path) -> impl Iterator<Item = io::Result<(usize, String)>> {
enum State {
Open(io::Result<fs::File>),
Lines(usize, io::Lines<io::BufReader<fs::File>>),
End,
}
let mut state = State::Open(fs::File::open(path));
std::iter::from_fn(move || {
loop {
match std::mem::replace(&mut state, State::End) {
State::Lines(count, mut lines) => {
if let Some(l) = lines.next() {
match l {
Ok(l) => {
state = State::Lines(count + 1, lines);
let test = l.trim();
if !test.is_empty() && !test.starts_with('#') {
return Some(Ok((count, l)));
}
}
Err(e) => {
return Some(Err(e));
}
}
}
}
State::Open(r) => match r {
Ok(f) => state = State::Lines(1, io::BufReader::new(f).lines()),
Err(e) => return Some(Err(e)),
},
State::End => return None,
}
}
})
}
fn read_path(request_file: &Path) -> io::Result<PathBuf> {
read_line(request_file, "path").map(PathBuf::from)
}
pub(crate) fn symlink_warn(path: &Path) {
warn!("symlink ignored in `{}`, use zr-tools to 'link'", path.display());
}
pub const ENV_TOOL: &str = "ZNG_RES_TOOL";
macro_rules! built_in {
($($tool:tt),+ $(,)?) => {
$(
mod $tool;
use $tool::$tool;
)+
pub static BUILT_INS: &[&str] = &[
$(stringify!($tool),)+
];
static BUILT_IN_FNS: &[fn()] = &[
$($tool,)+
];
};
}
built_in! { copy, glob, rp, sh, shf, warn, fail, apk, l10n, sfx, sfxf }
pub(crate) use l10n::release_langs;
pub(crate) use sh::sh_run;
pub fn run() {
if let Ok(tool) = env::var(ENV_TOOL) {
unsafe {
env::remove_var(ENV_TOOL);
}
if let Some(i) = BUILT_INS.iter().position(|n| *n == tool.as_str()) {
(BUILT_IN_FNS[i])();
std::process::exit(0);
} else {
fatal!("`tool` is not a built-in tool");
}
}
}