revive_solc_json_interface/standard_json/input/settings/
libraries.rs

1//! The Solidity libraries.
2
3use std::collections::BTreeMap;
4use std::collections::BTreeSet;
5
6use serde::Deserialize;
7use serde::Serialize;
8
9/// The Solidity libraries.
10#[derive(Debug, Default, Clone, Serialize, Deserialize)]
11pub struct Libraries {
12    /// The unified representation of libraries.
13    #[serde(flatten)]
14    pub inner: BTreeMap<String, BTreeMap<String, String>>,
15}
16
17impl Libraries {
18    /// Returns a representation of libraries suitable for the LLD linker.
19    pub fn as_linker_symbols(
20        &self,
21    ) -> anyhow::Result<BTreeMap<String, [u8; revive_common::BYTE_LENGTH_ETH_ADDRESS]>> {
22        let mut linker_symbols = BTreeMap::new();
23        for (file, contracts) in self.inner.iter() {
24            for (name, address) in contracts.iter() {
25                let path = format!("{file}:{name}");
26
27                let address_stripped = address.strip_prefix("0x").unwrap_or(address.as_str());
28                let address_vec = hex::decode(address_stripped).map_err(|error| {
29                    anyhow::anyhow!("Invalid address `{address}` of library `{path}`: {error}.")
30                })?;
31                let address_array: [u8; revive_common::BYTE_LENGTH_ETH_ADDRESS] = address_vec.try_into().map_err(|address_vec: Vec<u8>| {
32                    anyhow::anyhow!(
33                        "Incorrect size of address `{address}` of library `{path}`: expected {}, found {}.",
34                        revive_common::BYTE_LENGTH_ETH_ADDRESS,
35                        address_vec.len(),
36                    )
37                })?;
38
39                linker_symbols.insert(path, address_array);
40            }
41        }
42        Ok(linker_symbols)
43    }
44
45    /// Returns a representation of libraries suitable for filtering.
46    pub fn as_paths(&self) -> BTreeSet<String> {
47        self.inner
48            .iter()
49            .flat_map(|(file, names)| {
50                names
51                    .keys()
52                    .map(|name| format!("{file}:{name}"))
53                    .collect::<BTreeSet<String>>()
54            })
55            .collect::<BTreeSet<String>>()
56    }
57
58    /// Checks whether the libraries are empty.
59    pub fn is_empty(&self) -> bool {
60        self.inner.is_empty()
61    }
62
63    /// Returns a reference to the inner value.
64    pub fn as_inner(&self) -> &BTreeMap<String, BTreeMap<String, String>> {
65        &self.inner
66    }
67
68    /// Returns a mutable reference to the inner value.
69    pub fn as_inner_mut(&mut self) -> &mut BTreeMap<String, BTreeMap<String, String>> {
70        &mut self.inner
71    }
72}
73
74impl From<BTreeMap<String, BTreeMap<String, String>>> for Libraries {
75    fn from(inner: BTreeMap<String, BTreeMap<String, String>>) -> Self {
76        Self { inner }
77    }
78}
79
80impl TryFrom<&[String]> for Libraries {
81    type Error = anyhow::Error;
82
83    fn try_from(arguments: &[String]) -> Result<Self, Self::Error> {
84        let mut libraries = BTreeMap::new();
85        for (index, library) in arguments.iter().enumerate() {
86            let mut path_and_address = library.split('=');
87            let path = path_and_address
88                .next()
89                .ok_or_else(|| anyhow::anyhow!("Library #{index} path is missing."))?;
90            let mut file_and_contract = path.split(':');
91            let file = file_and_contract
92                .next()
93                .ok_or_else(|| anyhow::anyhow!("Library `{path}` file name is missing."))?;
94            let contract = file_and_contract
95                .next()
96                .ok_or_else(|| anyhow::anyhow!("Library `{path}` contract name is missing."))?;
97            let address = path_and_address
98                .next()
99                .ok_or_else(|| anyhow::anyhow!("Library `{path}` address is missing."))?;
100            libraries
101                .entry(file.to_owned())
102                .or_insert_with(BTreeMap::new)
103                .insert(contract.to_owned(), address.to_owned());
104        }
105        Ok(Self { inner: libraries })
106    }
107}