foundry_cheatcodes/
toml.rs

1//! Implementations of [`Toml`](spec::Group::Toml) cheatcodes.
2
3use crate::{
4    json::{
5        canonicalize_json_path, check_json_key_exists, parse_json, parse_json_coerce,
6        parse_json_keys, resolve_type,
7    },
8    Cheatcode, Cheatcodes, Result,
9    Vm::*,
10};
11use alloy_dyn_abi::DynSolType;
12use alloy_sol_types::SolValue;
13use foundry_common::fs;
14use foundry_config::fs_permissions::FsAccessKind;
15use serde_json::Value as JsonValue;
16use toml::Value as TomlValue;
17
18impl Cheatcode for keyExistsTomlCall {
19    fn apply(&self, _state: &mut Cheatcodes) -> Result {
20        let Self { toml, key } = self;
21        check_json_key_exists(&toml_to_json_string(toml)?, key)
22    }
23}
24
25impl Cheatcode for parseToml_0Call {
26    fn apply(&self, _state: &mut Cheatcodes) -> Result {
27        let Self { toml } = self;
28        parse_toml(toml, "$")
29    }
30}
31
32impl Cheatcode for parseToml_1Call {
33    fn apply(&self, _state: &mut Cheatcodes) -> Result {
34        let Self { toml, key } = self;
35        parse_toml(toml, key)
36    }
37}
38
39impl Cheatcode for parseTomlUintCall {
40    fn apply(&self, _state: &mut Cheatcodes) -> Result {
41        let Self { toml, key } = self;
42        parse_toml_coerce(toml, key, &DynSolType::Uint(256))
43    }
44}
45
46impl Cheatcode for parseTomlUintArrayCall {
47    fn apply(&self, _state: &mut Cheatcodes) -> Result {
48        let Self { toml, key } = self;
49        parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Uint(256))))
50    }
51}
52
53impl Cheatcode for parseTomlIntCall {
54    fn apply(&self, _state: &mut Cheatcodes) -> Result {
55        let Self { toml, key } = self;
56        parse_toml_coerce(toml, key, &DynSolType::Int(256))
57    }
58}
59
60impl Cheatcode for parseTomlIntArrayCall {
61    fn apply(&self, _state: &mut Cheatcodes) -> Result {
62        let Self { toml, key } = self;
63        parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Int(256))))
64    }
65}
66
67impl Cheatcode for parseTomlBoolCall {
68    fn apply(&self, _state: &mut Cheatcodes) -> Result {
69        let Self { toml, key } = self;
70        parse_toml_coerce(toml, key, &DynSolType::Bool)
71    }
72}
73
74impl Cheatcode for parseTomlBoolArrayCall {
75    fn apply(&self, _state: &mut Cheatcodes) -> Result {
76        let Self { toml, key } = self;
77        parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Bool)))
78    }
79}
80
81impl Cheatcode for parseTomlAddressCall {
82    fn apply(&self, _state: &mut Cheatcodes) -> Result {
83        let Self { toml, key } = self;
84        parse_toml_coerce(toml, key, &DynSolType::Address)
85    }
86}
87
88impl Cheatcode for parseTomlAddressArrayCall {
89    fn apply(&self, _state: &mut Cheatcodes) -> Result {
90        let Self { toml, key } = self;
91        parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Address)))
92    }
93}
94
95impl Cheatcode for parseTomlStringCall {
96    fn apply(&self, _state: &mut Cheatcodes) -> Result {
97        let Self { toml, key } = self;
98        parse_toml_coerce(toml, key, &DynSolType::String)
99    }
100}
101
102impl Cheatcode for parseTomlStringArrayCall {
103    fn apply(&self, _state: &mut Cheatcodes) -> Result {
104        let Self { toml, key } = self;
105        parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::String)))
106    }
107}
108
109impl Cheatcode for parseTomlBytesCall {
110    fn apply(&self, _state: &mut Cheatcodes) -> Result {
111        let Self { toml, key } = self;
112        parse_toml_coerce(toml, key, &DynSolType::Bytes)
113    }
114}
115
116impl Cheatcode for parseTomlBytesArrayCall {
117    fn apply(&self, _state: &mut Cheatcodes) -> Result {
118        let Self { toml, key } = self;
119        parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Bytes)))
120    }
121}
122
123impl Cheatcode for parseTomlBytes32Call {
124    fn apply(&self, _state: &mut Cheatcodes) -> Result {
125        let Self { toml, key } = self;
126        parse_toml_coerce(toml, key, &DynSolType::FixedBytes(32))
127    }
128}
129
130impl Cheatcode for parseTomlBytes32ArrayCall {
131    fn apply(&self, _state: &mut Cheatcodes) -> Result {
132        let Self { toml, key } = self;
133        parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::FixedBytes(32))))
134    }
135}
136
137impl Cheatcode for parseTomlType_0Call {
138    fn apply(&self, _state: &mut Cheatcodes) -> Result {
139        let Self { toml, typeDescription } = self;
140        parse_toml_coerce(toml, "$", &resolve_type(typeDescription)?).map(|v| v.abi_encode())
141    }
142}
143
144impl Cheatcode for parseTomlType_1Call {
145    fn apply(&self, _state: &mut Cheatcodes) -> Result {
146        let Self { toml, key, typeDescription } = self;
147        parse_toml_coerce(toml, key, &resolve_type(typeDescription)?).map(|v| v.abi_encode())
148    }
149}
150
151impl Cheatcode for parseTomlTypeArrayCall {
152    fn apply(&self, _state: &mut Cheatcodes) -> Result {
153        let Self { toml, key, typeDescription } = self;
154        let ty = resolve_type(typeDescription)?;
155        parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(ty))).map(|v| v.abi_encode())
156    }
157}
158
159impl Cheatcode for parseTomlKeysCall {
160    fn apply(&self, _state: &mut Cheatcodes) -> Result {
161        let Self { toml, key } = self;
162        parse_toml_keys(toml, key)
163    }
164}
165
166impl Cheatcode for writeToml_0Call {
167    fn apply(&self, state: &mut Cheatcodes) -> Result {
168        let Self { json, path } = self;
169        let value =
170            serde_json::from_str(json).unwrap_or_else(|_| JsonValue::String(json.to_owned()));
171
172        let toml_string = format_json_to_toml(value)?;
173        super::fs::write_file(state, path.as_ref(), toml_string.as_bytes())
174    }
175}
176
177impl Cheatcode for writeToml_1Call {
178    fn apply(&self, state: &mut Cheatcodes) -> Result {
179        let Self { json, path, valueKey } = self;
180        let json =
181            serde_json::from_str(json).unwrap_or_else(|_| JsonValue::String(json.to_owned()));
182
183        let data_path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?;
184        let toml_data = fs::read_to_string(data_path)?;
185        let json_data: JsonValue =
186            toml::from_str(&toml_data).map_err(|e| fmt_err!("failed parsing TOML: {e}"))?;
187        let value =
188            jsonpath_lib::replace_with(json_data, &canonicalize_json_path(valueKey), &mut |_| {
189                Some(json.clone())
190            })?;
191
192        let toml_string = format_json_to_toml(value)?;
193        super::fs::write_file(state, path.as_ref(), toml_string.as_bytes())
194    }
195}
196
197/// Parse
198fn parse_toml_str(toml: &str) -> Result<TomlValue> {
199    toml::from_str(toml).map_err(|e| fmt_err!("failed parsing TOML: {e}"))
200}
201
202/// Parse a TOML string and return the value at the given path.
203fn parse_toml(toml: &str, key: &str) -> Result {
204    parse_json(&toml_to_json_string(toml)?, key)
205}
206
207/// Parse a TOML string and return the value at the given path, coercing it to the given type.
208fn parse_toml_coerce(toml: &str, key: &str, ty: &DynSolType) -> Result {
209    parse_json_coerce(&toml_to_json_string(toml)?, key, ty)
210}
211
212/// Parse a TOML string and return an array of all keys at the given path.
213fn parse_toml_keys(toml: &str, key: &str) -> Result {
214    parse_json_keys(&toml_to_json_string(toml)?, key)
215}
216
217/// Convert a TOML string to a JSON string.
218fn toml_to_json_string(toml: &str) -> Result<String> {
219    let toml = parse_toml_str(toml)?;
220    let json = toml_to_json_value(toml);
221    serde_json::to_string(&json).map_err(|e| fmt_err!("failed to serialize JSON: {e}"))
222}
223
224/// Format a JSON value to a TOML pretty string.
225fn format_json_to_toml(json: JsonValue) -> Result<String> {
226    let toml = json_to_toml_value(json);
227    toml::to_string_pretty(&toml).map_err(|e| fmt_err!("failed to serialize TOML: {e}"))
228}
229
230/// Convert a TOML value to a JSON value.
231fn toml_to_json_value(toml: TomlValue) -> JsonValue {
232    match toml {
233        TomlValue::String(s) => match s.as_str() {
234            "null" => JsonValue::Null,
235            _ => JsonValue::String(s),
236        },
237        TomlValue::Integer(i) => JsonValue::Number(i.into()),
238        TomlValue::Float(f) => JsonValue::Number(serde_json::Number::from_f64(f).unwrap()),
239        TomlValue::Boolean(b) => JsonValue::Bool(b),
240        TomlValue::Array(a) => JsonValue::Array(a.into_iter().map(toml_to_json_value).collect()),
241        TomlValue::Table(t) => {
242            JsonValue::Object(t.into_iter().map(|(k, v)| (k, toml_to_json_value(v))).collect())
243        }
244        TomlValue::Datetime(d) => JsonValue::String(d.to_string()),
245    }
246}
247
248/// Convert a JSON value to a TOML value.
249fn json_to_toml_value(json: JsonValue) -> TomlValue {
250    match json {
251        JsonValue::String(s) => TomlValue::String(s),
252        JsonValue::Number(n) => match n.as_i64() {
253            Some(i) => TomlValue::Integer(i),
254            None => match n.as_f64() {
255                Some(f) => TomlValue::Float(f),
256                None => TomlValue::String(n.to_string()),
257            },
258        },
259        JsonValue::Bool(b) => TomlValue::Boolean(b),
260        JsonValue::Array(a) => TomlValue::Array(a.into_iter().map(json_to_toml_value).collect()),
261        JsonValue::Object(o) => {
262            TomlValue::Table(o.into_iter().map(|(k, v)| (k, json_to_toml_value(v))).collect())
263        }
264        JsonValue::Null => TomlValue::String("null".to_string()),
265    }
266}