1use std::{ffi::OsString, path::PathBuf, rc::Rc, sync::Arc};
2
3use clap::{Parser, Subcommand};
4use log::Log;
5use midenc_compile as compile;
6#[cfg(feature = "debug")]
7use midenc_debug as debugger;
8use midenc_session::{
9 diagnostics::{Emitter, Report},
10 InputFile,
11};
12
13use crate::ClapDiagnostic;
14
15#[derive(Debug, Parser)]
17#[command(name = "midenc")]
18#[command(author, version, about = "A compiler for Miden Assembly", long_about = None)]
19pub struct Midenc {
20 #[command(subcommand)]
21 command: Commands,
22}
23
24#[derive(Debug, Subcommand)]
25enum Commands {
26 Compile {
27 #[arg(required(true), value_name = "FILE")]
31 input: InputFile,
32 #[command(flatten)]
33 options: compile::Compiler,
34 },
35 #[cfg(feature = "debug")]
37 Run {
38 #[arg(required(true), value_name = "FILE")]
44 input: InputFile,
45 #[arg(long, value_name = "FILE")]
51 inputs: Option<debugger::DebuggerConfig>,
52 #[arg(long, short = 'n', default_value_t = 16)]
54 num_outputs: usize,
55 #[arg(last(true), value_name = "ARGV")]
65 args: Vec<debugger::Felt>,
66 #[command(flatten)]
67 options: debugger::Debugger,
68 },
69 #[cfg(feature = "debug")]
73 Debug {
74 #[arg(required(true), value_name = "FILE")]
80 input: InputFile,
81 #[arg(long, value_name = "FILE")]
87 inputs: Option<debugger::DebuggerConfig>,
88 #[arg(last(true), value_name = "ARGV")]
98 args: Vec<debugger::Felt>,
99 #[command(flatten)]
100 options: debugger::Debugger,
101 },
102}
103
104impl Midenc {
105 pub fn run<P, A>(
106 cwd: P,
107 args: A,
108 logger: Box<dyn Log>,
109 filter: log::LevelFilter,
110 ) -> Result<(), Report>
111 where
112 P: Into<PathBuf>,
113 A: IntoIterator<Item = OsString>,
114 {
115 Self::run_with_emitter(cwd, args, None, logger, filter)
116 }
117
118 pub fn run_with_emitter<P, A>(
119 cwd: P,
120 args: A,
121 emitter: Option<Arc<dyn Emitter>>,
122 logger: Box<dyn Log>,
123 filter: log::LevelFilter,
124 ) -> Result<(), Report>
125 where
126 P: Into<PathBuf>,
127 A: IntoIterator<Item = OsString>,
128 {
129 let command = <Self as clap::CommandFactory>::command();
130 let command = command.mut_subcommand("compile", midenc_session::flags::register_flags);
131
132 let mut matches = command.try_get_matches_from(args).map_err(ClapDiagnostic::from)?;
133 let compile_matches = matches.subcommand_matches("compile").cloned().unwrap_or_default();
134 let cli = <Self as clap::FromArgMatches>::from_arg_matches_mut(&mut matches)
135 .map_err(format_error::<Self>)
136 .map_err(ClapDiagnostic::from)?;
137
138 cli.invoke(cwd.into(), emitter, logger, filter, compile_matches)
139 }
140
141 fn invoke(
142 self,
143 cwd: PathBuf,
144 emitter: Option<Arc<dyn Emitter>>,
145 logger: Box<dyn Log>,
146 filter: log::LevelFilter,
147 matches: clap::ArgMatches,
148 ) -> Result<(), Report> {
149 match self.command {
150 Commands::Compile { input, mut options } => {
151 log::set_boxed_logger(logger)
152 .unwrap_or_else(|err| panic!("failed to install logger: {err}"));
153 log::set_max_level(filter);
154 if options.working_dir.is_none() {
155 options.working_dir = Some(cwd);
156 }
157 let session =
158 options.into_session(vec![input], emitter).with_extra_flags(matches.into());
159 compile::compile(Rc::new(session))
160 }
161 #[cfg(feature = "debug")]
162 Commands::Run {
163 input,
164 inputs,
165 args,
166 num_outputs,
167 mut options,
168 } => {
169 log::set_boxed_logger(logger)
170 .unwrap_or_else(|err| panic!("failed to install logger: {err}"));
171 log::set_max_level(filter);
172 if options.working_dir.is_none() {
173 options.working_dir = Some(cwd);
174 }
175 let session = options.into_session(vec![input], emitter);
176 let args = args.into_iter().map(|felt| felt.0).collect();
177 debugger::run_noninteractively(inputs, args, num_outputs, Rc::new(session))
178 }
179 #[cfg(feature = "debug")]
180 Commands::Debug {
181 input,
182 inputs,
183 args,
184 mut options,
185 } => {
186 if options.working_dir.is_none() {
187 options.working_dir = Some(cwd);
188 }
189 let session = options.into_session(vec![input], emitter);
190 let args = args.into_iter().map(|felt| felt.0).collect();
191 debugger::run(inputs, args, Rc::new(session), logger)
192 }
193 }
194 }
195}
196
197fn format_error<I: clap::CommandFactory>(err: clap::Error) -> clap::Error {
198 let mut cmd = I::command();
199 err.format(&mut cmd)
200}