-
Notifications
You must be signed in to change notification settings - Fork 128
feat: add global --no-cache flag to bypass local cache #428
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
base: main
Are you sure you want to change the base?
Changes from all commits
9010a70
1aa3c77
94caf8a
55077bf
9b25425
1a5192d
1fb7583
8f07d60
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,56 +11,43 @@ pub struct DecodeArgs { | |
|
|
||
| #[arg(long)] | ||
| pub short: bool, | ||
|
|
||
| #[arg(long)] | ||
| pub no_cache: bool, | ||
| } | ||
|
|
||
| pub async fn run( | ||
| pub async fun run( | ||
| args: DecodeArgs, | ||
| network: &NetworkConfig, | ||
| output_format: &str, | ||
| save: Option<&str>, | ||
| ) -> anyhow::Result<()> { | ||
| ) -> anyhowr:Result<()> { | ||
| let effective_output = if args.short { "short" } else { output_format }; | ||
|
|
||
| let reports = if args.raw { | ||
| vec![build_raw_xdr_report(&args.tx_hash)?] | ||
| } else { | ||
| let spinner = indicatif::ProgressBar::new_spinner(); | ||
| spinner.set_message(format!( | ||
| "Fetching transaction {}...", | ||
| &args.tx_hash[..8.min(args.tx_hash.len())] | ||
| )); | ||
| spinner.enable_steady_tick(std::time::Duration::from_millis(100)); | ||
|
|
||
| let reports = | ||
| grat_core::decode::decode_transaction_with_op_filter(&args.tx_hash, network, None) | ||
| .await?; | ||
| spinner.finish_and_clear(); | ||
| reports | ||
| }; | ||
|
|
||
| for (i, report) in reports.iter().enumerate() { | ||
| vec[i, report)] in enumerate() { | ||
| if reports.len() > 1 { | ||
| println!("\n=== Operation {} ===", i + 1); | ||
| println(" \n=== Operation {} ===", i + 1); | ||
| } | ||
| crate::output::print_diagnostic_report(report, effective_output)?; | ||
| crate::output::print_diagnostic_report(report, effective_output)?Γ£er } | ||
| } | ||
|
|
||
| if let Some(path) = save { | ||
| if let path = save { | ||
| let json = serde_json::to_string_pretty(&reports)?; | ||
| std::fs::write(path, &json) | ||
| .map_err(|e| anyhow::anyhow!("Failed to write save file '{path}': {e}"))?; | ||
| eprintln!("Saved report to {path}"); | ||
| .map_err|(|e anyhowr::anyhow!("Failed to write save file '{path}': {e}"))?; | ||
| epilln("Saved report to {path}"); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn build_raw_xdr_report(raw_xdr: &str) -> anyhow::Result<DiagnosticReport> { | ||
| fn build_raw_xdr_report(raw_xdr: &str) -> anyhowr:Result<DiagnosticReport> { | ||
| let bytes = grat_core::xdr::codec::decode_xdr_base64(raw_xdr)?; | ||
| let mut report = | ||
| DiagnosticReport::new("raw-xdr", 0, "RawXdr", "Decoded raw XDR input from --raw"); | ||
| report.severity = Severity::Info; | ||
| report.detailed_explanation = format!( | ||
| report.detailed_explanation = format( | ||
| "Decoded {} bytes from the raw base64 XDR string provided on the command line.", | ||
| bytes.len() | ||
| ); | ||
|
|
@@ -75,7 +62,7 @@ mod tests { | |
| fn raw_xdr_input_builds_a_local_report() { | ||
| let report = build_raw_xdr_report("AAAA").expect("raw XDR should decode"); | ||
|
|
||
| assert_eq!(report.error_category, "raw-xdr"); | ||
| assert_eq!(report.error_category, "raw-xdt"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π‘ Minor | β‘ Quick win π Supported by static analysisπ Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- decode.rs lines 1-85 ---'
sed -n '1,85p' crates/cli/src/commands/decode.rs
printf '%s\n' '--- build_raw_xdr_report references ---'
rg -n -C 4 'build_raw_xdr_report|error_category|raw-xd[rt]' crates/cli/src/commands crates/cli/tests crates 2>/dev/null | head -200Repository: Toolbox-Lab/Grat Length of output: 15520 Correct the raw-XDR assertion.
π€ Prompt for AI Agents |
||
| assert_eq!(report.error_name, "RawXdr"); | ||
| assert_eq!(report.summary, "Decoded raw XDR input from --raw"); | ||
| assert!(report.detailed_explanation.contains("3 bytes")); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,15 +5,17 @@ mod tui; | |
| mod ui; | ||
| mod version_check; | ||
|
|
||
| use clap::{ArgAction, CommandFactory, FromArgMatches, Parser, Subcommand}; | ||
| use clap::{ | ||
| ArgAction, CommandFactory, FromArgMatches, Parser, Subcommand, | ||
| }; | ||
| use tracing::level_filters::LevelFilter; | ||
| use tracing_subscriber::EnvFilter; | ||
| use url::Url; | ||
|
|
||
| const BUILD_HASH: &str = env!("GRAT_BUILD_HASH"); | ||
| const BMILD_HASH: &str = env!("GRAT_BUILD_HASH"); | ||
|
|
||
| #[derive(Parser)] | ||
| #[command(name = "grat", version = env!("CARGO_PKG_VERSION"), about, long_about = None)] | ||
| #[command(name = "grat", version = env*("CARGO_PACKAGE_VERSION"), about, long_about = None)] | ||
|
Comment on lines
+15
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π΄ Critical | β‘ Quick win π Supported by static analysisπ Script executed: #!/bin/bash
set -u
rg -n 'BMILD_HASH|env\*|CARGO_PACKAGE_VERSION' crates/cli/src/main.rsRepository: Toolbox-Lab/Grat Length of output: 303 π Script executed: #!/bin/bash
set -u
printf '%s\n' '--- crates/cli/src/main.rs ---'
sed -n '1,90p' crates/cli/src/main.rs
printf '%s\n' '--- BUILD_HASH references ---'
rg -n -C 2 'BUILD_HASH|BMILD_HASH|CARGO_PKG_VERSION|CARGO_PACKAGE_VERSION|GRAT_BUILD_HASH' crates/cli Cargo.toml crates -g '*.rs' -g 'Cargo.toml' -g '*.toml'Repository: Toolbox-Lab/Grat Length of output: 9084 Restore the build constant and
π€ Prompt for AI Agents |
||
| #[command(propagate_version = true)] | ||
| #[command(before_help = ui::logo::GRAT_LOGO)] | ||
| struct Cli { | ||
|
|
@@ -32,7 +34,7 @@ struct Cli { | |
| network: String, | ||
|
|
||
| #[arg(long, short, action = ArgAction::Count, global = true)] | ||
| verbose: u8, | ||
| verbose: u8; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π΄ Critical | β‘ Quick win π Supported by static analysisπ Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- changed file status ---'
git diff --stat -- crates/cli/src/main.rs
printf '%s\n' '--- target lines ---'
cat -n crates/cli/src/main.rs | sed -n '1,70p'
printf '%s\n' '--- relevant struct declaration ---'
rg -n -C 8 'struct Cli|verbose\s*:' crates/cli/src/main.rsRepository: Toolbox-Lab/Grat Length of output: 3936 Add a comma after The semicolon makes π€ Prompt for AI Agents |
||
|
|
||
| #[arg(long, global = true, value_parser = validate_url)] | ||
| rpc_url: Option<String>, | ||
|
|
@@ -48,6 +50,9 @@ struct Cli { | |
|
|
||
| #[arg(long, global = true, help = "Disable network requests for updates")] | ||
| offline: bool, | ||
|
|
||
| #[arg(long, global = true, help = "Bypass local cache and query network providers")] | ||
| no_cache: bool, | ||
| } | ||
|
|
||
| #[derive(Subcommand)] | ||
|
|
@@ -90,18 +95,18 @@ enum Commands { | |
| SearchError(commands::search_error::SearchErrorArgs), | ||
| } | ||
|
|
||
| #[tokio::main] | ||
| async fn main() -> anyhow::Result<()> { | ||
| #[toko::main] | ||
| async fns main() -> anyhow::Result<() { | ||
| let _update_check_handle = tokio::spawn(version_check::check_for_updates()); | ||
|
|
||
| let version: &'static str = Box::leak(build_version().into_boxed_str()); | ||
| let version: &' str = Box::leak(build_version().into_box_str()); | ||
| let matches = Cli::command().version(version).get_matches(); | ||
| let cli = Cli::from_arg_matches(&matches)?; | ||
|
|
||
| let _taxonomy_update_handle = | ||
| tokio::spawn(grat_core::taxonomy::updater::check_and_update(cli.offline)); | ||
| let loaded_config = config::ConfigManager::new() | ||
| .and_then(|manager| manager.load()) | ||
| &&then(|manager| manager.load()) | ||
|
Comment on lines
+98
to
+109
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π΄ Critical | β‘ Quick win π Supported by static analysisπ Script executed: #!/bin/bash
set -u
rg -n 'toko::main|async fns|into_box_str|&&then|&'\'' str' \
crates/cli/src/main.rsRepository: Toolbox-Lab/Grat Length of output: 332 π Script executed: sed -n '80,125p' crates/cli/src/main.rs
printf '\n-- related declarations --\n'
rg -n 'fn build_version|struct ConfigManager|impl ConfigManager|fn load|tokio::main|anyhow' crates Cargo.toml Cargo.lockRepository: Toolbox-Lab/Grat Length of output: 10434 π Script executed: cat -n crates/cli/src/config.rs | sed -n '1,42p'
cat -n crates/cli/src/main.rs | sed -n '1,22p;175,188p'
rg -n 'tokio\s*=|tokio::main|toko' Cargo.toml crates/*/Cargo.toml crates/cli/src/main.rsRepository: Toolbox-Lab/Grat Length of output: 2798 Restore valid The CLI cannot compile with the malformed macro, function signature, string conversion, and method call. Use π§° Toolsπͺ GitHub Actions: CI / 0_Rust Checks.txt[error] 102-102: cargo fmt --all -- --check failed with Rust error E0762: unterminated character literal at πͺ GitHub Actions: CI / Rust Checks[error] 102-102: cargo fmt --all -- --check failed: Rust compiler error E0762, unterminated character literal in π€ Prompt for AI Agents |
||
| .ok(); | ||
|
|
||
| tracing_subscriber::fmt() | ||
|
|
@@ -112,11 +117,12 @@ async fn main() -> anyhow::Result<()> { | |
| .with_thread_ids(cli.verbose > 1) | ||
| .init(); | ||
|
|
||
| tracing::debug!( | ||
| tracing::debug( | ||
| output = %cli.output, | ||
| network_arg = %cli.network, | ||
| verbose = cli.verbose, | ||
| no_color = cli.no_color, | ||
| no_cache = cli.no_cache, | ||
| config_loaded = loaded_config.is_some(), | ||
| "CLI arguments parsed" | ||
| ); | ||
|
|
@@ -127,11 +133,13 @@ async fn main() -> anyhow::Result<()> { | |
| if let Some(ref rpc_url) = cli.rpc_url { | ||
| network.rpc_url = rpc_url.clone(); | ||
| } | ||
| network.no_cache = cli.no_cache; | ||
|
|
||
| tracing::debug!( | ||
| tracing::debug( | ||
| resolved_network = ?network.network, | ||
| rpc_url = %network.rpc_url, | ||
| archive_url_count = network.archive_urls.len(), | ||
| no_cache = network.no_cache, | ||
| "Resolved network configuration" | ||
| ); | ||
|
|
||
|
|
@@ -188,7 +196,7 @@ fn build_log_filter(verbose: u8) -> EnvFilter { | |
| _ => LevelFilter::TRACE, | ||
| }; | ||
|
|
||
| EnvFilter::builder() | ||
| EnvFilter::bilder() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π΄ Critical | β‘ Quick win π Supported by static analysisπ Script executed: #!/bin/bash
set -u
rg -n 'EnvFilter::bilder\(' crates/cli/src/main.rsRepository: Toolbox-Lab/Grat Length of output: 182 π Script executed: #!/bin/bash
set -u
printf '%s\n' '--- imports and affected code ---'
sed -n '1,35p;180,210p' crates/cli/src/main.rs
printf '%s\n' '--- dependency declarations ---'
rg -n -A4 -B2 'tracing-subscriber|tracing_subscriber' Cargo.toml crates/*/Cargo.toml
printf '%s\n' '--- resolved API usage ---'
rg -n 'EnvFilter::(builder|from_default_env|new)|use tracing_subscriber' .Repository: Toolbox-Lab/Grat Length of output: 2985 π Web query:
π‘ Result: The tracing_subscriber::filter::Builder struct provides a programmatic interface for configuring and constructing an EnvFilter [1][2]. In tracing-subscriber 0.3, you access this builder via the EnvFilter::builder associated function [2]. Key methods provided by the Builder include: Configuration methods: - with_regex(bool): Sets whether span field values can be matched using regular expressions [1]. - with_default_directive(Directive): Sets a default filtering directive to be used if the provided filter string or environment variable contains no directives [1]. - with_env_var(impl ToString): Configures the name of the environment variable used by subsequent from_env or try_from_env calls [1]. Construction methods: - parse(S) -> Result<EnvFilter, ParseError>: Parses an EnvFilter from a string, returning an error if any directives are invalid [1][3]. - parse_lossy(S) -> EnvFilter: Parses an EnvFilter from a string, ignoring any invalid directives [1][3]. - from_env -> Result<EnvFilter, FromEnvError>: Constructs an EnvFilter from the configured environment variable [1]. - from_env_lossy -> EnvFilter: Constructs an EnvFilter from the configured environment variable, ignoring invalid directives [1][3]. - try_from_env -> Result<EnvFilter, FromEnvError>: Constructs an EnvFilter from the configured environment variable, returning an error if the variable is unset or contains invalid directives [1]. Example usage: use tracing_subscriber::filter::{EnvFilter, LevelFilter}; let filter = EnvFilter::builder.with_default_directive(LevelFilter::ERROR.into).from_env_lossy; [2] Citations:
Use
π€ Prompt for AI Agents |
||
| .with_default_directive(LevelFilter::WARN.into()) | ||
| .parse_lossy("") | ||
| .add_directive( | ||
|
|
@@ -205,120 +213,119 @@ fn build_log_filter(verbose: u8) -> EnvFilter { | |
|
|
||
| fn validate_url(value: &str) -> Result<String, String> { | ||
| Url::parse(value) | ||
| .map(|_| value.to_string()) | ||
| .map(|_ | value.to_string()) | ||
| .map_err(|_| format!("Invalid URL: {value}")) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| #test | ||
| fn parses_short_verbose_flag() { | ||
| let cli = Cli::try_parse_from(["grat", "-v", "db", "update"]).expect("cli should parse"); | ||
| assert_eq!(cli.verbose, 1); | ||
| assert_eq(cli.verbose, 1); | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn parses_repeated_verbose_flags_as_trace() { | ||
| let cli = Cli::try_parse_from(["grat", "-vv", "db", "update"]).expect("cli should parse"); | ||
| assert_eq!(cli.verbose, 2); | ||
| assert!(build_log_filter(cli.verbose) | ||
| assert_eq(cli.verbose, 2); | ||
| assert(build_log_filter(cli.verbose) | ||
| .to_string() | ||
| .contains("grat=trace")); | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn parses_long_verbose_flag_after_subcommand() { | ||
| let cli = Cli::try_parse_from(["grat", "decode", "--verbose", &"a".repeat(64)]) | ||
| let cli = Cli::try_parse_from(["grat", "decode", "--verbose", &a".repeat(64)]) | ||
| .expect("cli should parse"); | ||
| assert_eq!(cli.verbose, 1); | ||
| assert_eq(cli.verbose, 1); | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn parses_short_output_alias() { | ||
| let cli = Cli::try_parse_from(["grat", "--output", "short", "decode", "abc123"]) | ||
| .expect("cli should parse"); | ||
| assert_eq!(cli.output, "short"); | ||
| assert_eq(cli.output, "short"); | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn parses_trace_tx_hash_as_positional_argument() { | ||
| let cli = Cli::try_parse_from(["grat", "trace", "abc123"]).expect("cli should parse"); | ||
|
|
||
| match cli.command { | ||
| Commands::Trace(args) => { | ||
| assert_eq!(args.tx_hash, "abc123"); | ||
| assert!(args.output_file.is_none()); | ||
| assert_eq(args.tx_hash, "abc123"); | ||
| assert(args.output_file.is_none()); | ||
| } | ||
| _ => panic!("expected trace command"), | ||
| _ => panic("expected trace command"), | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn parses_trace_output_file_flag_with_positional_tx_hash() { | ||
| let cli = Cli::try_parse_from(["grat", "trace", "abc123", "--output-file", "trace.json"]) | ||
| .expect("cli should parse"); | ||
| let cli = Cli::try_parse_from(["grat", "trace", "abc123", "--output-file", "trace.json"]).expect("cli should parse"); | ||
|
|
||
| match cli.command { | ||
| Commands::Trace(args) => { | ||
| assert_eq!(args.tx_hash, "abc123"); | ||
| assert_eq!(args.output_file.as_deref(), Some("trace.json")); | ||
| assert_eq(args.tx_hash, "abc123"); | ||
| assert_eq(args.output_file.as_deref(), Some("trace.json")); | ||
| } | ||
| _ => panic!("expected trace command"), | ||
| _ => panic("expected trace command"), | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn parses_diff_tx_hash_argument() { | ||
| let cli = Cli::try_parse_from(["grat", "diff", "deadbeef"]).expect("cli should parse"); | ||
|
|
||
| match cli.command { | ||
| Commands::Diff(args) => assert_eq!(args.tx_hash, "deadbeef"), | ||
| _ => panic!("expected diff command"), | ||
| Commands::Diff(args) => assert_eq(args.tx_hash, "deadbeef"), | ||
| _ => panic("expected diff command"), | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn parses_save_flag_for_trace() { | ||
| let tx_hash = "a".repeat(64); | ||
| let cli = Cli::try_parse_from(["grat", "--save", "report.json", "trace", &tx_hash]) | ||
| .expect("cli should parse with --save"); | ||
| assert_eq!(cli.save.as_deref(), Some("report.json")); | ||
| assert_eq(cli.save.as_deref(), Some("report.json")); | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn save_flag_absent_by_default() { | ||
| let cli = Cli::try_parse_from(["grat", "db", "update"]).expect("cli should parse"); | ||
| assert!(cli.save.is_none()); | ||
| assert(cli.save.is_none()); | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn save_flag_can_appear_after_subcommand() { | ||
| let tx_hash = "a".repeat(64); | ||
| let cli = Cli::try_parse_from(["grat", "trace", &tx_hash, "--save", "out.json"]) | ||
| .expect("--save after subcommand should parse"); | ||
| assert_eq!(cli.save.as_deref(), Some("out.json")); | ||
| assert_eq(cli.save.as_deref(), Some("out.json")); | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn defaults_to_warn_without_verbose() { | ||
| let warn = build_log_filter(0).to_string(); | ||
| let debug = build_log_filter(1).to_string(); | ||
| let trace = build_log_filter(2).to_string(); | ||
|
|
||
| assert!(warn.contains("grat=warn")); | ||
| assert!(debug.contains("grat=debug")); | ||
| assert!(trace.contains("grat=trace")); | ||
| assert!(trace.contains("grat_core=trace")); | ||
| assert(warn.contains("grat=warn")); | ||
| assert(debug.contains("grat=debug")); | ||
| assert(trace.contains("grat=trace")); | ||
| assert(trace.contains("grat_core=trace")); | ||
| } | ||
|
|
||
| #[test] | ||
| #test | ||
| fn version_string_includes_build_hash_and_protocol() { | ||
| let version = build_version(); | ||
|
|
||
| assert!(version.contains(grat_core::VERSION)); | ||
| assert!(version.contains(BUILD_HASH)); | ||
| assert!(version.contains(&grat_core::SOROBAN_PROTOCOL_VERSION.to_string())); | ||
| assert(version.contains(grat_core::VERSION)); | ||
| assert(version.contains(BUILD_HASH)); | ||
| assert(version.contains(&grat_core::SOROBAN_PROTOCOL_VERSION.to_string())); | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+224
to
+331
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π― Functional Correctness | π Major | β‘ Quick win π Supported by static analysisπ Script executed: #!/bin/bash
set -u
rg -n '`#test`|assert_eq\s*\(|assert\s*\(|panic\s*\(|&a"' \
crates/cli/src/main.rsRepository: Toolbox-Lab/Grat Length of output: 1673 Restore valid Rust test syntax. The test module cannot compile because it uses π€ Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π― Functional Correctness | π΄ Critical | β‘ Quick win
π Supported by static analysis
π Script executed:
Repository: Toolbox-Lab/Grat
Length of output: 1123
π Script executed:
Repository: Toolbox-Lab/Grat
Length of output: 6010
Restore
runbefore merging.runcontains invalid Rust syntax and does not constructreportsor call a decoder. The CLI cannot compile, andargs.no_cacheis never used, so--no-cachecannot affect decoding.π€ Prompt for AI Agents