acorn_lib/analyzer/
vale.rs1use crate::util::{Label, SemanticVersion};
5use bon::Builder;
6use color_eyre::owo_colors::OwoColorize;
7use derive_more::Display;
8use serde::{Deserialize, Serialize};
9use std::path::PathBuf;
10use tracing::error;
11
12#[derive(Clone, Debug, Default, Display, Serialize, Deserialize)]
16#[serde(rename_all = "lowercase")]
17pub enum ValeOutputItemSeverity {
18 #[display("warning")]
22 Warning,
23 #[display("error")]
27 Error,
28 #[default]
32 #[display("suggestion")]
33 Suggestion,
34}
35#[derive(Builder, Clone, Debug, Default, Display)]
37#[display("{:?}", version)]
38#[builder(start_fn = init)]
39pub struct Vale {
40 pub version: Option<SemanticVersion>,
42 pub binary: Option<PathBuf>,
44 pub config: Option<ValeConfig>,
46}
47#[derive(Builder, Clone, Debug, Display)]
51#[display("{:?}", path)]
52#[builder(start_fn = init)]
53pub struct ValeConfig {
54 #[builder(default = PathBuf::from("./.vale/.vale.ini"))]
56 pub path: PathBuf,
57 #[builder(default = Vec::<String>::new())]
61 pub packages: Vec<String>,
62 #[builder(default = Vec::<String>::new())]
66 pub vocabularies: Vec<String>,
67 #[builder(default = Vec::<String>::new())]
71 pub disabled: Vec<String>,
72}
73#[derive(Clone, Debug, Serialize, Deserialize)]
75pub struct ValeOutput {
76 pub items: Vec<ValeOutputItem>,
78}
79#[derive(Clone, Debug, Serialize, Deserialize)]
83#[serde(rename_all = "PascalCase")]
84pub struct ValeOutputItem {
85 action: ValeOutputItemAction,
86 span: Vec<u32>,
87 check: String,
88 description: String,
89 link: String,
90 message: String,
91 severity: ValeOutputItemSeverity,
92 #[serde(rename = "Match")]
93 word_match: String,
94 pub line: u32,
96}
97#[derive(Clone, Debug, Serialize, Deserialize)]
99#[serde(rename_all = "PascalCase")]
100pub struct ValeOutputItemAction {
101 name: String,
103 params: Option<Vec<String>>,
105}
106impl ValeOutputItemSeverity {
107 pub fn colored(&self) -> String {
109 match self {
110 | ValeOutputItemSeverity::Warning => self.to_string().yellow().to_string(),
111 | ValeOutputItemSeverity::Error => self.to_string().red().to_string(),
112 | ValeOutputItemSeverity::Suggestion => self.to_string().blue().to_string(),
113 }
114 }
115}
116#[cfg(any(unix, target_os = "wasi", target_os = "redox"))]
118pub fn preprocess_vale_output(path: PathBuf, output: &str) -> String {
119 output.replace(path.to_str().unwrap(), "items")
120}
121#[cfg(windows)]
123pub fn preprocess_vale_output(path: PathBuf, output: &str) -> String {
124 let input = path.as_path().display().to_string().replace("\\", "/");
125 output.replace("\\\\", "/").replace(&input, "items")
126}
127pub fn parse_vale_output(path: PathBuf, output: &str) -> Vec<ValeOutputItem> {
129 let processed = preprocess_vale_output(path, output);
130 if processed != "{}" {
131 let parsed: serde_json::Result<ValeOutput> = serde_json::from_str(&processed);
132 match parsed {
133 | Ok(ValeOutput { items }) => items,
134 | Err(why) => {
135 error!("=> {} Parse Vale output - {why}", Label::fail());
136 vec![]
137 }
138 }
139 } else {
140 vec![]
141 }
142}
143pub fn print_vale_output(items: Vec<ValeOutputItem>) {
145 for item in items {
146 let ValeOutputItem {
147 check,
148 line,
149 message,
150 severity,
151 span,
152 ..
153 } = item;
154 let location = format!("Line {}, Character {}", line, span[0]);
155 println!("{:<24} {:<21} {} {}", location, severity.colored(), message, check.dimmed());
156 }
157}