use super::{Error, pest::Rule};
use crate::architecture::Architecture;
use pest::iterators::Pair;
#[derive(Clone, Debug, PartialEq)]
pub struct ArchConstraint {
pub negated: bool,
pub arch: Architecture,
}
impl std::fmt::Display for ArchConstraint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", if self.negated { "!" } else { "" }, self.arch)
}
}
impl TryFrom<Pair<'_, Rule>> for ArchConstraint {
type Error = Error;
fn try_from(token: Pair<'_, Rule>) -> Result<Self, Error> {
let mut negated: bool = false;
let mut arch: Option<String> = None;
for token in token.into_inner() {
match token.as_rule() {
Rule::not => {
if negated {
return Err(Error::InvalidArchConstraint);
}
negated = true;
}
Rule::arch_name => {
arch = Some(token.as_str().to_owned());
}
_ => continue,
};
}
let Some(arch) = arch else {
return Err(Error::InvalidArchConstraint);
};
Ok(ArchConstraint {
negated,
arch: arch.parse()?,
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ArchConstraints {
pub arches: Vec<ArchConstraint>,
}
impl std::fmt::Display for ArchConstraints {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
self.arches
.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(" ")
)
}
}
impl TryFrom<Pair<'_, Rule>> for ArchConstraints {
type Error = Error;
fn try_from(token: Pair<'_, Rule>) -> Result<Self, Error> {
let mut constraints = ArchConstraints { arches: vec![] };
for token in token.into_inner() {
match token.as_rule() {
Rule::arch_constraint => {}
_ => continue,
};
constraints.arches.push(token.try_into()?);
}
Ok(constraints)
}
}
impl ArchConstraint {
pub fn matches(&self, arch: &Architecture) -> bool {
let matched = arch.is(&self.arch);
if self.negated { !matched } else { matched }
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ArchConstraintsValidationError {
MixedNegations,
}
impl ArchConstraints {
fn negation_policy(&self) -> Result<bool, ArchConstraintsValidationError> {
let negations = self
.arches
.iter()
.map(|arch_constraint| arch_constraint.negated)
.collect::<Vec<_>>();
if negations.iter().all(|v| *v) {
Ok(true)
} else if negations.iter().all(|v| !v) {
Ok(false)
} else {
Err(ArchConstraintsValidationError::MixedNegations)
}
}
pub fn check(&self) -> Result<(), ArchConstraintsValidationError> {
self.negation_policy()?;
Ok(())
}
pub fn matches(&self, arch: &Architecture) -> bool {
let negated = match self.negation_policy() {
Ok(v) => v,
Err(_) => {
return true;
}
};
let mut matches = self
.arches
.iter()
.map(|arch_constraint| arch_constraint.matches(arch));
if !negated {
matches.any(|v| v)
} else {
matches.all(|v| v)
}
}
}