foundry_cli/opts/
tracing.rs1use std::str::FromStr;
4
5use alloy_primitives::{Address, map::AddressHashMap};
6use clap::{Parser, ValueHint};
7use foundry_config::TracingConfig;
8use serde::Serialize;
9
10#[derive(Clone, Debug, Default, Serialize, Parser)]
12#[command(about = None, long_about = None)]
13pub struct TracingArgs {
14 #[arg(long, help_heading = "Trace options")]
21 #[serde(skip)]
22 pub decode_internal: bool,
23
24 #[arg(long, value_name = "DEPTH", help_heading = "Trace options")]
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub trace_depth: Option<usize>,
28
29 #[arg(long, help_heading = "Trace options")]
31 #[serde(skip)]
32 pub disable_labels: bool,
33
34 #[arg(long, help_heading = "Trace options")]
36 #[serde(skip)]
37 pub compact_labels: bool,
38
39 #[arg(
43 long = "labels",
44 visible_alias = "label",
45 value_name = "ADDRESS:LABEL",
46 value_hint = ValueHint::Other,
47 help_heading = "Trace options"
48 )]
49 #[serde(skip_serializing_if = "Vec::is_empty")]
50 pub labels: Vec<String>,
51}
52
53impl TracingArgs {
54 pub fn resolve(&self, config: &TracingConfig, verbosity: u8) -> TracingConfig {
56 let mut tracing = config.clone();
57 tracing.verbosity = tracing.verbosity.max(verbosity);
58 tracing.labels.extend(self.parsed_labels());
59 tracing.disable_labels |= self.disable_labels;
60 tracing.compact_labels |= self.compact_labels;
61 tracing.trace_depth = self.trace_depth.or(tracing.trace_depth);
62 tracing.decode_internal |= self.decode_internal;
63 tracing
64 }
65
66 pub fn resolve_call_tracer(&self, config: &TracingConfig, verbosity: u8) -> TracingConfig {
68 let mut tracing = self.resolve(config, verbosity);
69 tracing.decode_internal = false;
70 tracing
71 }
72
73 fn parsed_labels(&self) -> AddressHashMap<String> {
74 self.labels
75 .iter()
76 .filter_map(|label| {
77 let (address, label) = label.split_once(':')?;
78 let address = Address::from_str(address).ok()?;
79 Some((address, label.to_string()))
80 })
81 .collect()
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use alloy_primitives::address;
89
90 #[test]
91 fn resolve_merges_cli_overrides() {
92 let address = address!("0x0000000000000000000000000000000000000001");
93 let config = TracingConfig {
94 verbosity: 2,
95 labels: AddressHashMap::from_iter([(address, "config".to_string())]),
96 disable_labels: false,
97 compact_labels: false,
98 trace_depth: Some(1),
99 decode_internal: false,
100 };
101 let args = TracingArgs {
102 decode_internal: true,
103 trace_depth: Some(2),
104 disable_labels: true,
105 compact_labels: true,
106 labels: vec![format!("{address}:cli")],
107 };
108
109 let tracing = args.resolve(&config, 3);
110 assert_eq!(tracing.verbosity, 3);
111 assert_eq!(tracing.labels.get(&address), Some(&"cli".to_string()));
112 assert!(tracing.disable_labels);
113 assert!(tracing.compact_labels);
114 assert_eq!(tracing.trace_depth, Some(2));
115 assert!(tracing.decode_internal);
116 }
117
118 #[test]
119 fn tracing_verbosity_is_independent_from_global_verbosity() {
120 let config = TracingConfig { verbosity: 5, ..Default::default() };
121
122 assert_eq!(TracingArgs::default().resolve(&config, 1).verbosity, 5);
123 assert_eq!(TracingArgs::default().resolve(&config, 0).verbosity, 5);
124 assert_eq!(TracingArgs::default().resolve(&config, 6).verbosity, 6);
125 }
126
127 #[test]
128 fn call_tracer_does_not_decode_internal_functions() {
129 let config = TracingConfig { decode_internal: true, ..Default::default() };
130 let args = TracingArgs { decode_internal: true, ..Default::default() };
131
132 assert!(!args.resolve_call_tracer(&config, 0).decode_internal);
133 }
134}