use std::path::Path;
use std::str::FromStr;
use miette::Report;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use to_and_fro::{default, ToAndFro};
use crate::ty::LintError;
#[derive(ToAndFro, Clone)]
#[serde]
enum SuggestionApplicability {
MachineApplicable,
HasPlaceholders,
MaybeIncorrect,
Unspecified,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct CompilerSpan {
byte_start: usize,
byte_end: usize,
column_start: usize,
column_end: usize,
line_end: usize,
line_start: usize,
expansion: Option<()>,
file_name: String,
is_primary: bool,
label: Option<String>,
suggested_replacement: Option<String>,
suggestion_applicability: Option<SuggestionApplicability>,
text: Vec<CompilerSpanText>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct CompilerSpanText {
highlight_start: usize,
highlight_end: usize,
text: String,
}
#[derive(ToAndFro, Clone)]
#[serde]
#[casing("kebab")]
#[default("CompilerMessage")]
pub enum CompilerMessageReason {
CompilerMessage,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct CompilerMessage {
reason: CompilerMessageReason,
package_id: String,
manifest_path: String,
target: CompilerMessageTarget,
message: CompilerMessageInner,
}
#[derive(ToAndFro, Clone)]
#[serde]
#[casing("kebab")]
pub enum CompilerMessageLevel {
Warning,
Note,
Help,
ErrorNote,
Error,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct CompilerMessageChild {
children: Vec<()>,
code: Option<String>,
level: CompilerMessageLevel,
message: String,
rendered: Option<String>,
spans: Vec<CompilerSpan>,
}
#[derive(ToAndFro, Clone)]
#[serde]
#[casing("kebab")]
#[default("Diagnostic")]
pub enum CompilerMessageType {
Diagnostic,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct CompilerMessageInner {
rendered: Option<String>,
#[serde(rename = "$message_type")]
message_type: CompilerMessageType,
children: Vec<CompilerMessageChild>,
code: CompilerMessageCode,
level: CompilerMessageLevel,
message: String,
spans: Vec<CompilerSpan>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct CompilerMessageCode {
code: String,
explanation: Option<String>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct CompilerMessageTarget {
kind: Vec<String>,
crate_types: Vec<String>,
name: String,
src_path: String,
edition: String,
doc: bool,
doctest: bool,
test: bool,
}
impl Into<CompilerSpan> for &LintError {
fn into(self) -> CompilerSpan {
let v = self.window.clone();
let byte_start = v.first().unwrap().span().byte_range().start;
let byte_end = v.last().unwrap().span().byte_range().end;
let column_start = v.first().unwrap().span().start().column + 1;
let column_end = v.last().unwrap().span().end().column + 1;
let line_start = v.first().unwrap().span().start().line;
let line_end = v.last().unwrap().span().end().line;
let highlight_end = byte_start - self.line.1;
let highlight_start = highlight_end - (byte_end - byte_start);
let file_name = self.source.name();
CompilerSpan {
byte_end: byte_end,
byte_start: byte_start,
column_end: column_end,
column_start: column_start,
expansion: None,
file_name: file_name.to_string(),
is_primary: true,
label: None,
line_end: line_end,
line_start: line_start,
suggested_replacement: self.rule.replace.clone(),
suggestion_applicability: None,
text: vec![CompilerSpanText {
highlight_end: highlight_end,
highlight_start: highlight_start,
text: self.line.0.to_string(),
}],
}
}
}
impl LintError {
pub fn json_diagnostic(&self) -> CompilerMessage {
let span: CompilerSpan = self.into();
let level = match self.rule.fails {
true => CompilerMessageLevel::Error,
false => CompilerMessageLevel::Warning,
};
let absolute_file_path = Path::new(&self.source.name())
.canonicalize()
.unwrap()
.to_string_lossy()
.to_string();
let mut children = vec![CompilerMessageChild {
children: vec![],
code: None,
level: level.clone(),
message: self.rule.clone().description,
rendered: None,
spans: vec![],
}];
if let Some(help) = self.rule.help.clone() {
children.push(CompilerMessageChild {
children: vec![],
code: None,
level: CompilerMessageLevel::Help,
message: help,
rendered: None,
spans: vec![span.clone()],
});
}
CompilerMessage {
reason: CompilerMessageReason::CompilerMessage,
package_id: String::new(),
manifest_path: String::new(),
target: CompilerMessageTarget {
kind: vec!["bin".to_string()],
crate_types: vec!["bin".to_string()],
name: "splint".to_string(),
src_path: absolute_file_path,
edition: "2021".to_string(),
doc: true,
doctest: false,
test: true,
},
message: CompilerMessageInner {
message_type: CompilerMessageType::Diagnostic,
children,
level: level.clone(),
rendered: Some(format!("{:?}", Report::new(self.clone()))),
message: self.rule.name.clone(),
spans: vec![span.clone()],
code: CompilerMessageCode {
code: self.rule.name.clone(),
explanation: None,
},
},
}
}
}