forge/cmd/
cache.rs
1use cache::Cache;
2use clap::{
3 builder::{PossibleValuesParser, TypedValueParser},
4 Arg, Command, Parser, Subcommand,
5};
6use eyre::Result;
7use foundry_config::{cache, Chain, Config, NamedChain};
8use std::{ffi::OsStr, str::FromStr};
9use strum::VariantNames;
10
11#[derive(Debug, Parser)]
13pub struct CacheArgs {
14 #[command(subcommand)]
15 pub sub: CacheSubcommands,
16}
17
18#[derive(Debug, Subcommand)]
19pub enum CacheSubcommands {
20 Clean(CleanArgs),
22
23 Ls(LsArgs),
25}
26
27#[derive(Debug, Parser)]
29#[command(group = clap::ArgGroup::new("etherscan-blocks").multiple(false))]
30pub struct CleanArgs {
31 #[arg(
35 env = "CHAIN",
36 default_value = "all",
37 value_parser = ChainOrAllValueParser::default(),
38 )]
39 chains: Vec<ChainOrAll>,
40
41 #[arg(
43 short,
44 long,
45 num_args(1..),
46 value_delimiter(','),
47 group = "etherscan-blocks"
48 )]
49 blocks: Vec<u64>,
50
51 #[arg(long, group = "etherscan-blocks")]
53 etherscan: bool,
54}
55
56impl CleanArgs {
57 pub fn run(self) -> Result<()> {
58 let Self { chains, blocks, etherscan } = self;
59
60 for chain_or_all in chains {
61 match chain_or_all {
62 ChainOrAll::NamedChain(chain) => {
63 clean_chain_cache(chain, blocks.to_vec(), etherscan)?
64 }
65 ChainOrAll::All => {
66 if etherscan {
67 Config::clean_foundry_etherscan_cache()?;
68 } else {
69 Config::clean_foundry_cache()?
70 }
71 }
72 }
73 }
74
75 Ok(())
76 }
77}
78
79#[derive(Debug, Parser)]
80pub struct LsArgs {
81 #[arg(
85 env = "CHAIN",
86 default_value = "all",
87 value_parser = ChainOrAllValueParser::default(),
88 )]
89 chains: Vec<ChainOrAll>,
90}
91
92impl LsArgs {
93 pub fn run(self) -> Result<()> {
94 let Self { chains } = self;
95 let mut cache = Cache::default();
96 for chain_or_all in chains {
97 match chain_or_all {
98 ChainOrAll::NamedChain(chain) => {
99 cache.chains.push(Config::list_foundry_chain_cache(chain.into())?)
100 }
101 ChainOrAll::All => cache = Config::list_foundry_cache()?,
102 }
103 }
104 sh_print!("{cache}")?;
105 Ok(())
106 }
107}
108
109#[derive(Clone, Debug)]
110pub enum ChainOrAll {
111 NamedChain(NamedChain),
112 All,
113}
114
115impl FromStr for ChainOrAll {
116 type Err = String;
117
118 fn from_str(s: &str) -> Result<Self, Self::Err> {
119 if let Ok(chain) = NamedChain::from_str(s) {
120 Ok(Self::NamedChain(chain))
121 } else if s == "all" {
122 Ok(Self::All)
123 } else {
124 Err(format!("Expected known chain or all, found: {s}"))
125 }
126 }
127}
128
129fn clean_chain_cache(chain: impl Into<Chain>, blocks: Vec<u64>, etherscan: bool) -> Result<()> {
130 let chain = chain.into();
131 if blocks.is_empty() {
132 Config::clean_foundry_etherscan_chain_cache(chain)?;
133 if etherscan {
134 return Ok(())
135 }
136 Config::clean_foundry_chain_cache(chain)?;
137 } else {
138 for block in blocks {
139 Config::clean_foundry_block_cache(chain, block)?;
140 }
141 }
142 Ok(())
143}
144
145#[derive(Clone, Debug)]
147pub struct ChainOrAllValueParser {
148 inner: PossibleValuesParser,
149}
150
151impl Default for ChainOrAllValueParser {
152 fn default() -> Self {
153 Self { inner: possible_chains() }
154 }
155}
156
157impl TypedValueParser for ChainOrAllValueParser {
158 type Value = ChainOrAll;
159
160 fn parse_ref(
161 &self,
162 cmd: &Command,
163 arg: Option<&Arg>,
164 value: &OsStr,
165 ) -> Result<Self::Value, clap::Error> {
166 self.inner.parse_ref(cmd, arg, value)?.parse::<ChainOrAll>().map_err(|_| {
167 clap::Error::raw(
168 clap::error::ErrorKind::InvalidValue,
169 "chain argument did not match any possible chain variant",
170 )
171 })
172 }
173}
174
175fn possible_chains() -> PossibleValuesParser {
176 Some(&"all").into_iter().chain(NamedChain::VARIANTS).into()
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn can_parse_cache_ls() {
185 let args: CacheArgs = CacheArgs::parse_from(["cache", "ls"]);
186 assert!(matches!(args.sub, CacheSubcommands::Ls(_)));
187 }
188}