1use crate::models::error::RashResult;
2use std::fmt;
3
4pub mod pipeline;
5pub mod rules;
6
7#[cfg(test)]
8mod tests;
9
10#[cfg(test)]
11mod pipeline_tests;
12
13#[derive(
14 Debug,
15 Clone,
16 Copy,
17 PartialEq,
18 Eq,
19 PartialOrd,
20 Ord,
21 Hash,
22 serde::Serialize,
23 serde::Deserialize,
24 Default,
25)]
26pub enum ValidationLevel {
27 None,
28 #[default]
29 Minimal,
30 Strict,
31 Paranoid,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum Severity {
36 Error,
37 Warning,
38 Style,
39}
40
41impl Severity {
42 pub fn as_str(&self) -> &'static str {
43 match self {
44 Severity::Error => "error",
45 Severity::Warning => "warning",
46 Severity::Style => "style",
47 }
48 }
49}
50
51#[derive(Debug, Clone)]
52pub struct ValidationError {
53 pub rule: &'static str,
54 pub severity: Severity,
55 pub message: String,
56 pub suggestion: Option<String>,
57 pub auto_fix: Option<Fix>,
58 pub line: Option<usize>,
59 pub column: Option<usize>,
60}
61
62#[derive(Debug, Clone)]
63pub struct Fix {
64 pub description: String,
65 pub replacement: String,
66}
67
68impl fmt::Display for ValidationError {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 write!(
71 f,
72 "[{}] {}: {}",
73 self.severity.as_str(),
74 self.rule,
75 self.message
76 )?;
77 if let Some(ref suggestion) = self.suggestion {
78 write!(f, "\n Suggestion: {suggestion}")?;
79 }
80 Ok(())
81 }
82}
83
84impl std::error::Error for ValidationError {}
85
86pub trait Validate {
87 #[allow(clippy::result_large_err)]
88 fn validate(&self) -> Result<(), ValidationError>;
89}
90
91pub trait ShellCheckValidation {
92 type Error: std::error::Error;
93
94 fn validate(&self) -> Result<(), Self::Error>;
95 fn emit_safe(&self) -> String;
96}
97
98#[repr(C)]
99pub struct ValidatedNode {
100 node_type: u16,
101 rule_mask: u16,
102 validation: u32,
103}
104
105static_assertions::const_assert_eq!(std::mem::size_of::<ValidatedNode>(), 8);
106
107pub const IMPLEMENTED_RULES: &[&str] = &[
108 "SC2086", "SC2046", "SC2035", "SC2181", "SC2006", "SC2016", "SC2034", "SC2154", "SC2129",
109 "SC2164", "SC2103", "SC2115", "SC2162", "SC2219", "SC2220", "SC2088", "SC2068", "SC2145",
110 "SC2053", "SC2010",
111];
112
113pub fn validate_shell_snippet(snippet: &str) -> RashResult<()> {
114 rules::validate_all(snippet)
115}