solar_config/
lib.rs

1#![doc = include_str!("../README.md")]
2#![doc(
3    html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/solar/main/assets/logo.png",
4    html_favicon_url = "https://raw.githubusercontent.com/paradigmxyz/solar/main/assets/favicon.ico"
5)]
6#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
7
8use std::{num::NonZeroUsize, path::PathBuf};
9use strum::EnumIs;
10
11#[macro_use]
12mod macros;
13
14mod opts;
15pub use opts::{Opts, UnstableOpts};
16
17mod utils;
18
19#[cfg(feature = "version")]
20pub mod version;
21
22str_enum! {
23    /// Compiler stage.
24    #[derive(strum::EnumIs)]
25    #[strum(serialize_all = "lowercase")]
26    pub enum CompilerStage {
27        /// Source code was parsed into an AST.
28        #[strum(serialize = "parsed", serialize = "parsing")]
29        Parsed,
30        // TODO: More
31    }
32}
33
34str_enum! {
35    /// Source code language.
36    #[derive(Default)]
37    #[derive(strum::EnumIs)]
38    #[strum(serialize_all = "lowercase")]
39    pub enum Language {
40        #[default]
41        Solidity,
42        Yul,
43    }
44}
45
46str_enum! {
47    /// A version specifier of the EVM we want to compile to.
48    ///
49    /// Defaults to the latest version deployed on Ethereum Mainnet at the time of compiler release.
50    #[derive(Default)]
51    #[strum(serialize_all = "camelCase")]
52    pub enum EvmVersion {
53        // NOTE: Order matters.
54        Homestead,
55        TangerineWhistle,
56        SpuriousDragon,
57        Byzantium,
58        Constantinople,
59        Petersburg,
60        Istanbul,
61        Berlin,
62        London,
63        Paris,
64        Shanghai,
65        #[default]
66        Cancun,
67        Prague,
68    }
69}
70
71impl EvmVersion {
72    pub fn supports_returndata(self) -> bool {
73        self >= Self::Byzantium
74    }
75    pub fn has_static_call(self) -> bool {
76        self >= Self::Byzantium
77    }
78    pub fn has_bitwise_shifting(self) -> bool {
79        self >= Self::Constantinople
80    }
81    pub fn has_create2(self) -> bool {
82        self >= Self::Constantinople
83    }
84    pub fn has_ext_code_hash(self) -> bool {
85        self >= Self::Constantinople
86    }
87    pub fn has_chain_id(self) -> bool {
88        self >= Self::Istanbul
89    }
90    pub fn has_self_balance(self) -> bool {
91        self >= Self::Istanbul
92    }
93    pub fn has_base_fee(self) -> bool {
94        self >= Self::London
95    }
96    pub fn has_blob_base_fee(self) -> bool {
97        self >= Self::Cancun
98    }
99    pub fn has_prev_randao(self) -> bool {
100        self >= Self::Paris
101    }
102    pub fn has_push0(self) -> bool {
103        self >= Self::Shanghai
104    }
105}
106
107str_enum! {
108    /// Type of output for the compiler to emit.
109    #[strum(serialize_all = "kebab-case")]
110    pub enum CompilerOutput {
111        /// JSON ABI.
112        Abi,
113        // /// Creation bytecode.
114        // Bin,
115        // /// Runtime bytecode.
116        // BinRuntime,
117        /// Function signature hashes.
118        Hashes,
119    }
120}
121
122/// `-Zdump=kind[=paths...]`.
123#[derive(Clone, Debug)]
124pub struct Dump {
125    pub kind: DumpKind,
126    pub paths: Option<Vec<String>>,
127}
128
129impl std::str::FromStr for Dump {
130    type Err = String;
131
132    fn from_str(s: &str) -> Result<Self, Self::Err> {
133        let (kind, paths) = if let Some((kind, paths)) = s.split_once('=') {
134            let paths = paths.split(',').map(ToString::to_string).collect();
135            (kind, Some(paths))
136        } else {
137            (s, None)
138        };
139        Ok(Self { kind: kind.parse::<DumpKind>().map_err(|e| e.to_string())?, paths })
140    }
141}
142
143str_enum! {
144    /// What kind of output to dump. See [`Dump`].
145    #[derive(EnumIs)]
146    #[strum(serialize_all = "kebab-case")]
147    pub enum DumpKind {
148        /// Print the AST.
149        Ast,
150        /// Print the HIR.
151        Hir,
152    }
153}
154
155str_enum! {
156    /// How errors and other messages are produced.
157    #[derive(Default)]
158    #[strum(serialize_all = "kebab-case")]
159    pub enum ErrorFormat {
160        /// Human-readable output.
161        #[default]
162        Human,
163        /// Solc-like JSON output.
164        Json,
165        /// Rustc-like JSON output.
166        RustcJson,
167    }
168}
169
170/// A single import map, AKA remapping: `map=path`.
171#[derive(Clone, Debug)]
172pub struct ImportMap {
173    pub map: PathBuf,
174    pub path: PathBuf,
175}
176
177impl std::str::FromStr for ImportMap {
178    type Err = &'static str;
179
180    fn from_str(s: &str) -> Result<Self, Self::Err> {
181        if let Some((a, b)) = s.split_once('=') {
182            Ok(Self { map: a.into(), path: b.into() })
183        } else {
184            Err("missing '='")
185        }
186    }
187}
188
189/// Wrapper to implement a custom `Default` value for the number of threads.
190#[derive(Clone, Copy)]
191pub struct Threads(pub NonZeroUsize);
192
193impl From<Threads> for NonZeroUsize {
194    fn from(threads: Threads) -> Self {
195        threads.0
196    }
197}
198
199impl From<NonZeroUsize> for Threads {
200    fn from(n: NonZeroUsize) -> Self {
201        Self(n)
202    }
203}
204
205impl From<usize> for Threads {
206    fn from(n: usize) -> Self {
207        Self::resolve(n)
208    }
209}
210
211impl Default for Threads {
212    fn default() -> Self {
213        Self(NonZeroUsize::new(8).unwrap())
214    }
215}
216
217impl std::str::FromStr for Threads {
218    type Err = <NonZeroUsize as std::str::FromStr>::Err;
219
220    fn from_str(s: &str) -> Result<Self, Self::Err> {
221        s.parse::<usize>().map(Self::resolve)
222    }
223}
224
225impl std::fmt::Display for Threads {
226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227        self.0.fmt(f)
228    }
229}
230
231impl std::fmt::Debug for Threads {
232    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233        self.0.fmt(f)
234    }
235}
236
237impl Threads {
238    /// Resolves the number of threads to use.
239    pub fn resolve(n: usize) -> Self {
240        Self(
241            NonZeroUsize::new(n)
242                .or_else(|| std::thread::available_parallelism().ok())
243                .unwrap_or(NonZeroUsize::MIN),
244        )
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251    use strum::IntoEnumIterator;
252
253    #[cfg(not(feature = "serde"))]
254    use serde_json as _;
255
256    #[test]
257    fn string_enum() {
258        for value in EvmVersion::iter() {
259            let s = value.to_str();
260            assert_eq!(value.to_string(), s);
261            assert_eq!(value, s.parse().unwrap());
262
263            #[cfg(feature = "serde")]
264            {
265                let json_s = format!("\"{value}\"");
266                assert_eq!(serde_json::to_string(&value).unwrap(), json_s);
267                assert_eq!(serde_json::from_str::<EvmVersion>(&json_s).unwrap(), value);
268            }
269        }
270    }
271}