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