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 #[derive(strum::EnumIs)]
25 #[strum(serialize_all = "lowercase")]
26 pub enum CompilerStage {
27 #[strum(serialize = "parsed", serialize = "parsing")]
29 Parsed,
30 }
32}
33
34str_enum! {
35 #[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 #[derive(Default)]
51 #[strum(serialize_all = "camelCase")]
52 pub enum EvmVersion {
53 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 #[strum(serialize_all = "kebab-case")]
110 pub enum CompilerOutput {
111 Abi,
113 Hashes,
119 }
120}
121
122#[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 #[derive(EnumIs)]
146 #[strum(serialize_all = "kebab-case")]
147 pub enum DumpKind {
148 Ast,
150 Hir,
152 }
153}
154
155str_enum! {
156 #[derive(Default)]
158 #[strum(serialize_all = "kebab-case")]
159 pub enum ErrorFormat {
160 #[default]
162 Human,
163 Json,
165 RustcJson,
167 }
168}
169
170#[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#[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 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}