1use crate::{
4 Cheatcode, Cheatcodes, Result,
5 Vm::*,
6 json::{
7 check_json_key_exists, parse_json, parse_json_coerce, parse_json_coerce_default,
8 parse_json_keys, resolve_type, 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 foundry_evm_core::evm::FoundryEvmNetwork;
16use serde_json::Value as JsonValue;
17use toml::Value as TomlValue;
18
19impl Cheatcode for keyExistsTomlCall {
20 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
21 let Self { toml, key } = self;
22 check_json_key_exists(&toml_to_json_string(toml)?, key)
23 }
24}
25
26impl Cheatcode for parseToml_0Call {
27 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
28 let Self { toml } = self;
29 parse_toml(
30 toml,
31 "$",
32 state.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok()),
33 )
34 }
35}
36
37impl Cheatcode for parseToml_1Call {
38 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
39 let Self { toml, key } = self;
40 parse_toml(
41 toml,
42 key,
43 state.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok()),
44 )
45 }
46}
47
48macro_rules! impl_parse_toml {
49 ($call:ident, $call_with_default:ident, $ty:expr) => {
50 impl Cheatcode for $call {
51 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
52 let Self { toml, key } = self;
53 parse_toml_coerce(toml, key, &$ty)
54 }
55 }
56
57 impl Cheatcode for $call_with_default {
58 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
59 let Self { toml, key, defaultValue } = self;
60 parse_toml_coerce_default(toml, key, &$ty, defaultValue)
61 }
62 }
63 };
64}
65
66impl_parse_toml!(parseTomlUint_0Call, parseTomlUint_1Call, DynSolType::Uint(256));
67impl_parse_toml!(
68 parseTomlUintArray_0Call,
69 parseTomlUintArray_1Call,
70 DynSolType::Array(Box::new(DynSolType::Uint(256)))
71);
72impl_parse_toml!(parseTomlInt_0Call, parseTomlInt_1Call, DynSolType::Int(256));
73impl_parse_toml!(
74 parseTomlIntArray_0Call,
75 parseTomlIntArray_1Call,
76 DynSolType::Array(Box::new(DynSolType::Int(256)))
77);
78impl_parse_toml!(parseTomlBool_0Call, parseTomlBool_1Call, DynSolType::Bool);
79impl_parse_toml!(
80 parseTomlBoolArray_0Call,
81 parseTomlBoolArray_1Call,
82 DynSolType::Array(Box::new(DynSolType::Bool))
83);
84impl_parse_toml!(parseTomlAddress_0Call, parseTomlAddress_1Call, DynSolType::Address);
85impl_parse_toml!(
86 parseTomlAddressArray_0Call,
87 parseTomlAddressArray_1Call,
88 DynSolType::Array(Box::new(DynSolType::Address))
89);
90impl_parse_toml!(parseTomlString_0Call, parseTomlString_1Call, DynSolType::String);
91impl_parse_toml!(
92 parseTomlStringArray_0Call,
93 parseTomlStringArray_1Call,
94 DynSolType::Array(Box::new(DynSolType::String))
95);
96impl_parse_toml!(parseTomlBytes_0Call, parseTomlBytes_1Call, DynSolType::Bytes);
97impl_parse_toml!(
98 parseTomlBytesArray_0Call,
99 parseTomlBytesArray_1Call,
100 DynSolType::Array(Box::new(DynSolType::Bytes))
101);
102impl_parse_toml!(parseTomlBytes32_0Call, parseTomlBytes32_1Call, DynSolType::FixedBytes(32));
103impl_parse_toml!(
104 parseTomlBytes32Array_0Call,
105 parseTomlBytes32Array_1Call,
106 DynSolType::Array(Box::new(DynSolType::FixedBytes(32)))
107);
108
109impl Cheatcode for parseTomlType_0Call {
110 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
111 let Self { toml, typeDescription } = self;
112 parse_toml_coerce(
113 toml,
114 "$",
115 &resolve_type(
116 typeDescription,
117 state.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok()),
118 )?,
119 )
120 .map(|v| v.abi_encode())
121 }
122}
123
124impl Cheatcode for parseTomlType_1Call {
125 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
126 let Self { toml, key, typeDescription } = self;
127 parse_toml_coerce(
128 toml,
129 key,
130 &resolve_type(
131 typeDescription,
132 state.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok()),
133 )?,
134 )
135 .map(|v| v.abi_encode())
136 }
137}
138
139impl Cheatcode for parseTomlTypeArrayCall {
140 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
141 let Self { toml, key, typeDescription } = self;
142 let ty = resolve_type(
143 typeDescription,
144 state.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok()),
145 )?;
146 parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(ty))).map(|v| v.abi_encode())
147 }
148}
149
150impl Cheatcode for parseTomlKeysCall {
151 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
152 let Self { toml, key } = self;
153 parse_toml_keys(toml, key)
154 }
155}
156
157impl Cheatcode for writeToml_0Call {
158 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
159 let Self { json, path } = self;
160 let value =
161 serde_json::from_str(json).unwrap_or_else(|_| JsonValue::String(json.to_owned()));
162
163 let toml_string = format_json_to_toml(value)?;
164 super::fs::write_file(state, path.as_ref(), toml_string.as_bytes())
165 }
166}
167
168impl Cheatcode for writeToml_1Call {
169 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
170 let Self { json: value, path, valueKey } = self;
171
172 let data_path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?;
175 let mut json_data: JsonValue = if data_path.exists() {
176 let toml_data = fs::locked_read_to_string(&data_path)?;
177 toml::from_str(&toml_data).map_err(|e| fmt_err!("failed parsing TOML: {e}"))?
178 } else {
179 JsonValue::Object(Default::default())
180 };
181 upsert_json_value(&mut json_data, value, valueKey)?;
182
183 let toml_string = format_json_to_toml(json_data)?;
185 super::fs::write_file(state, path.as_ref(), toml_string.as_bytes())
186 }
187}
188
189fn parse_toml_str(toml: &str) -> Result<TomlValue> {
191 toml::from_str(toml).map_err(|e| fmt_err!("failed parsing TOML: {e}"))
192}
193
194fn parse_toml(toml: &str, key: &str, struct_defs: Option<&StructDefinitions>) -> Result {
196 parse_json(&toml_to_json_string(toml)?, key, struct_defs)
197}
198
199fn parse_toml_coerce(toml: &str, key: &str, ty: &DynSolType) -> Result {
201 parse_json_coerce(&toml_to_json_string(toml)?, key, ty)
202}
203
204fn parse_toml_coerce_default<T: SolValue>(
207 toml: &str,
208 key: &str,
209 ty: &DynSolType,
210 default: &T,
211) -> Result {
212 parse_json_coerce_default(&toml_to_json_string(toml)?, key, ty, default)
213}
214
215fn parse_toml_keys(toml: &str, key: &str) -> Result {
217 parse_json_keys(&toml_to_json_string(toml)?, key)
218}
219
220fn toml_to_json_string(toml: &str) -> Result<String> {
222 let toml = parse_toml_str(toml)?;
223 let json = toml_to_json_value(toml);
224 serde_json::to_string(&json).map_err(|e| fmt_err!("failed to serialize JSON: {e}"))
225}
226
227fn format_json_to_toml(json: JsonValue) -> Result<String> {
229 let toml = json_to_toml_value(json);
230 toml::to_string_pretty(&toml).map_err(|e| fmt_err!("failed to serialize TOML: {e}"))
231}
232
233pub(super) fn toml_to_json_value(toml: TomlValue) -> JsonValue {
235 match toml {
236 TomlValue::String(s) => match s.as_str() {
237 "null" => JsonValue::Null,
238 _ => JsonValue::String(s),
239 },
240 TomlValue::Integer(i) => JsonValue::Number(i.into()),
241 TomlValue::Float(f) => match serde_json::Number::from_f64(f) {
242 Some(n) => JsonValue::Number(n),
243 None => JsonValue::String(f.to_string()),
244 },
245 TomlValue::Boolean(b) => JsonValue::Bool(b),
246 TomlValue::Array(a) => JsonValue::Array(a.into_iter().map(toml_to_json_value).collect()),
247 TomlValue::Table(t) => {
248 JsonValue::Object(t.into_iter().map(|(k, v)| (k, toml_to_json_value(v))).collect())
249 }
250 TomlValue::Datetime(d) => JsonValue::String(d.to_string()),
251 }
252}
253
254fn json_to_toml_value(json: JsonValue) -> TomlValue {
256 match json {
257 JsonValue::String(s) => TomlValue::String(s),
258 JsonValue::Number(n) => match n.as_i64() {
259 Some(i) => TomlValue::Integer(i),
260 None => match n.as_f64() {
261 Some(f) => TomlValue::Float(f),
262 None => TomlValue::String(n.to_string()),
263 },
264 },
265 JsonValue::Bool(b) => TomlValue::Boolean(b),
266 JsonValue::Array(a) => TomlValue::Array(a.into_iter().map(json_to_toml_value).collect()),
267 JsonValue::Object(o) => {
268 TomlValue::Table(o.into_iter().map(|(k, v)| (k, json_to_toml_value(v))).collect())
269 }
270 JsonValue::Null => TomlValue::String("null".to_string()),
271 }
272}