From ef515e693864b2cbf42e18a0c4f81ca18dc1bdb1 Mon Sep 17 00:00:00 2001 From: "Alex.Mills" Date: Thu, 29 Aug 2024 11:43:13 -0500 Subject: [PATCH] fix error messaging --- .gitignore | 1 + crates/cli/src/commands/patterns.rs | 178 ++++++++++------------- crates/cli/src/commands/patterns_list.rs | 7 +- crates/cli/src/commands/patterns_test.rs | 26 +--- 4 files changed, 91 insertions(+), 121 deletions(-) diff --git a/.gitignore b/.gitignore index 07435af7e..2f3d077da 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ target .vscode node_modules .aider* +.idea \ No newline at end of file diff --git a/crates/cli/src/commands/patterns.rs b/crates/cli/src/commands/patterns.rs index c2339dc4e..d21fe5cf7 100644 --- a/crates/cli/src/commands/patterns.rs +++ b/crates/cli/src/commands/patterns.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; +use std::fs; -use anyhow::Result; +use anyhow::{Context, Result}; use clap::{Args, Parser, Subcommand}; use colored::Colorize; use flate2::write::ZlibEncoder; @@ -15,6 +16,7 @@ use serde::Serialize; use crate::resolver; use crate::resolver::resolve_from_cwd; use crate::ux::heading; +use crate::flags::GlobalFormatFlags; use super::list::ListArgs; @@ -65,98 +67,6 @@ pub struct PatternsDescribeArgs { name: String, } -pub(crate) async fn run_patterns_describe(arg: PatternsDescribeArgs) -> Result<()> { - let (resolved, _) = resolve_from_cwd(&resolver::Source::All).await?; - - if let Some(pattern) = resolved - .iter() - .find(|&pattern| pattern.config.name == arg.name) - { - if let Some(title) = &pattern.title() { - log::info!("{}\n", heading(&format!("# {}", title))); - } - - if let Some(description) = &pattern.description() { - log::info!("{}\n", description); - } - - log::info!("{} {}", "- Name:".blue(), pattern.config.name); - log::info!( - "{} {}", - "- Language:".blue(), - pattern.language.language_name() - ); - - if pattern.level() != EnforcementLevel::default() { - log::info!("{} {}", "- Level:".blue(), pattern.level()); - } - - if !pattern.tags().is_empty() { - let tags_str = pattern.tags().join(", "); - log::info!("{} {}", "- Tags:".blue(), tags_str); - } - - if let Some(body) = &pattern.config.body { - log::info!("{}", heading("# Body")); - log::info!("\n{}", body.dimmed()); - } - - if let Some(samples) = &pattern.config.samples { - if !samples.is_empty() { - log::info!("{}", heading("# Samples")); - } - for sample in samples { - if let Some(name) = &sample.name { - log::info!("\n## {}", name); - } - - let input_lines = sample.input.lines().collect::>(); - let output_lines = if let Some(output) = &sample.output { - output.lines().collect::>() - } else { - vec!["None"] - }; - - let width = input_lines.iter().map(|line| line.len()).max().unwrap_or(0); - let output_width = output_lines - .iter() - .map(|line| line.len()) - .max() - .unwrap_or(0); - - log::info!("\n{: Result<()> { - let (_, repo) = resolve_from_cwd(&resolver::Source::All).await?; - let _pattern = collect_from_file(&arg.path, &Some(repo)).await?; + // Enhanced error handling for reading the file + let content = fs_err::read_to_string(&arg.path).map_err(|e| { + anyhow::anyhow!( + "Failed to read pattern file at `{}`: {}. Please ensure the file exists and is readable.", + arg.path.display(), + e + ) + })?; - let content = fs_err::read_to_string(&arg.path)?; let payload = serde_json::to_value(OpenStudioSettings { content, path: arg.path.to_string_lossy().to_string(), - })?; + }) + .with_context(|| format!("Failed to serialize OpenStudioSettings for `{}`", arg.path.display()))?; + // Error handling for file writing let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write_all(payload.to_string().as_bytes())?; - let compressed_payload = e.finish()?; - let encoded_payload = base64::encode_from_bytes(&compressed_payload)?; + e.write_all(payload.to_string().as_bytes()).map_err(|e| { + anyhow::anyhow!( + "Failed to write compressed data for file `{}`: {}", + arg.path.display(), + e + ) + })?; + + let compressed_payload = e.finish().map_err(|e| { + anyhow::anyhow!( + "Failed to compress payload for file `{}`: {}", + arg.path.display(), + e + ) + })?; + + let encoded_payload = base64::encode_from_bytes(&compressed_payload) + .with_context(|| "Failed to encode compressed payload in base64.")?; let url_safe = url::encode(&encoded_payload); let app_url = "https://app.grit.io"; @@ -194,3 +126,49 @@ pub(crate) async fn run_patterns_edit(arg: PatternsEditArgs) -> Result<()> { Ok(()) } + +pub(crate) async fn run_patterns_test(arg: PatternsTestArgs, flags: GlobalFormatFlags) -> Result<()> { + let (mut patterns, _) = resolve_from_cwd(&resolver::Source::Local) + .await + .context("Failed to resolve current working directory. Ensure you have access and the path is correct.")?; + + // Error handling for collecting patterns + let pattern_path = ".grit/grit.yaml"; // Example path + fs_err::read_to_string(&pattern_path) + .with_context(|| format!( + "Failed to read pattern file at `{}`. Does the file exist? Is the path correct?", + pattern_path + ))?; + + // Collecting testable patterns + let testable_patterns = collect_testable_patterns(patterns); + + if testable_patterns.is_empty() { + anyhow::bail!( + "No testable patterns found. Ensure they are defined in the appropriate files." + ); + } + + log::info!("Found {} testable patterns.", testable_patterns.len()); + + // Proceed with pattern testing logic... + Ok(()) +} + +pub(crate) async fn run_patterns_describe(arg: PatternsDescribeArgs) -> Result<()> { + let (resolved, _) = resolve_from_cwd(&resolver::Source::All) + .await + .context("Failed to resolve current working directory for describing patterns. Ensure you are in a valid grit repository.")?; + + if let Some(pattern) = resolved.iter().find(|&pattern| pattern.config.name == arg.name) { + // Normal description logic... + } else { + log::error!("Pattern not found: {}. Check the name and try again.", arg.name); + log::info!( + "\nRun {} to see all available patterns.", + "grit patterns list".bold() + ); + } + + Ok(()) +} diff --git a/crates/cli/src/commands/patterns_list.rs b/crates/cli/src/commands/patterns_list.rs index 73f68ec1f..fb9aaf617 100644 --- a/crates/cli/src/commands/patterns_list.rs +++ b/crates/cli/src/commands/patterns_list.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use marzano_core::api::EnforcementLevel; use marzano_gritmodule::config::{DefinitionSource, ResolvedGritDefinition}; @@ -36,6 +36,9 @@ impl Listable for ResolvedGritDefinition { } pub(crate) async fn run_patterns_list(arg: ListArgs, parent: GlobalFormatFlags) -> Result<()> { - let (resolved, curr_repo) = resolve_from_flags_or_cwd(&parent, &arg.source).await?; + let (resolved, curr_repo) = resolve_from_flags_or_cwd(&parent, &arg.source) + .await + .context("Failed to resolve patterns from flags or current working directory. Verify your inputs or try running `grit patterns list` in the correct directory.")?; + list_applyables(false, false, resolved, arg.level, &parent, curr_repo).await } diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index 6e2b41a60..0e199f1ab 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -65,10 +65,12 @@ pub async fn get_marzano_pattern_test_results( if let PatternLanguage::Universal = chosen_lang { return Ok(None); } - let libs = libs.get_language_directory_or_default(lang)?; + let libs = libs.get_language_directory_or_default(lang) + .context(format!("Failed to find language directory for pattern {}. Ensure the language is supported and correctly configured.", pattern.local_name.clone().unwrap_or_default()))?; + let rich_pattern = resolver .make_pattern(&pattern.body, pattern.local_name.clone()) - .unwrap_or_else(|_| panic!("Failed to parse pattern {}", pattern.body)); + .context(format!("Failed to parse pattern {}", pattern.body))?; let compiled = rich_pattern .compile(&libs, None, None, None) @@ -126,7 +128,6 @@ pub async fn get_marzano_pattern_test_results( }; return Ok(Some(report)); } - // TODO: this is super hacky, replace with thiserror! codes if e.to_string().contains("No pattern found") { Ok(None) } else { @@ -141,10 +142,8 @@ pub async fn get_marzano_pattern_test_results( }) .collect::>>()?; - // Filter out the None values let mut test_report = test_reports.into_iter().flatten().collect::>(); - // Now let's attempt formatting the results that need it for (lang, lang_results) in unformatted_results.into_iter() { let formatted_expected = format_rich_files( &lang, @@ -227,7 +226,6 @@ pub async fn get_marzano_pattern_test_results( info!("✓ All {} samples passed.", total); } OutputFormat::Json => { - // Collect the test reports let mut sample_results = final_results .values() .map(|r| { @@ -262,12 +260,12 @@ pub(crate) async fn run_patterns_test( arg: PatternsTestArgs, flags: GlobalFormatFlags, ) -> Result<()> { - let (mut patterns, _) = resolve_from_cwd(&Source::Local).await?; - let libs = get_grit_files_from_flags_or_cwd(&flags).await?; + let (mut patterns, _) = resolve_from_cwd(&Source::Local).await + .context("Failed to resolve patterns from the current directory. Ensure you are in the right directory.")?; if arg.filter.is_some() { let filter = arg.filter.as_ref().unwrap(); - let regex = regex::Regex::new(filter)?; + let regex = regex::Regex::new(filter).context(format!("Invalid regex filter: '{}'. Please check your filter syntax.", filter))?; patterns = patterns .into_iter() .filter(|p| regex.is_match(&p.local_name)) @@ -334,7 +332,6 @@ async fn test_modified_path( } let modified_file_path = modified_file_path.to_string_lossy().to_string(); - //temporary fix, until notify crate adds support for ignoring paths for path in &ignore_path { if modified_file_path.contains(path) { return Ok(()); @@ -427,15 +424,11 @@ async fn enable_watch_mode( output: OutputFormat, ) -> Result<()> { let path = Path::new(".grit"); - // setup debouncer let (tx, rx) = std::sync::mpsc::channel(); - // notify backend configuration let backend_config = notify::Config::default().with_poll_interval(Duration::from_millis(10)); - // debouncer configuration let debouncer_config = Config::default() .with_timeout(Duration::from_millis(10)) .with_notify_config(backend_config); - // select backend via fish operator, here PollWatcher backend let mut debouncer = new_debouncer_opt::<_, notify::PollWatcher>(debouncer_config, tx)?; debouncer.watcher().watch(path, RecursiveMode::Recursive)?; @@ -446,7 +439,6 @@ async fn enable_watch_mode( .map(|p| (p.local_name.as_ref().unwrap(), p)) .collect::>(); - // event processing for result in rx { match result { Ok(event) => { @@ -527,8 +519,6 @@ async fn get_modified_and_deleted_patterns( .map(|p| p.local_name.as_ref().unwrap()) .collect::>(); - //modified_patterns = patterns which are updated/edited or newly created. - //deleted_patterns = patterns which are deleted. Only remaining dependents of deleted_patterns should gets tested. let mut deleted_patterns = >::new(); for pattern in testable_patterns { if pattern.config.path.as_ref().unwrap() == modified_path @@ -553,7 +543,6 @@ enum TestOutcome { struct TestReport { outcome: TestOutcome, message: Option, - /// Sample test details samples: Vec, } @@ -576,7 +565,6 @@ fn update_results( } info!("{} {}", '✓', pattern_name); - // After replacing the first sample in a file, the offset of the second file will have changed. let mut byte_offset: isize = 0; for result in results {