1use std::collections::HashMap;
3use std::env;
5use std::fs;
7use std::path::MAIN_SEPARATOR;
9use std::path::PathBuf;
11use std::process::Command;
13use std::process::Stdio;
15use cargo_metadata::CompilerMessage;
17use cargo_metadata::Message;
19use mist_parser::rev_mapper;
21#[derive(Debug, Clone)]
22pub struct MistDiagnosticMessage {
24 pub message: String,
25 pub file_path: PathBuf,
26 pub file_name: String,
27 pub line: usize,
28 pub column: usize,
29}
30
31#[derive(Debug, Clone)]
32pub enum MistDiagnostic {
34 Error (MistDiagnosticMessage),
35 Warning (MistDiagnosticMessage),
36 Rust (CompilerMessage),
37}
38
39pub fn build(mut args: Vec<String>, root: PathBuf) -> bool {
41 args.remove(0);
43 args.insert(1, "--message-format=json".to_string());
45 let is_root = root==env::current_dir().expect("Failed getting env");
47 let mut command = Command::new("cargo").args(args).stdout(Stdio::piped()).stderr(Stdio::inherit()).stdin(Stdio::inherit()).spawn().expect("Failed to run cargo");
49 let mut reader = std::io::BufReader::new(command.stdout.take().expect("Failed to get reader"));
51 let mut diagnostics = Vec::new();
53 let mut mapping = HashMap::new();
55 let mist_src = format!(".mist{MAIN_SEPARATOR}src");
57 for message in cargo_metadata::Message::parse_stream(&mut reader){
59 match message { Ok (Message::CompilerMessage (msg,),) => {
63 for span in &msg.message.spans{
65 if span.is_primary {
67 let rust_path = root.join(&span.file_name);
69 let mist_file = span.file_name.replacen(&mist_src, "src", 1).trim_end_matches(".rs").to_string()+".mist";
71 let mist_path = root.join(&mist_file);
73 if !fs::exists(&mist_path).expect("Unable to check if mist file exists") {
75 diagnostics.push(MistDiagnostic::Rust(msg));
77 break
79;
80 }
81 let map = mapping.entry(rust_path.clone()).or_insert_with(|| {
83 rev_mapper::get_mapping(&fs::read_to_string(rust_path).expect("Failed to read file for mapping"))
85 });
86 let (_,rev_mapper::MistMap (line,column,),) = rev_mapper::find_mapping(&map, &rev_mapper::RustMap(span.line_end, span.column_start)).expect("Unable to find mapping");
88 let mist_msg = MistDiagnosticMessage{message: format!("{}: {}",
90 msg.message.message,
91 span.label.clone().unwrap_or_default()),file_name: if is_root {
92 mist_file
94 } else {
95 mist_path.to_string_lossy().to_string()
97 },file_path: mist_path,line: line,column: column,};
98 match msg.message.level { cargo_metadata::diagnostic::DiagnosticLevel::Error => {
102 diagnostics.push(MistDiagnostic::Error(mist_msg))
104 } cargo_metadata::diagnostic::DiagnosticLevel::Warning => {
107 diagnostics.push(MistDiagnostic::Warning(mist_msg))
109 } _ => {
112 }
113 }
114 }
115 }
116 } Ok (Message::BuildFinished (finish,),) => {
119 print_diagnostics(&diagnostics);
121 let mut raw_reader = reader.into_inner();
123 let mut stdout = std::io::stdout();
125 std::io::copy(&mut raw_reader, &mut stdout).map_err(|v| v.to_string()).expect("(Mist) Failed to copy IO");
127 command.wait().unwrap();
129 return finish.success;
131 } Ok (Message::TextLine (text,),) => {println!("{text}");} _ => {
136 }
137 }
138 }
139 false
141}pub fn print_diagnostics(diagnostics: &Vec<MistDiagnostic>) -> () {
143 let mut files = HashMap::new();
145 for diag in diagnostics{
147 match diag { MistDiagnostic::Error (msg,) => {
151 let line = get_line(&mut files, &msg);
153 println!("\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}\n\t{}",
155 msg.file_name,
156 msg.line,
157 msg.column,
158 msg.message,
159 line.unwrap_or_default(),)
160 } MistDiagnostic::Warning (msg,) => {
163 let line = get_line(&mut files, &msg);
165 println!("\n{}:{}:{}\n \x1b[33mWarning\x1b[0m: {}\n\t{}",
167 msg.file_name,
168 msg.line,
169 msg.column,
170 msg.message,
171 line.unwrap_or_default(),)
172 } MistDiagnostic::Rust (rs,) => {println!("{rs}")}
175 }
176 }
177}pub fn get_line(files: &mut HashMap<PathBuf, Vec<String>>, msg: &MistDiagnosticMessage) -> Option<String> {
179 let src_path = msg.file_path.clone();
181 let lines = files.entry(src_path.clone()).or_insert_with(|| {
183 fs::read_to_string(src_path).expect("Unable to read mist file").lines().into_iter().map(String::from).collect()
185 });
186 lines.get(msg.line-1).map(|v| v.trim().to_string())
188}