use std::path::Path;
use std::sync::{Arc, Mutex};
use crate::dap;
use crate::dap::yadap;
use crate::debugger::DebuggerBuilder;
use crate::debugger::process::{Child, Installed};
use crate::oracle::builtin;
use crate::ui::console::TerminalApplication;
use crate::ui::tui::TuiApplication;
use crate::ui::{console, tui};
use anyhow::Context;
use log::{info, warn};
use nix::unistd::Pid;
pub enum Interface<'a> {
TUI {
source: DebugeeSource<'a>,
},
Default {
source: DebugeeSource<'a>,
},
DAP {
tracer: Option<dap::tracer::FileTracer>,
},
}
pub enum DebugeeSource<'a> {
File {
path: &'a str,
args: &'a [String],
cwd: Option<&'a Path>,
},
Process { pid: i32 },
}
impl DebugeeSource<'_> {
pub fn create_child(
self,
stdout_writer: os_pipe::PipeWriter,
stderr_writer: os_pipe::PipeWriter,
) -> anyhow::Result<Child<Installed>> {
match self {
DebugeeSource::File { path, args, cwd } => {
let path = if !Path::new(path).exists() {
which::which(path)?.to_string_lossy().to_string()
} else {
path.to_string()
};
let proc_tpl = Child::new(path, args, cwd, stdout_writer, stderr_writer);
proc_tpl.install().context("Initial process instantiation")
}
DebugeeSource::Process { pid } => {
Child::from_external(Pid::from_raw(pid), stdout_writer, stderr_writer)
.context("Attach external process")
}
}
}
}
#[allow(clippy::large_enum_variant)]
pub enum Application {
TUI(TuiApplication),
Terminal(TerminalApplication),
DAP(Option<dap::tracer::FileTracer>),
}
impl Application {
pub fn run(self) -> anyhow::Result<ControlFlow> {
match self {
Application::TUI(tui_app) => tui_app.run(),
Application::Terminal(term_app) => term_app.run(),
Application::DAP(tracer) => {
let transport = dap::transport::new_stdio_transport(tracer);
let transport = Arc::new(Mutex::new(transport));
let session = yadap::session::DebugSession::new(transport);
session.run(vec![])?;
Ok(ControlFlow::Exit)
}
}
}
}
#[allow(clippy::large_enum_variant)]
pub enum ControlFlow {
Exit,
Switch(Application),
}
pub struct Supervisor;
impl Supervisor {
pub fn run(ui: Interface, oracles: &[String]) -> anyhow::Result<()> {
let (stdout_reader, stdout_writer) = os_pipe::pipe()?;
let (stderr_reader, stderr_writer) = os_pipe::pipe()?;
let process = |src: DebugeeSource| src.create_child(stdout_writer, stderr_writer);
let oracles = oracles
.iter()
.filter_map(|ora_name| {
if let Some(oracle) = builtin::make_builtin(ora_name) {
info!(target: "debugger", "oracle `{ora_name}` discovered");
Some(oracle)
} else {
warn!(target: "debugger", "oracle `{ora_name}` not found");
None
}
})
.collect();
let mut app = match ui {
Interface::TUI { source } => {
let app_builder = tui::AppBuilder::new(stdout_reader.into(), stderr_reader.into());
let app = app_builder
.build(
DebuggerBuilder::new().with_oracles(oracles),
process(source)?,
)
.context("Build debugger")?;
Application::TUI(app)
}
Interface::Default { source } => {
let app_builder =
console::AppBuilder::new(stdout_reader.into(), stderr_reader.into());
let app = app_builder
.build(
DebuggerBuilder::new().with_oracles(oracles),
process(source)?,
)
.context("Build debugger")?;
Application::Terminal(app)
}
Interface::DAP { tracer } => Application::DAP(tracer),
};
loop {
let ctl = app.run()?;
match ctl {
ControlFlow::Exit => {
return Ok(());
}
ControlFlow::Switch(next_app) => {
app = next_app;
}
}
}
}
}