1mod json;
2mod text;
3mod vscode;
4
5use std::{borrow::Cow, fs};
6
7pub use json::{JsonFix, JsonOutput, JsonReplacement, JsonViolation, format_json};
8use serde::Serialize;
9pub use text::format_text;
10pub use vscode::{
11 VsCodeCodeAction, VsCodeDiagnostic, VsCodeJsonOutput, VsCodeLocation, VsCodePosition,
12 VsCodeRange, VsCodeRelatedInformation, VsCodeTextEdit, format_vscode_json,
13};
14
15use crate::{config::LintLevel, violation::Violation};
16
17#[derive(Serialize)]
18pub struct Summary {
19 pub errors: usize,
20 pub warnings: usize,
21 pub info: usize,
22 pub files_checked: usize,
23}
24
25impl Summary {
26 #[must_use]
27 pub fn from_violations(violations: &[Violation]) -> Self {
28 let (errors, warnings, info) = violations.iter().fold(
29 (0, 0, 0),
30 |(errors, warnings, info), violation| match violation.lint_level {
31 LintLevel::Deny => (errors + 1, warnings, info),
32 LintLevel::Warn => (errors, warnings + 1, info),
33 LintLevel::Allow => (errors, warnings, info + 1),
34 },
35 );
36
37 Self {
38 errors,
39 warnings,
40 info,
41 files_checked: 1,
42 }
43 }
44
45 #[must_use]
46 pub fn format_compact(&self) -> String {
47 let parts: Vec<String> = [
48 (self.errors > 0).then(|| format!("{} error(s)", self.errors)),
49 (self.warnings > 0).then(|| format!("{} warning(s)", self.warnings)),
50 (self.info > 0).then(|| format!("{} info", self.info)),
51 ]
52 .into_iter()
53 .flatten()
54 .collect();
55
56 if parts.is_empty() {
57 String::from("0 violations")
58 } else {
59 parts.join(", ")
60 }
61 }
62}
63
64pub(super) fn calculate_line_column(source: &str, offset: usize) -> (usize, usize) {
65 source
66 .char_indices()
67 .take_while(|(pos, _)| *pos < offset)
68 .fold((1, 1), |(line, column), (_, ch)| {
69 if ch == '\n' {
70 (line + 1, 1)
71 } else {
72 (line, column + 1)
73 }
74 })
75}
76
77pub(super) fn read_source_code(file: Option<&Cow<'_, str>>) -> String {
78 file.and_then(|path| fs::read_to_string(path.as_ref()).ok())
79 .unwrap_or_default()
80}