pub mod checks;
pub mod rule;
use core::fmt;
use crate::diagnostic::Diagnostic;
use crate::output::warning::{TaggedViolation, Violation};
use crate::types::{CableCapabilities, CandidateConfig, SinkCapabilities, SourceCapabilities};
#[cfg(any(feature = "alloc", feature = "std"))]
use alloc::vec::Vec;
pub use rule::{CheckList, ConstraintRule};
pub const MAX_WARNINGS: usize = 8;
#[cfg(any(feature = "alloc", feature = "std"))]
pub type CheckResult<W, V> = Result<Vec<W>, Vec<V>>;
#[cfg(not(any(feature = "alloc", feature = "std")))]
pub type CheckResult<W, V> = Result<[Option<W>; MAX_WARNINGS], V>;
pub trait ConstraintEngine {
type Warning: Diagnostic;
type Violation: Diagnostic;
fn check(
&self,
sink: &SinkCapabilities,
source: &SourceCapabilities,
cable: &CableCapabilities,
config: &CandidateConfig<'_>,
) -> CheckResult<Self::Warning, Self::Violation>;
}
pub struct DefaultConstraintEngine<V: 'static = Violation> {
checks: CheckList<V>,
}
impl<V: 'static> Clone for DefaultConstraintEngine<V> {
fn clone(&self) -> Self {
*self
}
}
impl<V: 'static> Copy for DefaultConstraintEngine<V> {}
impl Default for DefaultConstraintEngine<Violation> {
fn default() -> Self {
Self {
checks: checks::DEFAULT_CHECKS,
}
}
}
impl<V: Diagnostic> DefaultConstraintEngine<V> {
pub fn with_checks(checks: CheckList<V>) -> Self {
Self { checks }
}
}
impl<V: Diagnostic> fmt::Debug for DefaultConstraintEngine<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut list = f.debug_list();
for rule in self.checks {
list.entry(&rule.display_name());
}
list.finish()
}
}
#[cfg(all(test, any(feature = "alloc", feature = "std")))]
mod tests {
use super::*;
use crate::output::warning::Violation;
#[test]
fn clone_produces_equivalent_engine() {
let original = DefaultConstraintEngine::default();
let cloned = original.clone();
assert_eq!(alloc::format!("{original:?}"), alloc::format!("{cloned:?}"));
}
#[test]
fn debug_lists_rule_names() {
let engine = DefaultConstraintEngine::<Violation>::default();
let output = alloc::format!("{engine:?}");
assert!(!output.is_empty());
assert!(
output.contains("frl_ceiling"),
"expected 'frl_ceiling' in debug output, got: {output}"
);
}
}
impl<V: Diagnostic> ConstraintEngine for DefaultConstraintEngine<V> {
type Warning = crate::output::warning::Warning;
type Violation = TaggedViolation<V>;
fn check(
&self,
sink: &SinkCapabilities,
source: &SourceCapabilities,
cable: &CableCapabilities,
config: &CandidateConfig<'_>,
) -> CheckResult<Self::Warning, Self::Violation> {
#[cfg(any(feature = "alloc", feature = "std"))]
{
let mut violations = Vec::new();
for rule in self.checks {
if let Some(violation) = rule.check(sink, source, cable, config) {
violations.push(TaggedViolation {
rule: rule.display_name(),
violation,
});
}
}
if violations.is_empty() {
Ok(Vec::new())
} else {
Err(violations)
}
}
#[cfg(not(any(feature = "alloc", feature = "std")))]
{
for rule in self.checks {
if let Some(violation) = rule.check(sink, source, cable, config) {
return Err(TaggedViolation {
rule: rule.display_name(),
violation,
});
}
}
Ok(Default::default())
}
}
}