use eyre::{bail, eyre, Result};
use std::cell::RefCell;
use std::path::Path;
use std::rc::Rc;
use super::target::Target;
#[derive(Clone)]
pub enum LookupInternal<'a> {
Partial {
targets: &'a Vec<&'a str>,
},
Complete {
target: Option<&'a Target>,
get_target: &'a dyn Fn(&str) -> Result<Rc<RefCell<Target>>>,
},
}
impl<'a> LookupInternal<'a> {
pub const fn new_partial(targets: &'a Vec<&str>) -> Self {
Self::Partial { targets }
}
pub const fn new(
target: Option<&'a Target>,
get_target: &'a dyn Fn(&str) -> Result<Rc<RefCell<Target>>>,
) -> Self {
Self::Complete { target, get_target }
}
pub fn lookup(&self, macro_name: &str) -> Result<String> {
let macro_pieces = match macro_name.chars().next() {
Some('@') => self.target_name()?,
Some('?') => self.newer_prerequisites()?,
Some('<') => self.inference_prerequisite()?,
Some('*') => self.target_stem()?,
#[cfg(feature = "full")]
Some('^') => self.all_prerequisites()?,
_ => bail!("unknown internal macro {}", macro_name),
};
let macro_pieces = if macro_name.ends_with('D') {
macro_pieces
.into_iter()
.map(|x| {
Path::new(&x)
.parent()
.ok_or_else(|| eyre!("no parent"))
.map(|x| x.to_string_lossy().into())
})
.collect::<Result<_, _>>()?
} else if macro_name.ends_with('F') {
macro_pieces
.into_iter()
.map(|x| {
Path::new(&x)
.file_name()
.ok_or_else(|| eyre!("no filename"))
.map(|x| x.to_string_lossy().into())
})
.collect::<Result<_, _>>()?
} else {
macro_pieces
};
Ok(macro_pieces.join(" "))
}
fn target_name(&self) -> Result<Vec<String>> {
match self {
Self::Partial { targets } => {
Ok(targets.iter().map(|target| target.to_string()).collect())
}
Self::Complete {
target: Some(target),
..
} => Ok(vec![target.name.clone()]),
Self::Complete { target: None, .. } => {
bail!("tried to expand internal macro with no target")
}
}
}
fn newer_prerequisites(&self) -> Result<Vec<String>> {
match self {
Self::Partial { .. } => bail!("can’t expand $? when target not defined"),
Self::Complete {
target: Some(target),
get_target,
} => Ok(target
.prerequisites
.iter()
.filter(|prereq| {
get_target(prereq)
.ok()
.and_then(|prereq| prereq.borrow().newer_than(target))
.unwrap_or(false)
})
.cloned()
.collect()),
Self::Complete { target: None, .. } => {
bail!("tried to expand internal macro with no target")
}
}
}
fn inference_prerequisite(&self) -> Result<Vec<String>> {
match self {
Self::Partial { .. } => bail!("can’t expand $< when target not defined"),
Self::Complete {
target: Some(target),
..
} => {
Ok(vec![target
.prerequisites
.first()
.cloned()
.unwrap_or_default()])
}
Self::Complete { target: None, .. } => {
bail!("tried to expand internal macro with no target")
}
}
}
fn target_stem(&self) -> Result<Vec<String>> {
match self {
Self::Partial { .. } => bail!("can’t expand $* when target not defined"),
Self::Complete {
target: Some(target),
..
} => Ok(vec![target
.stem
.as_ref()
.unwrap_or(&target.name)
.to_owned()]),
Self::Complete { target: None, .. } => {
bail!("tried to expand internal macro with no target")
}
}
}
#[cfg(feature = "full")]
fn all_prerequisites(&self) -> Result<Vec<String>> {
match self {
Self::Partial { .. } => bail!("can’t expand $^ when target not defined"),
Self::Complete {
target: Some(target),
..
} => Ok(target.prerequisites.clone()),
Self::Complete { target: None, .. } => {
bail!("tried to expand internal macro with no target")
}
}
}
}