1use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
4use std::{collections::BTreeMap, fmt, str::FromStr};
5
6pub type FileOutputSelection = BTreeMap<String, Vec<String>>;
8
9#[derive(Debug, Clone, Eq, PartialEq, Default, Deserialize)]
72#[serde(transparent)]
73pub struct OutputSelection(pub BTreeMap<String, FileOutputSelection>);
74
75impl OutputSelection {
76 pub fn complete_output_selection() -> Self {
80 BTreeMap::from([(
81 "*".to_string(),
82 BTreeMap::from([
83 ("*".to_string(), vec!["*".to_string()]),
84 ("".to_string(), vec!["*".to_string()]),
85 ]),
86 )])
87 .into()
88 }
89
90 pub fn default_output_selection() -> Self {
97 BTreeMap::from([("*".to_string(), Self::default_file_output_selection())]).into()
98 }
99
100 pub fn default_file_output_selection() -> FileOutputSelection {
107 BTreeMap::from([(
108 "*".to_string(),
109 vec![
110 "abi".to_string(),
111 "evm.bytecode".to_string(),
112 "evm.deployedBytecode".to_string(),
113 "evm.methodIdentifiers".to_string(),
114 ],
115 )])
116 }
117
118 pub fn empty_file_output_select() -> FileOutputSelection {
120 Default::default()
121 }
122}
123
124impl Serialize for OutputSelection {
128 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
129 where
130 S: Serializer,
131 {
132 struct EmptyFileOutput;
133
134 impl Serialize for EmptyFileOutput {
135 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
136 where
137 S: Serializer,
138 {
139 let mut map = serializer.serialize_map(Some(1))?;
140 map.serialize_entry("*", &[] as &[String])?;
141 map.end()
142 }
143 }
144
145 let mut map = serializer.serialize_map(Some(self.0.len()))?;
146 for (file, selection) in self.0.iter() {
147 if selection.is_empty() {
148 map.serialize_entry(file, &EmptyFileOutput {})?;
149 } else {
150 map.serialize_entry(file, selection)?;
151 }
152 }
153 map.end()
154 }
155}
156
157impl AsRef<BTreeMap<String, FileOutputSelection>> for OutputSelection {
158 fn as_ref(&self) -> &BTreeMap<String, FileOutputSelection> {
159 &self.0
160 }
161}
162
163impl AsMut<BTreeMap<String, FileOutputSelection>> for OutputSelection {
164 fn as_mut(&mut self) -> &mut BTreeMap<String, FileOutputSelection> {
165 &mut self.0
166 }
167}
168
169impl From<BTreeMap<String, FileOutputSelection>> for OutputSelection {
170 fn from(s: BTreeMap<String, FileOutputSelection>) -> Self {
171 OutputSelection(s)
172 }
173}
174
175#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
177pub enum ContractOutputSelection {
178 Abi,
179 DevDoc,
180 UserDoc,
181 Metadata,
182 Ir,
183 IrOptimized,
184 StorageLayout,
185 Evm(EvmOutputSelection),
186 Ewasm(EwasmOutputSelection),
187}
188
189impl ContractOutputSelection {
190 pub fn basic() -> Vec<ContractOutputSelection> {
197 vec![
198 ContractOutputSelection::Abi,
199 BytecodeOutputSelection::All.into(),
200 DeployedBytecodeOutputSelection::All.into(),
201 EvmOutputSelection::MethodIdentifiers.into(),
202 ]
203 }
204}
205
206impl Serialize for ContractOutputSelection {
207 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208 where
209 S: Serializer,
210 {
211 serializer.collect_str(self)
212 }
213}
214
215impl<'de> Deserialize<'de> for ContractOutputSelection {
216 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
217 where
218 D: Deserializer<'de>,
219 {
220 String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
221 }
222}
223
224impl fmt::Display for ContractOutputSelection {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 match self {
227 ContractOutputSelection::Abi => f.write_str("abi"),
228 ContractOutputSelection::DevDoc => f.write_str("devdoc"),
229 ContractOutputSelection::UserDoc => f.write_str("userdoc"),
230 ContractOutputSelection::Metadata => f.write_str("metadata"),
231 ContractOutputSelection::Ir => f.write_str("ir"),
232 ContractOutputSelection::IrOptimized => f.write_str("irOptimized"),
233 ContractOutputSelection::StorageLayout => f.write_str("storageLayout"),
234 ContractOutputSelection::Evm(e) => e.fmt(f),
235 ContractOutputSelection::Ewasm(e) => e.fmt(f),
236 }
237 }
238}
239
240impl FromStr for ContractOutputSelection {
241 type Err = String;
242
243 fn from_str(s: &str) -> Result<Self, Self::Err> {
244 match s {
245 "abi" => Ok(ContractOutputSelection::Abi),
246 "devdoc" => Ok(ContractOutputSelection::DevDoc),
247 "userdoc" => Ok(ContractOutputSelection::UserDoc),
248 "metadata" => Ok(ContractOutputSelection::Metadata),
249 "ir" => Ok(ContractOutputSelection::Ir),
250 "ir-optimized" | "irOptimized" | "iroptimized" => {
251 Ok(ContractOutputSelection::IrOptimized)
252 }
253 "storage-layout" | "storagelayout" | "storageLayout" => {
254 Ok(ContractOutputSelection::StorageLayout)
255 }
256 s => EvmOutputSelection::from_str(s)
257 .map(ContractOutputSelection::Evm)
258 .or_else(|_| EwasmOutputSelection::from_str(s).map(ContractOutputSelection::Ewasm))
259 .map_err(|_| format!("Invalid contract output selection: {s}")),
260 }
261 }
262}
263
264impl<T: Into<EvmOutputSelection>> From<T> for ContractOutputSelection {
265 fn from(evm: T) -> Self {
266 ContractOutputSelection::Evm(evm.into())
267 }
268}
269
270impl From<EwasmOutputSelection> for ContractOutputSelection {
271 fn from(ewasm: EwasmOutputSelection) -> Self {
272 ContractOutputSelection::Ewasm(ewasm)
273 }
274}
275
276#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
278pub enum EvmOutputSelection {
279 All,
280 Assembly,
281 LegacyAssembly,
282 MethodIdentifiers,
283 GasEstimates,
284 ByteCode(BytecodeOutputSelection),
285 DeployedByteCode(DeployedBytecodeOutputSelection),
286}
287
288impl From<BytecodeOutputSelection> for EvmOutputSelection {
289 fn from(b: BytecodeOutputSelection) -> Self {
290 EvmOutputSelection::ByteCode(b)
291 }
292}
293
294impl From<DeployedBytecodeOutputSelection> for EvmOutputSelection {
295 fn from(b: DeployedBytecodeOutputSelection) -> Self {
296 EvmOutputSelection::DeployedByteCode(b)
297 }
298}
299
300impl Serialize for EvmOutputSelection {
301 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
302 where
303 S: Serializer,
304 {
305 serializer.collect_str(self)
306 }
307}
308
309impl<'de> Deserialize<'de> for EvmOutputSelection {
310 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
311 where
312 D: Deserializer<'de>,
313 {
314 String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
315 }
316}
317
318impl fmt::Display for EvmOutputSelection {
319 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320 match self {
321 EvmOutputSelection::All => f.write_str("evm"),
322 EvmOutputSelection::Assembly => f.write_str("evm.assembly"),
323 EvmOutputSelection::LegacyAssembly => f.write_str("evm.legacyAssembly"),
324 EvmOutputSelection::MethodIdentifiers => f.write_str("evm.methodIdentifiers"),
325 EvmOutputSelection::GasEstimates => f.write_str("evm.gasEstimates"),
326 EvmOutputSelection::ByteCode(b) => b.fmt(f),
327 EvmOutputSelection::DeployedByteCode(b) => b.fmt(f),
328 }
329 }
330}
331
332impl FromStr for EvmOutputSelection {
333 type Err = String;
334
335 fn from_str(s: &str) -> Result<Self, Self::Err> {
336 match s {
337 "evm" => Ok(EvmOutputSelection::All),
338 "asm" | "evm.assembly" => Ok(EvmOutputSelection::Assembly),
339 "evm.legacyAssembly" => Ok(EvmOutputSelection::LegacyAssembly),
340 "methodidentifiers" | "evm.methodIdentifiers" | "evm.methodidentifiers" => {
341 Ok(EvmOutputSelection::MethodIdentifiers)
342 }
343 "gas" | "evm.gasEstimates" | "evm.gasestimates" => Ok(EvmOutputSelection::GasEstimates),
344 s => BytecodeOutputSelection::from_str(s)
345 .map(EvmOutputSelection::ByteCode)
346 .or_else(|_| {
347 DeployedBytecodeOutputSelection::from_str(s)
348 .map(EvmOutputSelection::DeployedByteCode)
349 })
350 .map_err(|_| format!("Invalid evm selection: {s}")),
351 }
352 }
353}
354
355#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
357pub enum BytecodeOutputSelection {
358 All,
359 FunctionDebugData,
360 Object,
361 Opcodes,
362 SourceMap,
363 LinkReferences,
364 GeneratedSources,
365}
366
367impl Serialize for BytecodeOutputSelection {
368 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
369 where
370 S: Serializer,
371 {
372 serializer.collect_str(self)
373 }
374}
375
376impl<'de> Deserialize<'de> for BytecodeOutputSelection {
377 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
378 where
379 D: Deserializer<'de>,
380 {
381 String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
382 }
383}
384
385impl fmt::Display for BytecodeOutputSelection {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 match self {
388 BytecodeOutputSelection::All => f.write_str("evm.bytecode"),
389 BytecodeOutputSelection::FunctionDebugData => {
390 f.write_str("evm.bytecode.functionDebugData")
391 }
392 BytecodeOutputSelection::Object => f.write_str("evm.bytecode.object"),
393 BytecodeOutputSelection::Opcodes => f.write_str("evm.bytecode.opcodes"),
394 BytecodeOutputSelection::SourceMap => f.write_str("evm.bytecode.sourceMap"),
395 BytecodeOutputSelection::LinkReferences => f.write_str("evm.bytecode.linkReferences"),
396 BytecodeOutputSelection::GeneratedSources => {
397 f.write_str("evm.bytecode.generatedSources")
398 }
399 }
400 }
401}
402
403impl FromStr for BytecodeOutputSelection {
404 type Err = String;
405
406 fn from_str(s: &str) -> Result<Self, Self::Err> {
407 match s {
408 "evm.bytecode" => Ok(BytecodeOutputSelection::All),
409 "evm.bytecode.functionDebugData" => Ok(BytecodeOutputSelection::FunctionDebugData),
410 "code" | "bin" | "evm.bytecode.object" => Ok(BytecodeOutputSelection::Object),
411 "evm.bytecode.opcodes" => Ok(BytecodeOutputSelection::Opcodes),
412 "evm.bytecode.sourceMap" => Ok(BytecodeOutputSelection::SourceMap),
413 "evm.bytecode.linkReferences" => Ok(BytecodeOutputSelection::LinkReferences),
414 "evm.bytecode.generatedSources" => Ok(BytecodeOutputSelection::GeneratedSources),
415 s => Err(format!("Invalid bytecode selection: {s}")),
416 }
417 }
418}
419
420#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
422pub enum DeployedBytecodeOutputSelection {
423 All,
424 FunctionDebugData,
425 Object,
426 Opcodes,
427 SourceMap,
428 LinkReferences,
429 GeneratedSources,
430 ImmutableReferences,
431}
432
433impl Serialize for DeployedBytecodeOutputSelection {
434 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
435 where
436 S: Serializer,
437 {
438 serializer.collect_str(self)
439 }
440}
441
442impl<'de> Deserialize<'de> for DeployedBytecodeOutputSelection {
443 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
444 where
445 D: Deserializer<'de>,
446 {
447 String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
448 }
449}
450
451impl fmt::Display for DeployedBytecodeOutputSelection {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 match self {
454 DeployedBytecodeOutputSelection::All => f.write_str("evm.deployedBytecode"),
455 DeployedBytecodeOutputSelection::FunctionDebugData => {
456 f.write_str("evm.deployedBytecode.functionDebugData")
457 }
458 DeployedBytecodeOutputSelection::Object => f.write_str("evm.deployedBytecode.object"),
459 DeployedBytecodeOutputSelection::Opcodes => f.write_str("evm.deployedBytecode.opcodes"),
460 DeployedBytecodeOutputSelection::SourceMap => {
461 f.write_str("evm.deployedBytecode.sourceMap")
462 }
463 DeployedBytecodeOutputSelection::LinkReferences => {
464 f.write_str("evm.deployedBytecode.linkReferences")
465 }
466 DeployedBytecodeOutputSelection::GeneratedSources => {
467 f.write_str("evm.deployedBytecode.generatedSources")
468 }
469 DeployedBytecodeOutputSelection::ImmutableReferences => {
470 f.write_str("evm.deployedBytecode.immutableReferences")
471 }
472 }
473 }
474}
475
476impl FromStr for DeployedBytecodeOutputSelection {
477 type Err = String;
478
479 fn from_str(s: &str) -> Result<Self, Self::Err> {
480 match s {
481 "evm.deployedBytecode" => Ok(DeployedBytecodeOutputSelection::All),
482 "evm.deployedBytecode.functionDebugData" => {
483 Ok(DeployedBytecodeOutputSelection::FunctionDebugData)
484 }
485 "deployed-code" |
486 "deployed-bin" |
487 "runtime-code" |
488 "runtime-bin" |
489 "evm.deployedBytecode.object" => Ok(DeployedBytecodeOutputSelection::Object),
490 "evm.deployedBytecode.opcodes" => Ok(DeployedBytecodeOutputSelection::Opcodes),
491 "evm.deployedBytecode.sourceMap" => Ok(DeployedBytecodeOutputSelection::SourceMap),
492 "evm.deployedBytecode.linkReferences" => {
493 Ok(DeployedBytecodeOutputSelection::LinkReferences)
494 }
495 "evm.deployedBytecode.generatedSources" => {
496 Ok(DeployedBytecodeOutputSelection::GeneratedSources)
497 }
498 "evm.deployedBytecode.immutableReferences" => {
499 Ok(DeployedBytecodeOutputSelection::ImmutableReferences)
500 }
501 s => Err(format!("Invalid deployedBytecode selection: {s}")),
502 }
503 }
504}
505
506#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
508pub enum EwasmOutputSelection {
509 All,
510 Wast,
511 Wasm,
512}
513
514impl Serialize for EwasmOutputSelection {
515 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
516 where
517 S: Serializer,
518 {
519 serializer.collect_str(self)
520 }
521}
522
523impl<'de> Deserialize<'de> for EwasmOutputSelection {
524 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
525 where
526 D: Deserializer<'de>,
527 {
528 String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
529 }
530}
531
532impl fmt::Display for EwasmOutputSelection {
533 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
534 match self {
535 EwasmOutputSelection::All => f.write_str("ewasm"),
536 EwasmOutputSelection::Wast => f.write_str("ewasm.wast"),
537 EwasmOutputSelection::Wasm => f.write_str("ewasm.wasm"),
538 }
539 }
540}
541
542impl FromStr for EwasmOutputSelection {
543 type Err = String;
544
545 fn from_str(s: &str) -> Result<Self, Self::Err> {
546 match s {
547 "ewasm" => Ok(EwasmOutputSelection::All),
548 "ewasm.wast" => Ok(EwasmOutputSelection::Wast),
549 "ewasm.wasm" => Ok(EwasmOutputSelection::Wasm),
550 s => Err(format!("Invalid ewasm selection: {s}")),
551 }
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558
559 #[test]
560 fn outputselection_serde_works() {
561 let mut output = BTreeMap::default();
562 output.insert(
563 "*".to_string(),
564 vec![
565 "abi".to_string(),
566 "evm.bytecode".to_string(),
567 "evm.deployedBytecode".to_string(),
568 "evm.methodIdentifiers".to_string(),
569 ],
570 );
571
572 let json = serde_json::to_string(&output).unwrap();
573 let deserde_selection: BTreeMap<String, Vec<ContractOutputSelection>> =
574 serde_json::from_str(&json).unwrap();
575
576 assert_eq!(json, serde_json::to_string(&deserde_selection).unwrap());
577 }
578
579 #[test]
580 fn empty_outputselection_serde_works() {
581 let mut empty = OutputSelection::default();
582 empty.0.insert("contract.sol".to_string(), OutputSelection::empty_file_output_select());
583 let s = serde_json::to_string(&empty).unwrap();
584 assert_eq!(s, r#"{"contract.sol":{"*":[]}}"#);
585 }
586
587 #[test]
588 fn deployed_bytecode_from_str() {
589 assert_eq!(
590 DeployedBytecodeOutputSelection::from_str("evm.deployedBytecode.immutableReferences")
591 .unwrap(),
592 DeployedBytecodeOutputSelection::ImmutableReferences
593 )
594 }
595}