nu_lint/
lint.rs

1use std::borrow::Cow;
2
3use miette::SourceSpan;
4use nu_protocol::Span;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
7pub enum Severity {
8    Error,
9    Warning,
10    Info,
11}
12
13impl std::fmt::Display for Severity {
14    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        match self {
16            Severity::Error => write!(f, "error"),
17            Severity::Warning => write!(f, "warning"),
18            Severity::Info => write!(f, "info"),
19        }
20    }
21}
22
23#[derive(Debug, Clone)]
24pub struct Violation {
25    pub rule_id: Cow<'static, str>,
26    pub severity: Severity,
27    pub message: Cow<'static, str>,
28    pub span: Span,
29    pub suggestion: Option<Cow<'static, str>>,
30    pub fix: Option<Fix>,
31    pub file: Option<Cow<'static, str>>,
32}
33
34impl Violation {
35    /// Create a new violation with static strings (most common case)
36    #[must_use]
37    pub const fn new_static(
38        rule_id: &'static str,
39        severity: Severity,
40        message: &'static str,
41        span: Span,
42    ) -> Self {
43        Self {
44            rule_id: Cow::Borrowed(rule_id),
45            severity,
46            message: Cow::Borrowed(message),
47            span,
48            suggestion: None,
49            fix: None,
50            file: None,
51        }
52    }
53
54    /// Create a new violation with a dynamic message
55    #[must_use]
56    pub fn new_dynamic(
57        rule_id: &'static str,
58        severity: Severity,
59        message: String,
60        span: Span,
61    ) -> Self {
62        Self {
63            rule_id: Cow::Borrowed(rule_id),
64            severity,
65            message: Cow::Owned(message),
66            span,
67            suggestion: None,
68            fix: None,
69            file: None,
70        }
71    }
72
73    /// Add a static suggestion to this violation
74    #[must_use]
75    pub fn with_suggestion_static(mut self, suggestion: &'static str) -> Self {
76        self.suggestion = Some(Cow::Borrowed(suggestion));
77        self
78    }
79
80    /// Add a dynamic suggestion to this violation
81    #[must_use]
82    pub fn with_suggestion_dynamic(mut self, suggestion: String) -> Self {
83        self.suggestion = Some(Cow::Owned(suggestion));
84        self
85    }
86
87    /// Add a fix to this violation
88    #[must_use]
89    pub fn with_fix(mut self, fix: Fix) -> Self {
90        self.fix = Some(fix);
91        self
92    }
93
94    #[must_use]
95    pub fn to_source_span(&self) -> SourceSpan {
96        SourceSpan::from((self.span.start, self.span.end - self.span.start))
97    }
98}
99
100#[derive(Debug, Clone)]
101pub struct Fix {
102    pub description: Cow<'static, str>,
103    pub replacements: Vec<Replacement>,
104}
105
106impl Fix {
107    /// Create a fix with a static description
108    #[must_use]
109    pub fn new_static(description: &'static str, replacements: Vec<Replacement>) -> Self {
110        Self {
111            description: Cow::Borrowed(description),
112            replacements,
113        }
114    }
115
116    /// Create a fix with a dynamic description
117    #[must_use]
118    pub fn new_dynamic(description: String, replacements: Vec<Replacement>) -> Self {
119        Self {
120            description: Cow::Owned(description),
121            replacements,
122        }
123    }
124}
125
126#[derive(Debug, Clone)]
127pub struct Replacement {
128    pub span: Span,
129    pub new_text: Cow<'static, str>,
130}
131
132impl Replacement {
133    /// Create a replacement with static text
134    #[must_use]
135    pub const fn new_static(span: Span, new_text: &'static str) -> Self {
136        Self {
137            span,
138            new_text: Cow::Borrowed(new_text),
139        }
140    }
141
142    /// Create a replacement with dynamic text
143    #[must_use]
144    pub fn new_dynamic(span: Span, new_text: String) -> Self {
145        Self {
146            span,
147            new_text: Cow::Owned(new_text),
148        }
149    }
150}