use std::path::PathBuf;
use crate::{EnvPath, OsCow, os_cow};
pub(crate) const FULL_COLON: char = '\u{FF1A}';
pub(crate) const HALF_COLON: char = '\u{3A}';
const CHUNK_NUM: usize = 2;
pub(crate) fn parse<S: AsRef<str>, I: IntoIterator<Item = S>>(
iter: I,
) -> Option<PathBuf> {
let mut casing = String::with_capacity(30);
#[allow(clippy::manual_try_fold)]
iter
.into_iter()
.fold(Some(PathBuf::with_capacity(16)), |acc, s| {
acc.and_then(|acc_p| {
let s = s.as_ref();
let chunks = get_chunks(s.trim());
let len = if chunks.is_empty() { 0 } else { CHUNK_NUM };
fn or_default<'a>(val: OsCow<'a>, s: &'a str) -> OsCow<'a> {
val.or_else(|| os_cow::from_str(s))
}
let get_2nd_chunk = || unsafe { chunks.get_unchecked(1) };
match len {
0 | 1 => or_default(None, s),
_ => match chunks[0] {
"$env" => {
match get_2nd_chunk() {
x if x.contains('*') => {
casing = x.to_string();
}
x => {
casing = x.to_ascii_uppercase();
replace_hyphens_with_underscores(&mut casing);
}
}
or_default(EnvPath::handle_envs(&casing), s)
}
#[cfg(feature = "consts")]
"$const" => or_default(EnvPath::handle_consts(get_2nd_chunk()), s),
#[cfg(feature = "value")]
"$val" => or_default(EnvPath::handle_values(get_2nd_chunk()), s),
#[cfg(feature = "dirs")]
"$dir" => or_default(EnvPath::handle_dirs(get_2nd_chunk()), s),
#[cfg(feature = "project")]
x if x.starts_with("$proj") => {
or_default(EnvPath::handle_project_dirs(x, get_2nd_chunk()), s)
}
_ => or_default(None, s),
},
}
.map(|p| acc_p.join(p))
})
})
}
fn replace_hyphens_with_underscores(casing: &mut str) {
if !casing.contains('-') {
return;
}
for byte in unsafe { casing.as_bytes_mut() } {
if *byte == b'-' {
*byte = b'_';
}
}
}
impl EnvPath<'_> {
pub fn de(self) -> Self {
let ref_raw = self.get_raw();
if ref_raw.is_empty() {
return EnvPath {
raw: self.raw,
path: None,
};
}
let path = ref_raw.parse();
Self {
raw: self.raw,
path,
}
}
}
pub(crate) fn get_chunks(s: &str) -> Vec<&str> {
let hc = HALF_COLON;
let fc = FULL_COLON;
match (s.find(hc), s.find(fc)) {
(Some(h), Some(f)) if h < f => split_n(s, hc),
(Some(h), Some(f)) if f < h => split_n(s, fc),
(Some(_), None) => split_n(s, hc),
(None, Some(_)) => split_n(s, fc),
_ => Vec::new(),
}
}
fn split_n(s: &str, c: char) -> Vec<&str> {
s.splitn(CHUNK_NUM, c)
.map(|x| x.trim())
.collect()
}
#[cfg(test)]
mod tests {
use crate::EnvPath;
#[test]
fn deser_vec() {
let v = EnvPath::from([" $env : home "]).de();
dbg!(v);
}
#[test]
fn deser_doc() {
let path = EnvPath::from(["$env: home"]).de();
dbg!(path.display(), path.exists());
}
}