diff --git a/cli/src/main.rs b/cli/src/main.rs index ae7c2d2..9f98102 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -3,7 +3,7 @@ use clap::{crate_authors, crate_description, crate_version, Arg, ArgMatches, Com use reqlang::{parse, ParseResult}; use std::{collections::HashMap, fs, process::exit}; -use reqlang::{diagnostics::Diagnoser, export, template, Format, ReqlangError, Spanned}; +use reqlang::{diagnostics::get_diagnostics, export, template, Format}; use std::error::Error; @@ -29,16 +29,6 @@ where Ok((key, value)) } -fn map_errs(errs: &[Spanned]) -> String { - let err = errs - .iter() - .map(|x| format!("{} ({:?})", x.0, x.1)) - .collect::>() - .join("\n- "); - - format!("Errors:\n\n- {err}") -} - fn export_command(matches: &ArgMatches) { let path = matches.get_one::("path").unwrap(); @@ -62,52 +52,52 @@ fn export_command(matches: &ArgMatches) { let contents = fs::read_to_string(path).expect("Should have been able to read the file"); - let diagnostics = Diagnoser::get_diagnostics_with_env(&contents, env, &prompts, &secrets); - - if !diagnostics.is_empty() { - eprintln!("Invalid request file or errors when exporting"); - let json = serde_json::to_string_pretty(&diagnostics).unwrap(); - println!("{json}"); - exit(1); - } - let provider_values = HashMap::from([(String::from("env"), env.clone())]); let reqfile = template(&contents, env, &prompts, &secrets, &provider_values); - let reqfile = match reqfile { - Ok(reqfile) => reqfile, + match reqfile { + Ok(reqfile) => { + let exported_request = export(&reqfile.request, format); + + println!("{}", exported_request); + } Err(errs) => { - let err = map_errs(&errs); - eprintln!("{err}"); - exit(1); + let diagnostics = get_diagnostics(&errs, &contents); + + if !diagnostics.is_empty() { + eprintln!("Invalid request file or errors when exporting"); + let json = serde_json::to_string_pretty(&diagnostics).unwrap(); + println!("{json}"); + exit(1); + } } }; - - let exported_request = export(&reqfile.request, format); - - println!("{}", exported_request); } fn parse_command(matches: &ArgMatches) { let path = matches.get_one::("path").unwrap(); let contents = fs::read_to_string(path).expect("Should have been able to read the file"); - let diagnostics = Diagnoser::get_diagnostics(&contents); - - if !diagnostics.is_empty() { - eprintln!("Invalid request file"); - let json = serde_json::to_string_pretty(&diagnostics).unwrap(); - println!("{json}"); - exit(1); - } - - let parsed_reqfile = parse(&contents).unwrap(); - let parse_results: ParseResult = parsed_reqfile.into(); + match parse(&contents) { + Ok(parsed_reqfile) => { + let parse_results: ParseResult = parsed_reqfile.into(); - let json = serde_json::to_string_pretty(&parse_results).unwrap(); + let json = serde_json::to_string_pretty(&parse_results).unwrap(); - println!("{json}"); + println!("{json}"); + } + Err(errs) => { + let diagnostics = get_diagnostics(&errs, &contents); + + if !diagnostics.is_empty() { + eprintln!("Invalid request file"); + let json = serde_json::to_string_pretty(&diagnostics).unwrap(); + println!("{json}"); + exit(1); + } + } + } } fn main() { diff --git a/diagnostics/src/lib.rs b/diagnostics/src/lib.rs index 7a05775..3171513 100644 --- a/diagnostics/src/lib.rs +++ b/diagnostics/src/lib.rs @@ -1,67 +1,33 @@ -use std::collections::HashMap; - use codespan_reporting::diagnostic::{Diagnostic, Label}; use errors::{ParseError, ReqlangError, ResolverError}; -use parser::template; use serde::{Deserialize, Serialize}; -use span::Span; +use span::{Span, Spanned}; use str_idxpos::index_to_position; -#[derive(Debug, Default)] -pub struct Diagnoser {} - -impl Diagnoser { - pub fn get_diagnostics(source: &str) -> Vec { - match parser::parse(source) { - Ok(_) => vec![], - Err(errs) => { - return errs - .iter() - .map(|(err, span)| Diagnosis { - range: Diagnoser::get_range(source, span), - severity: Some(DiagnosisSeverity::ERROR), - message: err.to_string(), - }) - .collect(); - } - } - } - - pub fn get_diagnostics_with_env( - source: &str, - env: &str, - prompts: &HashMap, - secrets: &HashMap, - ) -> Vec { - match template(source, env, prompts, secrets, &HashMap::new()) { - Ok(_) => vec![], - Err(errs) => { - return errs - .iter() - .map(|(err, span)| Diagnosis { - range: Diagnoser::get_range(source, span), - severity: Some(DiagnosisSeverity::ERROR), - message: err.to_string(), - }) - .collect(); - } - } - } +/// Get a list of diagnostics from a list of errors +pub fn get_diagnostics(errs: &[Spanned], source: &str) -> Vec { + errs.iter() + .map(|(err, span)| Diagnosis { + range: get_range(source, span), + severity: Some(DiagnosisSeverity::ERROR), + message: err.to_string(), + }) + .collect() +} - pub fn get_range(source: &str, span: &Span) -> DiagnosisRange { - DiagnosisRange { - start: Diagnoser::get_position(source, span.start), - end: Diagnoser::get_position(source, span.end), - } +fn get_range(source: &str, span: &Span) -> DiagnosisRange { + DiagnosisRange { + start: get_position(source, span.start), + end: get_position(source, span.end), } +} - pub fn get_position(source: &str, idx: usize) -> DiagnosisPosition { - let (line, character) = index_to_position(source, idx); +fn get_position(source: &str, idx: usize) -> DiagnosisPosition { + let (line, character) = index_to_position(source, idx); - DiagnosisPosition { - line: line as u32, - character: character as u32, - } + DiagnosisPosition { + line: line as u32, + character: character as u32, } } @@ -143,12 +109,16 @@ impl AsDiagnostic for ReqlangError { #[cfg(test)] mod tests { - use crate::{Diagnoser, Diagnosis, DiagnosisPosition, DiagnosisRange, DiagnosisSeverity}; + use parser::parse; + + use crate::{get_diagnostics, Diagnosis, DiagnosisPosition, DiagnosisRange, DiagnosisSeverity}; #[test] fn it_works() { let source = String::from(""); + let errs = parse(&source).unwrap_err(); + assert_eq!( vec![Diagnosis { range: DiagnosisRange { @@ -164,7 +134,7 @@ mod tests { severity: Some(DiagnosisSeverity::ERROR), message: String::from("ParseError: Request file is an empty file") }], - Diagnoser::get_diagnostics(&source) + get_diagnostics(&errs, &source) ); } } diff --git a/errors/src/lib.rs b/errors/src/lib.rs index b576e98..89be324 100644 --- a/errors/src/lib.rs +++ b/errors/src/lib.rs @@ -3,7 +3,7 @@ use thiserror::Error; use types::ReferenceType; /// Common error for parsing and templating request files -#[derive(Debug, Error, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Error, PartialEq, Serialize, Deserialize)] pub enum ReqlangError { #[error("ParseError: {0}")] ParseError(ParseError), @@ -11,7 +11,7 @@ pub enum ReqlangError { ResolverError(ResolverError), } -#[derive(Debug, Error, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Error, PartialEq, Serialize, Deserialize)] pub enum ParseError { #[error("Request file is an empty file")] EmptyFileError, @@ -33,7 +33,7 @@ pub enum ParseError { ForbiddenRequestHeaderNameError(String), } -#[derive(Debug, Error, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Error, PartialEq, Serialize, Deserialize)] pub enum ResolverError { #[error("Invalid env: {0}")] InvalidEnvError(String), diff --git a/reqlang-lsp/src/main.rs b/reqlang-lsp/src/main.rs index eda164e..f38a701 100644 --- a/reqlang-lsp/src/main.rs +++ b/reqlang-lsp/src/main.rs @@ -4,7 +4,9 @@ use std::ops::Deref; use anyhow::{Context, Result}; use reqlang::{ assert_response::assert_response, - diagnostics::{Diagnoser, Diagnosis, DiagnosisPosition, DiagnosisRange, DiagnosisSeverity}, + diagnostics::{ + get_diagnostics, Diagnosis, DiagnosisPosition, DiagnosisRange, DiagnosisSeverity, + }, export, parse, template, Fetch, Format, HttpRequestFetcher, ParseResult, ReqlangError, RequestParamsFromClient, Spanned, }; @@ -92,12 +94,20 @@ impl LanguageServer for Backend { let mut file_texts = self.file_texts.lock().await; file_texts.insert(uri.clone(), source.clone()); - let result: Result>> = - parse(&source).map(|unresolved_reqfile| { - let result: ParseResult = unresolved_reqfile.into(); + let result: Result = parse(&source).map(|reqfile| reqfile.into()); - result - }); + if let Err(errs) = &result { + self.client + .publish_diagnostics( + uri.clone(), + get_diagnostics(errs, &source) + .iter() + .map(|x| (LspDiagnosis((*x).clone())).into()) + .collect(), + Some(params.text_document.version), + ) + .await; + } self.client .send_notification::(ParseNotificationParams::new( @@ -105,37 +115,26 @@ impl LanguageServer for Backend { result, )) .await; - - let version = Some(params.text_document.version); - let diagnostics = Diagnoser::get_diagnostics(&source); - self.client - .publish_diagnostics( - uri.clone(), - diagnostics - .iter() - .map(|x| (LspDiagnosis((*x).clone())).into()) - .collect(), - version, - ) - .await; } async fn did_change(&self, params: DidChangeTextDocumentParams) { let uri = params.text_document.uri; let source = ¶ms.content_changes.first().unwrap().text; - let version = Some(params.text_document.version); - let diagnostics = Diagnoser::get_diagnostics(source); - self.client - .publish_diagnostics( - uri, - diagnostics - .iter() - .map(|x| LspDiagnosis((*x).clone()).into()) - .collect(), - version, - ) - .await; + let result: Result = parse(source).map(|reqfile| reqfile.into()); + + if let Err(errs) = &result { + self.client + .publish_diagnostics( + uri.clone(), + get_diagnostics(errs, source) + .iter() + .map(|x| (LspDiagnosis((*x).clone())).into()) + .collect(), + Some(params.text_document.version), + ) + .await; + } } async fn did_save(&self, params: DidSaveTextDocumentParams) { diff --git a/types/src/lib.rs b/types/src/lib.rs index f30f378..4682f86 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -233,7 +233,7 @@ impl From for RequestParamsFromClient { /// A simplified version of a [ParsedRequestFile] /// /// This is useful for language server clients -#[derive(Debug, Deserialize, Serialize, PartialEq, TS)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, TS)] #[ts(export)] pub struct ParseResult { pub vars: Vec,