use std::fmt;
use std::path::Path;
use ito_config::types::ItoConfig;
use crate::errors::CoreError;
use crate::process::ProcessRunner;
use crate::validate::ValidationIssue;
use super::staged::StagedFiles;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RuleId(&'static str);
impl RuleId {
#[must_use]
pub const fn new(id: &'static str) -> Self {
Self(id)
}
#[must_use]
pub const fn as_str(self) -> &'static str {
self.0
}
}
impl fmt::Display for RuleId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
impl AsRef<str> for RuleId {
fn as_ref(&self) -> &str {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleSeverity {
Error,
Warning,
Info,
}
impl RuleSeverity {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Error => "ERROR",
Self::Warning => "WARNING",
Self::Info => "INFO",
}
}
}
impl fmt::Display for RuleSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[non_exhaustive]
pub struct RuleContext<'a> {
pub config: &'a ItoConfig,
pub project_root: &'a Path,
pub staged: &'a StagedFiles,
pub runner: &'a dyn ProcessRunner,
}
impl<'a> RuleContext<'a> {
#[must_use]
pub fn new(
config: &'a ItoConfig,
project_root: &'a Path,
staged: &'a StagedFiles,
runner: &'a dyn ProcessRunner,
) -> Self {
Self {
config,
project_root,
staged,
runner,
}
}
}
pub trait Rule: Send + Sync {
fn id(&self) -> RuleId;
fn severity(&self) -> RuleSeverity;
fn description(&self) -> &'static str;
fn gate(&self) -> Option<&'static str> {
None
}
fn is_active(&self, config: &ItoConfig) -> bool;
fn check(&self, ctx: &RuleContext<'_>) -> Result<Vec<ValidationIssue>, CoreError>;
}
#[cfg(test)]
#[path = "rule_tests.rs"]
mod rule_tests;