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::{fmt, num::NonZeroUsize};
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
22pub const SINGLE_THREADED_TARGET: bool =
29 cfg!(target_os = "emscripten") || cfg!(target_family = "wasm") || cfg!(target_os = "zkvm");
30
31str_enum! {
32 #[derive(strum::EnumIs)]
34 #[strum(serialize_all = "lowercase")]
35 pub enum CompilerStage {
36 #[strum(serialize = "parsed", serialize = "parsing")]
38 Parsed,
39 }
41}
42
43str_enum! {
44 #[derive(Default)]
46 #[derive(strum::EnumIs)]
47 #[strum(serialize_all = "lowercase")]
48 pub enum Language {
49 #[default]
50 Solidity,
51 Yul,
52 }
53}
54
55str_enum! {
56 #[derive(Default)]
60 #[strum(serialize_all = "camelCase")]
61 pub enum EvmVersion {
62 Homestead,
64 TangerineWhistle,
65 SpuriousDragon,
66 Byzantium,
67 Constantinople,
68 Petersburg,
69 Istanbul,
70 Berlin,
71 London,
72 Paris,
73 Shanghai,
74 #[default]
75 Cancun,
76 Prague,
77 }
78}
79
80impl EvmVersion {
81 pub fn supports_returndata(self) -> bool {
82 self >= Self::Byzantium
83 }
84 pub fn has_static_call(self) -> bool {
85 self >= Self::Byzantium
86 }
87 pub fn has_bitwise_shifting(self) -> bool {
88 self >= Self::Constantinople
89 }
90 pub fn has_create2(self) -> bool {
91 self >= Self::Constantinople
92 }
93 pub fn has_ext_code_hash(self) -> bool {
94 self >= Self::Constantinople
95 }
96 pub fn has_chain_id(self) -> bool {
97 self >= Self::Istanbul
98 }
99 pub fn has_self_balance(self) -> bool {
100 self >= Self::Istanbul
101 }
102 pub fn has_base_fee(self) -> bool {
103 self >= Self::London
104 }
105 pub fn has_blob_base_fee(self) -> bool {
106 self >= Self::Cancun
107 }
108 pub fn has_prev_randao(self) -> bool {
109 self >= Self::Paris
110 }
111 pub fn has_push0(self) -> bool {
112 self >= Self::Shanghai
113 }
114}
115
116str_enum! {
117 #[strum(serialize_all = "kebab-case")]
119 pub enum CompilerOutput {
120 Abi,
122 Hashes,
128 }
129}
130
131#[derive(Clone, Debug)]
133pub struct Dump {
134 pub kind: DumpKind,
135 pub paths: Option<Vec<String>>,
136}
137
138impl std::str::FromStr for Dump {
139 type Err = String;
140
141 fn from_str(s: &str) -> Result<Self, Self::Err> {
142 let (kind, paths) = if let Some((kind, paths)) = s.split_once('=') {
143 let paths = paths.split(',').map(ToString::to_string).collect();
144 (kind, Some(paths))
145 } else {
146 (s, None)
147 };
148 Ok(Self { kind: kind.parse::<DumpKind>().map_err(|e| e.to_string())?, paths })
149 }
150}
151
152str_enum! {
153 #[derive(EnumIs)]
155 #[strum(serialize_all = "kebab-case")]
156 pub enum DumpKind {
157 Ast,
159 Hir,
161 }
162}
163
164str_enum! {
165 #[derive(Default)]
167 #[strum(serialize_all = "kebab-case")]
168 pub enum ErrorFormat {
169 #[default]
171 Human,
172 Json,
174 RustcJson,
176 }
177}
178
179#[derive(Clone)]
181pub struct ImportRemapping {
182 pub context: String,
184 pub prefix: String,
185 pub path: String,
186}
187
188impl std::str::FromStr for ImportRemapping {
189 type Err = &'static str;
190
191 fn from_str(s: &str) -> Result<Self, Self::Err> {
192 if let Some((prefix_, path)) = s.split_once('=') {
193 let (context, prefix) = prefix_.split_once(':').unzip();
194 let prefix = prefix.unwrap_or(prefix_);
195 if prefix.is_empty() {
196 return Err("empty prefix");
197 }
198 Ok(Self {
199 context: context.unwrap_or_default().into(),
200 prefix: prefix.into(),
201 path: path.into(),
202 })
203 } else {
204 Err("missing '='")
205 }
206 }
207}
208
209impl fmt::Display for ImportRemapping {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 if !self.context.is_empty() {
212 write!(f, "{}:", self.context)?;
213 }
214 write!(f, "{}={}", self.prefix, self.path)
215 }
216}
217
218impl fmt::Debug for ImportRemapping {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 write!(f, "ImportRemapping({self})")
221 }
222}
223
224#[derive(Clone, Copy)]
226pub struct Threads(pub NonZeroUsize);
227
228impl From<Threads> for NonZeroUsize {
229 fn from(threads: Threads) -> Self {
230 threads.0
231 }
232}
233
234impl From<NonZeroUsize> for Threads {
235 fn from(n: NonZeroUsize) -> Self {
236 Self(n)
237 }
238}
239
240impl From<usize> for Threads {
241 fn from(n: usize) -> Self {
242 Self::resolve(n)
243 }
244}
245
246impl Default for Threads {
247 fn default() -> Self {
248 Self::resolve(if SINGLE_THREADED_TARGET { 1 } else { 8 })
249 }
250}
251
252impl std::str::FromStr for Threads {
253 type Err = <NonZeroUsize as std::str::FromStr>::Err;
254
255 fn from_str(s: &str) -> Result<Self, Self::Err> {
256 s.parse::<usize>().map(Self::resolve)
257 }
258}
259
260impl std::fmt::Display for Threads {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 self.0.fmt(f)
263 }
264}
265
266impl std::fmt::Debug for Threads {
267 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268 self.0.fmt(f)
269 }
270}
271
272impl Threads {
273 pub fn resolve(n: usize) -> Self {
275 Self(
276 NonZeroUsize::new(n)
277 .or_else(|| std::thread::available_parallelism().ok())
278 .unwrap_or(NonZeroUsize::MIN),
279 )
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286 use strum::IntoEnumIterator;
287
288 #[cfg(not(feature = "serde"))]
289 use serde_json as _;
290
291 #[test]
292 fn string_enum() {
293 for value in EvmVersion::iter() {
294 let s = value.to_str();
295 assert_eq!(value.to_string(), s);
296 assert_eq!(value, s.parse().unwrap());
297
298 #[cfg(feature = "serde")]
299 {
300 let json_s = format!("\"{value}\"");
301 assert_eq!(serde_json::to_string(&value).unwrap(), json_s);
302 assert_eq!(serde_json::from_str::<EvmVersion>(&json_s).unwrap(), value);
303 }
304 }
305 }
306}