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