1use crate::{utils, CompilerInput, CompilerOutput, SolcError};
4use md5::Digest;
5use semver::Version;
6use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
7use std::{cell::RefCell, path::Path, rc::Rc};
8
9pub const ETHERS_FORMAT_VERSION: &str = "ethers-rs-sol-build-info-1";
10
11#[derive(Serialize, Deserialize)]
13#[serde(rename_all = "camelCase")]
14pub struct BuildInfo {
15 pub id: String,
16 #[serde(rename = "_format")]
17 pub format: String,
18 pub solc_version: Version,
19 pub solc_long_version: Version,
20 pub input: CompilerInput,
21 pub output: CompilerOutput,
22}
23
24impl BuildInfo {
25 pub fn read(path: impl AsRef<Path>) -> Result<Self, SolcError> {
27 utils::read_json_file(path)
28 }
29}
30
31#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
33pub struct RawBuildInfo {
34 pub id: String,
36 pub build_info: String,
38}
39
40impl RawBuildInfo {
43 pub fn new(
45 input: &CompilerInput,
46 output: &CompilerOutput,
47 version: &Version,
48 ) -> serde_json::Result<RawBuildInfo> {
49 let mut hasher = md5::Md5::new();
50 let w = BuildInfoWriter { buf: Rc::new(RefCell::new(Vec::with_capacity(128))) };
51 let mut buf = w.clone();
52 let mut serializer = serde_json::Serializer::pretty(&mut buf);
53 let mut s = serializer.serialize_struct("BuildInfo", 6)?;
54 s.serialize_field("_format", ÐERS_FORMAT_VERSION)?;
55 let solc_short = format!("{}.{}.{}", version.major, version.minor, version.patch);
56 s.serialize_field("solcVersion", &solc_short)?;
57 s.serialize_field("solcLongVersion", &version)?;
58 s.serialize_field("input", input)?;
59
60 hasher.update(&*w.buf.borrow());
64 let result = hasher.finalize();
65 let id = hex::encode(result);
66
67 s.serialize_field("id", &id)?;
68 s.serialize_field("output", output)?;
69 s.end()?;
70
71 drop(buf);
72
73 let build_info = unsafe {
74 String::from_utf8_unchecked(w.buf.take())
76 };
77
78 Ok(RawBuildInfo { id, build_info })
79 }
80}
81
82#[derive(Clone)]
83struct BuildInfoWriter {
84 buf: Rc<RefCell<Vec<u8>>>,
85}
86
87impl std::io::Write for BuildInfoWriter {
88 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
89 self.buf.borrow_mut().write(buf)
90 }
91
92 fn flush(&mut self) -> std::io::Result<()> {
93 self.buf.borrow_mut().flush()
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::Source;
101 use std::{collections::BTreeMap, path::PathBuf};
102
103 #[test]
104 fn build_info_serde() {
105 let inputs = CompilerInput::with_sources(BTreeMap::from([(
106 PathBuf::from("input.sol"),
107 Source::new(""),
108 )]));
109 let output = CompilerOutput::default();
110 let v: Version = "0.8.4+commit.c7e474f2".parse().unwrap();
111 let raw_info = RawBuildInfo::new(&inputs[0], &output, &v).unwrap();
112 let _info: BuildInfo = serde_json::from_str(&raw_info.build_info).unwrap();
113 }
114}