Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix error messaging #479

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ target
.vscode
node_modules
.aider*
.idea
178 changes: 78 additions & 100 deletions crates/cli/src/commands/patterns.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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::<Vec<_>>();
let output_lines = if let Some(output) = &sample.output {
output.lines().collect::<Vec<_>>()
} 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{:<width$} {}", "Input".blue(), "| Output".blue(),);
log::info!(
"{} {} {}",
"-".repeat(width).blue(),
"|".blue(),
"-".repeat(output_width).blue(),
);
let max_len = std::cmp::max(input_lines.len(), output_lines.len());
for i in 0..max_len {
let input_line = input_lines.get(i).unwrap_or(&"");
let output_line = output_lines.get(i).unwrap_or(&"");
log::info!("{:<width$} {} {}", input_line, "|".blue(), output_line);
}
log::info!(
"{} {} {}",
"-".repeat(width).blue(),
"|".blue(),
"-".repeat(output_width).blue(),
);
log::info!("");
}
}
} else {
log::error!("Pattern not found: {}", arg.name);
log::info!(
"\nRun {} to see all available patterns.",
"grit patterns list".bold()
);
}

Ok(())
}

#[derive(Args, Debug, Serialize)]
pub struct PatternsEditArgs {
/// The pattern path to edit
Expand All @@ -172,19 +82,41 @@ pub struct OpenStudioSettings {
}

pub(crate) async fn run_patterns_edit(arg: PatternsEditArgs) -> 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";
Expand All @@ -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(())
}
7 changes: 5 additions & 2 deletions crates/cli/src/commands/patterns_list.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::Result;
use anyhow::{Context, Result};
use marzano_core::api::EnforcementLevel;
use marzano_gritmodule::config::{DefinitionSource, ResolvedGritDefinition};

Expand Down Expand Up @@ -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
}
26 changes: 7 additions & 19 deletions crates/cli/src/commands/patterns_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -141,10 +142,8 @@ pub async fn get_marzano_pattern_test_results(
})
.collect::<Result<Vec<_>>>()?;

// Filter out the None values
let mut test_report = test_reports.into_iter().flatten().collect::<Vec<_>>();

// 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,
Expand Down Expand Up @@ -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| {
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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(());
Expand Down Expand Up @@ -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)?;
Expand All @@ -446,7 +439,6 @@ async fn enable_watch_mode(
.map(|p| (p.local_name.as_ref().unwrap(), p))
.collect::<HashMap<_, _>>();

// event processing
for result in rx {
match result {
Ok(event) => {
Expand Down Expand Up @@ -527,8 +519,6 @@ async fn get_modified_and_deleted_patterns(
.map(|p| p.local_name.as_ref().unwrap())
.collect::<Vec<_>>();

//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 = <Vec<GritPatternTestInfo>>::new();
for pattern in testable_patterns {
if pattern.config.path.as_ref().unwrap() == modified_path
Expand All @@ -553,7 +543,6 @@ enum TestOutcome {
struct TestReport {
outcome: TestOutcome,
message: Option<String>,
/// Sample test details
samples: Vec<SampleTestResult>,
}

Expand All @@ -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 {
Expand Down
Loading