1use std::{
7 collections::HashMap,
8 fs::{self, read_to_string},
9 path::Path,
10 str::FromStr,
11};
12
13use ethbind_json::{
14 AbiField, Array, ArrayM, Constructor, Error, Event, FixedMN, Function, HardhatArtifact,
15 IntegerM, Type,
16};
17use serde::{Deserialize, Serialize};
18use thiserror::Error;
19
20#[derive(Debug, Error)]
22pub enum BindError {
23 #[error("Runtime binder didn't found mapping runtime type for {0}")]
26 UnknownType(String),
27}
28
29pub trait Generatable {
33 fn generate<C: Context>(&self, context: &mut C) -> anyhow::Result<()>;
35}
36
37pub trait Context {
39 type Runtime: RuntimeBinder;
41
42 type Language: Generator;
44
45 fn get_mut(&mut self) -> (&mut Self::Language, &mut Self::Runtime);
47
48 fn finalize(self) -> (Self::Language, Self::Runtime);
49}
50
51pub trait RuntimeBinder {
53 fn to_runtime_type(&mut self, r#type: &Type) -> anyhow::Result<Option<&str>>;
57
58 fn get(&mut self, name: &str) -> anyhow::Result<&str>;
60}
61
62pub trait Generator {
66 fn begin<R: RuntimeBinder>(&mut self, runtime_binder: &mut R, name: &str)
68 -> anyhow::Result<()>;
69
70 fn end<R: RuntimeBinder>(&mut self, runtime_binder: &mut R, name: &str) -> anyhow::Result<()>;
72
73 fn generate_fn<R: RuntimeBinder>(
75 &mut self,
76 runtime_binder: &mut R,
77 r#fn: &Function,
78 ) -> anyhow::Result<()>;
79
80 fn generate_deploy<R: RuntimeBinder>(
82 &mut self,
83 runtime_binder: &mut R,
84 contructor: &Constructor,
85 deploy_bytes: &str,
86 ) -> anyhow::Result<()>;
87
88 fn generate_event<R: RuntimeBinder>(
90 &mut self,
91 runtime_binder: &mut R,
92 event: &Event,
93 ) -> anyhow::Result<()>;
94
95 fn generate_error<R: RuntimeBinder>(
97 &mut self,
98 runtime_binder: &mut R,
99 error: &Error,
100 ) -> anyhow::Result<()>;
101
102 fn finalize<R: RuntimeBinder>(self, runtime_binder: &mut R) -> anyhow::Result<Vec<Contract>>;
104}
105
106pub struct Contract {
108 pub files: Vec<File>,
109}
110
111pub struct File {
113 pub name: String,
114 pub data: String,
115}
116
117pub trait SaveTo {
118 fn save_to<P: AsRef<Path>>(&self, output_dir: P) -> anyhow::Result<()>;
119}
120
121impl SaveTo for Contract {
122 fn save_to<P: AsRef<Path>>(&self, output_dir: P) -> anyhow::Result<()> {
124 if !output_dir.as_ref().exists() {
125 fs::create_dir_all(&output_dir)?;
126 }
127
128 for file in &self.files {
129 let path = output_dir.as_ref().join(&file.name);
130
131 fs::write(path, &file.data)?;
132 }
133
134 Ok(())
135 }
136}
137
138impl SaveTo for Vec<Contract> {
139 fn save_to<P: AsRef<Path>>(&self, output_dir: P) -> anyhow::Result<()> {
140 for c in self {
141 c.save_to(&output_dir)?;
142 }
143
144 Ok(())
145 }
146}
147
148#[derive(Debug, Serialize, Deserialize)]
150pub struct JsonRuntimeBinder {
151 #[serde(flatten)]
152 runtime_types: HashMap<String, String>,
153
154 #[serde(skip)]
155 components: HashMap<String, String>,
156}
157
158impl FromStr for JsonRuntimeBinder {
159 type Err = serde_json::Error;
160
161 fn from_str(s: &str) -> Result<Self, Self::Err> {
162 serde_json::from_str(s)
163 }
164}
165
166impl TryFrom<&str> for JsonRuntimeBinder {
167 type Error = serde_json::Error;
168
169 fn try_from(value: &str) -> Result<Self, Self::Error> {
170 serde_json::from_str(value.as_ref())
171 }
172}
173
174impl TryFrom<String> for JsonRuntimeBinder {
175 type Error = serde_json::Error;
176
177 fn try_from(value: String) -> Result<Self, Self::Error> {
178 serde_json::from_str(value.as_ref())
179 }
180}
181
182impl TryFrom<&[u8]> for JsonRuntimeBinder {
183 type Error = serde_json::Error;
184
185 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
186 serde_json::from_slice(value)
187 }
188}
189
190impl JsonRuntimeBinder {
191 pub fn from_reader<R>(reader: R) -> anyhow::Result<Self>
193 where
194 R: std::io::Read,
195 {
196 Ok(serde_json::from_reader(reader)?)
197 }
198
199 pub fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
201 Ok(fs::read_to_string(path)?.try_into()?)
202 }
203
204 fn search_basic_type<T: AsRef<str>>(&self, type_name: T) -> anyhow::Result<&str> {
206 self.runtime_types
207 .get(type_name.as_ref())
208 .map(|c| c.as_str())
209 .ok_or(BindError::UnknownType(type_name.as_ref().to_owned()).into())
210 }
211
212 fn to_array_m(&mut self, element: &ArrayM) -> anyhow::Result<Option<&str>> {
213 let tag = element.to_string();
214
215 if self.components.contains_key(&tag) {
217 return Ok(self.components.get(&tag).map(|c| c.as_str()));
218 }
219
220 let el_type = { self.to_runtime_type(&element.element)?.map(|c| c.clone()) };
221
222 if el_type.is_none() {
223 return Ok(None);
224 }
225
226 let el_type = el_type.unwrap().to_owned();
227
228 let runtime_type = self.search_basic_type("array_m")?;
229
230 let m = element.m;
231
232 let declare_type = runtime_type
233 .replace("$el", &el_type)
234 .replace("$m", &m.to_string());
235
236 self.components.insert(tag.clone(), declare_type);
237
238 Ok(self.components.get(&tag).map(|c| c.as_str()))
239 }
240
241 fn to_bytes_m(&mut self, m: usize) -> anyhow::Result<&str> {
242 let tag = format!("array{}", m);
243
244 if self.components.contains_key(&tag) {
246 return Ok(self.components.get(&tag).unwrap());
247 }
248
249 let runtime_type = self.search_basic_type("bytes_m")?;
250
251 let declare_type = runtime_type.replace("$m", &m.to_string());
252
253 self.components.insert(tag.clone(), declare_type);
254
255 Ok(self.components.get(&tag).unwrap())
256 }
257
258 fn to_array(&mut self, element: &Array) -> anyhow::Result<Option<&str>> {
259 let tag = element.to_string();
260
261 if self.components.contains_key(&tag) {
263 return Ok(self.components.get(&tag).map(|c| c.as_str()));
264 }
265
266 let el_type = { self.to_runtime_type(&element.element)?.map(|c| c.clone()) };
267
268 if el_type.is_none() {
269 return Ok(None);
270 }
271
272 let el_type = el_type.unwrap().to_owned();
273
274 let runtime_type = { self.search_basic_type("array")? };
275
276 let declare_type = runtime_type.replace("$el", &el_type);
277
278 self.components.insert(tag.clone(), declare_type);
279
280 Ok(self.components.get(&tag).map(|c| c.as_str()))
281 }
282
283 fn to_fixed_m_n(&mut self, fixed: &FixedMN) -> anyhow::Result<&str> {
284 let tag = fixed.to_string();
285
286 if self.components.contains_key(&tag) {
288 return Ok(self.components.get(&tag).unwrap());
289 }
290
291 let runtime_type = self.search_basic_type("fixed_m_n")?;
292
293 let declare_type = runtime_type
294 .replace("$m", &fixed.m.to_string())
295 .replace("$n", &fixed.n.to_string());
296
297 self.components.insert(tag.clone(), declare_type);
298
299 Ok(self.components.get(&tag).unwrap())
300 }
301
302 fn to_integer_m(&mut self, integer_m: &IntegerM) -> anyhow::Result<&str> {
303 let tag = integer_m.to_string();
304
305 if self.components.contains_key(&tag) {
307 return Ok(self.components.get(&tag).unwrap());
308 }
309
310 let runtime_type = if integer_m.signed {
311 self.search_basic_type("int_m")?
312 } else {
313 self.search_basic_type("uint_m")?
314 };
315
316 let declare_type = runtime_type.replace("$m", &integer_m.m.to_string());
317
318 self.components.insert(tag.clone(), declare_type);
319
320 Ok(self.components.get(&tag).unwrap())
321 }
322}
323
324impl RuntimeBinder for JsonRuntimeBinder {
325 fn to_runtime_type(&mut self, r#type: &Type) -> anyhow::Result<Option<&str>> {
326 match r#type {
327 Type::Simple(element) if element.is_tuple() => Ok(None),
328 Type::Simple(element) => self.search_basic_type(element.to_string()).map(|c| Some(c)),
329 Type::ArrayM(element) => self.to_array_m(&element),
330 Type::Array(element) => self.to_array(&element),
331 Type::BytesM(element) => self.to_bytes_m(element.m).map(|c| Some(c)),
332 Type::FixedMN(element) => self.to_fixed_m_n(element).map(|c| Some(c)),
333 Type::IntegerM(element) => self.to_integer_m(element).map(|c| Some(c)),
334 }
335 }
336
337 fn get(&mut self, name: &str) -> anyhow::Result<&str> {
338 Ok(self
339 .runtime_types
340 .get(name)
341 .ok_or(BindError::UnknownType(name.to_string()))?)
342 }
343}
344
345pub struct Executor<L, R> {
347 generator: L,
348 runtime_binder: R,
349}
350
351impl<L, R> Context for Executor<L, R>
352where
353 R: RuntimeBinder,
354 L: Generator,
355{
356 type Language = L;
357 type Runtime = R;
358
359 fn get_mut(&mut self) -> (&mut Self::Language, &mut Self::Runtime) {
360 (&mut self.generator, &mut self.runtime_binder)
361 }
362
363 fn finalize(self) -> (Self::Language, Self::Runtime) {
364 (self.generator, self.runtime_binder)
365 }
366}
367
368impl<L, R> From<(L, R)> for Executor<L, R>
369where
370 R: RuntimeBinder,
371 L: Generator,
372{
373 fn from(value: (L, R)) -> Self {
374 Executor {
375 generator: value.0,
376 runtime_binder: value.1,
377 }
378 }
379}
380
381pub struct BindingBuilder<C: Context> {
382 context: C,
383 builders: Vec<Box<dyn Fn(&mut C) -> anyhow::Result<()>>>,
384}
385
386impl<C: Context + Default> Default for BindingBuilder<C> {
387 fn default() -> Self {
388 Self {
389 context: Default::default(),
390 builders: Default::default(),
391 }
392 }
393}
394
395impl<C: Context> BindingBuilder<C> {
396 pub fn new<C1: Into<C>>(context: C1) -> Self {
398 Self {
399 context: context.into(),
400 builders: Default::default(),
401 }
402 }
403
404 pub fn bind<S: AsRef<str> + 'static, CN: AsRef<str>>(
406 mut self,
407 contract_name: CN,
408 contract: S,
409 ) -> Self {
410 let contract_name = contract_name.as_ref().to_string();
411
412 self.builders.push(Box::new(move |c| {
413 let fields: Vec<AbiField> = serde_json::from_str(contract.as_ref())?;
414
415 {
416 let (generator, runtime_binder) = c.get_mut();
417
418 generator.begin(runtime_binder, &contract_name)?;
419 }
420
421 fields.generate(c)?;
422
423 Ok(())
424 }));
425
426 self
427 }
428
429 pub fn bind_hardhat<S: AsRef<str> + 'static>(mut self, contract: S) -> Self {
431 self.builders.push(Box::new(move |c| {
432 let fields: HardhatArtifact = serde_json::from_str(contract.as_ref())?;
433
434 {
435 let (generator, runtime_binder) = c.get_mut();
436
437 generator.begin(runtime_binder, &fields.contract_name)?;
438 }
439
440 fields.generate(c)?;
441
442 Ok(())
443 }));
444
445 self
446 }
447
448 pub fn bind_file<P: AsRef<Path> + 'static, CN: AsRef<str>>(
450 mut self,
451 contract_name: CN,
452 path: P,
453 ) -> Self {
454 let contract_name = contract_name.as_ref().to_string();
455
456 self.builders.push(Box::new(move |c| {
457 let contract = read_to_string(&path)?;
458
459 let fields: Vec<AbiField> = serde_json::from_str(contract.as_ref())?;
460
461 {
462 let (generator, runtime_binder) = c.get_mut();
463
464 generator.begin(runtime_binder, &contract_name)?;
465 }
466
467 fields.generate(c)?;
468
469 Ok(())
470 }));
471
472 self
473 }
474
475 pub fn bind_hardhat_file<P: AsRef<Path> + 'static>(mut self, path: P) -> Self {
477 self.builders.push(Box::new(move |c| {
478 let contract = read_to_string(&path)?;
479
480 let fields: HardhatArtifact = serde_json::from_str(contract.as_ref())?;
481
482 {
483 let (generator, runtime_binder) = c.get_mut();
484
485 generator.begin(runtime_binder, &fields.contract_name)?;
486 }
487
488 fields.generate(c)?;
489
490 Ok(())
491 }));
492
493 self
494 }
495
496 pub fn finalize(mut self) -> anyhow::Result<Vec<Contract>> {
498 for builder in self.builders {
499 builder(&mut self.context)?;
500 }
501
502 let (generator, mut runtime_binder) = self.context.finalize();
503
504 Ok(generator.finalize(&mut runtime_binder)?)
505 }
506}
507
508impl Generatable for HardhatArtifact {
509 fn generate<C: Context>(&self, context: &mut C) -> anyhow::Result<()> {
510 self.abi.generate(context)?;
511
512 for abi in &self.abi {
513 match abi {
514 AbiField::Constructor(contructor) => {
515 let (generator, runtime_binder) = context.get_mut();
517
518 generator.generate_deploy(runtime_binder, contructor, &self.bytecode)?;
519 }
520 _ => {}
521 }
522 }
523
524 Ok(())
525 }
526}
527
528impl Generatable for Vec<AbiField> {
529 fn generate<C: Context>(&self, context: &mut C) -> anyhow::Result<()> {
530 let (generator, runtime_binder) = context.get_mut();
531
532 for abi in self {
533 match abi {
534 AbiField::Function(function) => generator.generate_fn(runtime_binder, function)?,
535 AbiField::Event(event) => generator.generate_event(runtime_binder, event)?,
536 AbiField::Error(error) => generator.generate_error(runtime_binder, error)?,
537 _ => {
538 }
542 }
543 }
544
545 Ok(())
546 }
547}
548
549#[cfg(test)]
550mod tests {
551 use ethbind_json::Type;
552
553 use crate::{JsonRuntimeBinder, RuntimeBinder};
554
555 #[test]
556 fn test_json_runtime_binder() {
557 let mut runtime_binder: JsonRuntimeBinder =
558 include_str!("../../rust/macros/tests/mapping.json")
559 .parse()
560 .expect("Load abi");
561
562 let t: Type = "uint256".parse().expect("Parse type string");
563
564 let runtime_type = runtime_binder
565 .to_runtime_type(&t)
566 .expect("Get runtime type")
567 .expect("Is not tuple type");
568
569 assert_eq!(runtime_type, "mock::Int<false,256>");
570
571 let t: Type = "fixed128x8".parse().expect("Parse type string");
572
573 let runtime_type = runtime_binder
574 .to_runtime_type(&t)
575 .expect("Get runtime type")
576 .expect("Is not tuple type");
577
578 assert_eq!(runtime_type, "mock::Fixed<true,128,8>");
579
580 let t: Type = "uint256[20]".parse().expect("Parse type string");
581
582 let runtime_type = runtime_binder
583 .to_runtime_type(&t)
584 .expect("Get runtime type")
585 .expect("Is not tuple type");
586
587 assert_eq!(runtime_type, "[mock::Int<false,256>;20]");
588
589 let t: Type = "uint256[]".parse().expect("Parse type string");
590
591 let runtime_type = runtime_binder
592 .to_runtime_type(&t)
593 .expect("Get runtime type")
594 .expect("Is not tuple type");
595
596 assert_eq!(runtime_type, "Vec<mock::Int<false,256>>");
597
598 let t: Type = "bytes24".parse().expect("Parse type string");
599
600 let runtime_type = runtime_binder
601 .to_runtime_type(&t)
602 .expect("Get runtime type")
603 .expect("Is not tuple type");
604
605 assert_eq!(runtime_type, "[u8;24]");
606
607 let t: Type = "address".parse().expect("Parse type string");
608
609 let runtime_type = runtime_binder
610 .to_runtime_type(&t)
611 .expect("Get runtime type")
612 .expect("Is not tuple type");
613
614 assert_eq!(runtime_type, "mock::Address");
615 }
616}