mod output;
pub mod portable;
mod read;
pub mod render;
pub mod targets;
#[cfg(test)]
mod tests;
mod typescript;
use std::collections::{BTreeMap, BTreeSet};
use crate::ordered::OrderedValue;
use crate::visibility::Visibility;
use portable::PortableHooks;
use render::Skip;
pub use output::{FileOutput, FragmentError, HookOutput, MergeOutput, OutputFile};
pub use read::{HookRead, REMOVED_SCHEMA_CODE, REMOVED_SCHEMA_MESSAGE, read, uses_removed_schema};
pub const HOOK_DIR_TOKEN: &str = "${MARS_HOOK_DIR}";
pub(crate) const RESERVED_NAME_SUFFIX: &str = ".hooks";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Origin {
Local,
Dependency,
}
pub const HOOK_TARGET_TOKEN: &str = "${MARS_HOOK_TARGET}";
#[derive(Debug, Clone, Copy, Default)]
pub struct Tokens<'a> {
pub hook_dir: &'a str,
pub hook_target: &'a str,
pub hook_name: &'a str,
}
impl Tokens<'_> {
pub fn substitute(&self, text: &str) -> String {
text.replace(HOOK_DIR_TOKEN, self.hook_dir)
.replace(HOOK_TARGET_TOKEN, self.hook_target)
}
fn substitute_value(&self, value: &mut OrderedValue) {
match value {
OrderedValue::String(text) => *text = self.substitute(text),
OrderedValue::Array(values) => values.iter_mut().for_each(|v| self.substitute_value(v)),
OrderedValue::Object(values) => {
values.values_mut().for_each(|v| self.substitute_value(v))
}
_ => {}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Fragment {
pub file: String,
unchecked: bool,
}
impl Fragment {
pub fn is_unchecked(&self) -> bool {
self.unchecked
}
}
#[derive(Debug, Clone)]
pub struct HookDef {
pub name: String,
pub visibility: Visibility,
pub order: i32,
targets: BTreeMap<String, Fragment>,
portable: Option<PortableHooks>,
skip_targets: BTreeSet<String>,
}
impl HookDef {
pub fn fragment(&self, target_root: &str) -> Option<&Fragment> {
self.targets.get(target_root)
}
pub fn fragments(&self) -> impl Iterator<Item = (&str, &Fragment)> {
self.targets.iter().map(|(root, f)| (root.as_str(), f))
}
pub fn portable(&self) -> Option<&PortableHooks> {
self.portable.as_ref()
}
fn generates_for(&self, target_root: &str) -> bool {
self.portable.is_some() && !self.skip_targets.contains(target_root)
}
pub fn select<'a>(&'a self, managed_targets: &'a [String]) -> Vec<&'a str> {
managed_targets
.iter()
.map(String::as_str)
.filter(|root| {
self.targets.contains_key(*root)
|| (self.generates_for(root)
&& targets::target(root).is_some_and(|target| {
self.portable
.as_ref()
.is_some_and(|p| p.renders_on(&target.profile))
}))
})
.collect()
}
pub fn skips_for(&self, target_root: &str) -> Vec<Skip> {
match (&self.portable, targets::target(target_root)) {
(Some(portable), Some(target)) if self.generates_for(target_root) => {
portable.render(&target.profile, &Tokens::default()).skips
}
_ => Vec::new(),
}
}
}