From fbf8965cd4ce517af1251380c848b11c0c09a4b7 Mon Sep 17 00:00:00 2001 From: WillLillis Date: Mon, 23 Dec 2024 21:54:48 -0500 Subject: [PATCH] feat(cli): add json summary of parsing --- cli/src/main.rs | 18 +++++++++----- cli/src/parse.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 2e4b1f30..2768d1fc 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -213,6 +213,9 @@ struct Parse { /// Open `log.html` in the default browser, if `--debug-graph` is supplied #[arg(long)] pub open_log: bool, + /// Output parsing results in a JSON format + #[arg(long, short = 'j')] + pub output_json_summary: bool, /// The path to an alternative config.json file #[arg(long)] pub config_path: Option, @@ -812,7 +815,7 @@ impl Parse { ParseOutput::Xml } else if self.output_cst { ParseOutput::Cst - } else if self.quiet { + } else if self.quiet || self.output_json_summary { ParseOutput::Quiet } else { ParseOutput::Normal @@ -859,7 +862,7 @@ impl Parse { loader.find_all_languages(&loader_config)?; let should_track_stats = self.stat; - let mut stats = parse::Stats::default(); + let mut stats = parse::ParseStats::new(); let options = ParseFileOptions { edits: &edits @@ -882,11 +885,11 @@ impl Parse { if should_track_stats { stats.total_parses += 1; if parse_result.successful { - stats.successful_parses += 1; + stats.cumulative_stats.successful_parses += 1; } if let Some(duration) = parse_result.duration { - stats.total_bytes += parse_result.bytes; - stats.total_duration += duration; + stats.cumulative_stats.total_bytes += parse_result.bytes; + stats.cumulative_stats.total_duration += duration; } } @@ -972,7 +975,10 @@ impl Parse { } if should_track_stats { - println!("\n{stats}"); + println!("\n{}", stats.cumulative_stats); + } + if self.output_json_summary { + println!("{}", serde_json::to_string_pretty(&stats)?); } if has_error { diff --git a/cli/src/parse.rs b/cli/src/parse.rs index 3368f5e1..ba55ab08 100644 --- a/cli/src/parse.rs +++ b/cli/src/parse.rs @@ -1,7 +1,7 @@ use std::{ fmt, fs, io::{self, StdoutLock, Write}, - path::Path, + path::{Path, PathBuf}, sync::atomic::{AtomicUsize, Ordering}, time::{Duration, Instant}, }; @@ -17,7 +17,7 @@ use tree_sitter::{ use super::util; use crate::{fuzz::edits::Edit, test::paint}; -#[derive(Debug, Default)] +#[derive(Debug, Default, Serialize)] pub struct Stats { pub successful_parses: usize, pub total_parses: usize, @@ -177,9 +177,65 @@ pub enum ParseOutput { Dot, } +/// A position in a multi-line text document, in terms of rows and columns. +/// +/// Rows and columns are zero-based. +/// +/// This serves as a serializable wrapper for `Point` +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)] +pub struct ParsePoint { + pub row: usize, + pub column: usize, +} + +impl From for ParsePoint { + fn from(value: Point) -> Self { + Self { + row: value.row, + column: value.column, + } + } +} + +#[derive(Serialize, Default, Debug, Clone)] +pub struct ParseSummary { + pub file: PathBuf, + pub successful: bool, + pub start: Option, + pub end: Option, + pub duration: Option, + pub bytes: Option, +} + +impl ParseSummary { + pub fn new(path: &Path) -> Self { + Self { + file: path.to_path_buf(), + successful: false, + ..Default::default() + } + } +} + +#[derive(Serialize, Debug)] +pub struct ParseStats { + pub parse_summaries: Vec, + pub cumulative_stats: Stats, +} + +impl ParseStats { + pub fn new() -> Self { + Self { + parse_summaries: Vec::new(), + cumulative_stats: Stats::default(), + } + } +} + pub struct ParseFileOptions<'a> { pub edits: &'a [&'a str], pub output: ParseOutput, + pub stats: &'a mut ParseStats, pub print_time: bool, pub timeout: u64, pub debug: bool, @@ -342,6 +398,10 @@ pub fn parse_file_at_path( parser.stop_printing_dot_graphs(); + let current_summary = opts.stats.parse_summaries.last_mut().unwrap(); + current_summary.start = Some(tree.root_node().start_position().into()); + current_summary.end = Some(tree.root_node().end_position().into()); + let parse_duration_ms = parse_duration.as_micros() as f64 / 1e3; let edit_duration_ms = edit_duration.as_micros() as f64 / 1e3; let mut cursor = tree.walk();