use clap_cryo::Parser;
use color_print::cstr;
use colored::Colorize;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{default::Default, path::PathBuf};
#[derive(Parser, Debug, Serialize, Deserialize, Clone, Default)]
#[command(
name = "cryo",
author,
version = cryo_freeze::CRYO_VERSION,
about = &get_about_str(),
long_about = None,
styles=get_styles(),
after_help=&get_after_str(),
allow_negative_numbers = true,
)]
pub struct Args {
#[arg(help=get_datatype_help(), num_args(0..))]
pub datatype: Vec<String>,
#[arg(short, long, allow_negative_numbers = true, help_heading = "Content Options", num_args(1..))]
pub blocks: Option<Vec<String>>,
#[arg(long, allow_negative_numbers = true, help_heading = "Content Options", num_args(0..))]
pub timestamps: Option<Vec<String>>,
#[arg(
short,
long,
help_heading = "Content Options",
num_args(1..),
)]
pub txs: Option<Vec<String>>,
#[arg(short, long, help_heading = "Content Options", verbatim_doc_comment)]
pub align: bool,
#[arg(
long,
default_value_t = 0,
value_name = "N_BLOCKS",
help_heading = "Content Options",
verbatim_doc_comment
)]
pub reorg_buffer: u64,
#[arg(short, long, value_name="COLS", num_args(0..), verbatim_doc_comment, help_heading="Content Options")]
pub include_columns: Option<Vec<String>>,
#[arg(short, long, value_name="COLS", num_args(0..), help_heading="Content Options")]
pub exclude_columns: Option<Vec<String>>,
#[arg(long, value_name="COLS", num_args(0..), verbatim_doc_comment, help_heading="Content Options")]
pub columns: Option<Vec<String>>,
#[arg(long, num_args(1..), help_heading = "Content Options", verbatim_doc_comment)]
pub u256_types: Option<Vec<String>>,
#[arg(long, help_heading = "Content Options")]
pub hex: bool,
#[arg(short, long, num_args(0..), help_heading="Content Options")]
pub sort: Option<Vec<String>>,
#[arg(long, help_heading = "Content Options")]
pub exclude_failed: bool,
#[arg(short, long, help_heading = "Source Options")]
pub rpc: Option<String>,
#[arg(long, help_heading = "Source Options")]
pub network_name: Option<String>,
#[arg(short('l'), long, value_name = "limit", help_heading = "Acquisition Options")]
pub requests_per_second: Option<u32>,
#[arg(long, default_value_t = 5, value_name = "R", help_heading = "Acquisition Options")]
pub max_retries: u32,
#[arg(long, default_value_t = 500, value_name = "B", help_heading = "Acquisition Options")]
pub initial_backoff: u64,
#[arg(long, value_name = "M", help_heading = "Acquisition Options")]
pub max_concurrent_requests: Option<u64>,
#[arg(long, value_name = "M", help_heading = "Acquisition Options")]
pub max_concurrent_chunks: Option<u64>,
#[arg(long, help_heading = "Acquisition Options")]
pub chunk_order: Option<String>,
#[arg(short, long, help_heading = "Acquisition Options")]
pub dry: bool,
#[arg(long)]
pub remember: bool,
#[arg(short, long)]
pub verbose: bool,
#[arg(long)]
pub no_verbose: bool,
#[arg(short, long, default_value_t = 1000, help_heading = "Output Options")]
pub chunk_size: u64,
#[arg(long, help_heading = "Output Options")]
pub n_chunks: Option<u64>,
#[arg(long, help_heading = "Output Options")]
pub partition_by: Option<Vec<String>>,
#[arg(short, long, default_value = ".", help_heading = "Output Options")]
pub output_dir: String,
#[arg(long, help_heading = "Output Options", verbatim_doc_comment, num_args(1..))]
pub subdirs: Vec<String>,
#[arg(long, help_heading = "Output Options")]
pub label: Option<String>,
#[arg(long, help_heading = "Output Options")]
pub overwrite: bool,
#[arg(long, help_heading = "Output Options")]
pub csv: bool,
#[arg(long, help_heading = "Output Options")]
pub json: bool,
#[arg(long, value_name = "GROUP_SIZE", help_heading = "Output Options")]
pub row_group_size: Option<usize>,
#[arg(long, help_heading = "Output Options")]
pub n_row_groups: Option<usize>,
#[arg(long, help_heading = "Output Options")]
pub no_stats: bool,
#[arg(long, help_heading="Output Options", value_name="NAME [#]", num_args(1..=2), default_value = "lz4")]
pub compression: Vec<String>,
#[arg(long, help_heading = "Output Options", verbatim_doc_comment)]
pub report_dir: Option<PathBuf>,
#[arg(long, help_heading = "Output Options")]
pub no_report: bool,
#[arg(long, help_heading = "Dataset-specific Options", num_args(1..))]
pub address: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", value_name="address", num_args(1..))]
pub to_address: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", value_name="address", num_args(1..))]
pub from_address: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", num_args(1..))]
pub call_data: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", num_args(1..))]
pub function: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", num_args(1..))]
pub inputs: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", num_args(1..))]
pub slot: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", num_args(1..))]
pub contract: Option<Vec<String>>,
#[arg(long, visible_alias = "event", help_heading = "Dataset-specific Options", num_args(1..))]
pub topic0: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", num_args(1..))]
pub topic1: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", num_args(1..))]
pub topic2: Option<Vec<String>>,
#[arg(long, help_heading = "Dataset-specific Options", num_args(1..))]
pub topic3: Option<Vec<String>>,
#[arg(long, value_name = "SIG", help_heading = "Dataset-specific Options", num_args(1..))]
pub event_signature: Option<String>,
#[arg(
long,
value_name = "BLOCKS",
default_value_t = 1,
help_heading = "Dataset-specific Options"
)]
pub inner_request_size: u64,
#[arg(long, value_name = "tracer", help_heading = "Dataset-specific Options")]
pub js_tracer: Option<String>,
}
impl Args {
pub(crate) fn merge_with_precedence(self, other: Args) -> Self {
let default_struct = Args::default();
let mut s1_value: Value = serde_json::to_value(self).expect("Failed to serialize to JSON");
let s2_value: Value = serde_json::to_value(other).expect("Failed to serialize to JSON");
let default_value: Value =
serde_json::to_value(default_struct).expect("Failed to serialize to JSON");
if let (Value::Object(s1_map), Value::Object(s2_map), Value::Object(default_map)) =
(&mut s1_value, &s2_value, &default_value)
{
for (k, v) in s2_map.iter() {
if default_map.get(k) != Some(v) {
s1_map.insert(k.clone(), v.clone());
}
}
}
serde_json::from_value(s1_value).expect("Failed to deserialize from JSON")
}
}
pub(crate) fn get_styles() -> clap_cryo::builder::Styles {
let white = anstyle::Color::Rgb(anstyle::RgbColor(255, 255, 255));
let green = anstyle::Color::Rgb(anstyle::RgbColor(0, 225, 0));
let grey = anstyle::Color::Rgb(anstyle::RgbColor(170, 170, 170));
let title = anstyle::Style::new().bold().fg_color(Some(green));
let arg = anstyle::Style::new().bold().fg_color(Some(white));
let comment = anstyle::Style::new().fg_color(Some(grey));
clap_cryo::builder::Styles::styled()
.header(title)
.error(comment)
.usage(title)
.literal(arg)
.placeholder(comment)
.valid(title)
.invalid(comment)
}
fn get_about_str() -> String {
cstr!(r#"<white><bold>cryo</bold></white> extracts blockchain data to parquet, csv, or json"#)
.to_string()
}
fn get_after_str() -> String {
let header = "Optional Subcommands:".truecolor(0, 225, 0).bold().to_string();
let subcommands = cstr!(
r#"
<white><bold>cryo help</bold></white> display help message
<white><bold>cryo help syntax</bold></white> display block + tx specification syntax
<white><bold>cryo help datasets</bold></white> display list of all datasets
<white><bold>cryo help</bold></white>"#
);
let post_subcommands = " <DATASET(S)> display info about a dataset";
format!("{}{}{}", header, subcommands, post_subcommands)
}
fn get_datatype_help() -> &'static str {
cstr!(
r#"datatype(s) to collect, use <white><bold>cryo datasets</bold></white> to see all available"#
)
}