disassemble/
disassemble.rs1use std::{
2 env,
3 fs::File,
4 path::{Path, PathBuf},
5 process::ExitCode,
6};
7
8use ferro_babe::{Diagnostic, Disassembler, FerroFormatter, Formatter};
9
10fn main() -> ExitCode {
11 run().unwrap_or_else(|message| {
12 eprintln!("ferro-disassemble: {message}");
13 ExitCode::FAILURE
14 })
15}
16
17fn run() -> Result<ExitCode, String> {
18 let path = class_path()?;
19 let file =
20 File::open(&path).map_err(|error| format!("could not open {}: {error}", path.display()))?;
21 let disassembly = Disassembler::default()
22 .parse_reader(file)
23 .map_err(|error| format!("could not parse {}: {error}", path.display()))?;
24
25 let version = disassembly.version();
26 let Some(class) = disassembly.class() else {
27 println!(
28 "partial class file v{}.{}",
29 version.major(),
30 version.minor()
31 );
32 for diagnostic in disassembly.diagnostics() {
33 print_diagnostic(diagnostic);
34 }
35 return Ok(ExitCode::from(2));
36 };
37
38 let output = FerroFormatter
39 .format(class)
40 .map_err(|error| format!("could not format {}: {error}", path.display()))?;
41 print!("{output}");
42
43 Ok(ExitCode::SUCCESS)
44}
45
46fn class_path() -> Result<PathBuf, String> {
47 let mut arguments = env::args_os();
48 let executable = arguments.next().unwrap_or_default();
49 let Some(path) = arguments.next() else {
50 return Err(usage(&executable));
51 };
52
53 if arguments.next().is_some() {
54 return Err(usage(&executable));
55 }
56
57 Ok(PathBuf::from(path))
58}
59
60fn usage(executable: &std::ffi::OsStr) -> String {
61 format!("usage: {} <class-file>", Path::new(executable).display())
62}
63
64fn print_diagnostic(diagnostic: &Diagnostic) {
65 let offset = diagnostic
66 .offset()
67 .map(|offset| format!(" at code offset 0x{offset:04x}"))
68 .unwrap_or_default();
69
70 eprintln!(
71 "diagnostic: {:?} in {:?}{offset}: {}",
72 diagnostic.severity(),
73 diagnostic.stage(),
74 diagnostic.message()
75 );
76}