foundry_cheatcodes/
toml.rs

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