pub mod delve;
pub mod dotnettrace;
pub mod callgrind;
pub mod ghci;
pub mod ghcprof;
pub mod jdb;
pub mod jitdasm;
pub mod lldb;
pub mod netcoredbg;
pub mod massif;
pub mod memcheck;
pub mod node_inspect;
pub mod nodeprof;
pub mod ocamldebug;
pub mod perf;
pub mod pdb;
pub mod phpdbg;
pub mod pprof;
pub mod pstats;
pub mod rdbg;
pub mod stackprof;
pub mod xdebug;
use std::collections::HashMap;
pub use dbg_cli::deps::{Dependency, DependencyCheck, DepStatus};
pub struct CleanResult {
pub output: String,
pub events: Vec<String>,
}
pub struct SpawnConfig {
pub bin: String,
pub args: Vec<String>,
pub env: Vec<(String, String)>,
pub init_commands: Vec<String>,
}
pub trait Backend: Send + Sync {
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
fn types(&self) -> &'static [&'static str];
fn spawn_config(&self, target: &str, args: &[String]) -> anyhow::Result<SpawnConfig>;
fn prompt_pattern(&self) -> &str;
fn dependencies(&self) -> Vec<Dependency>;
fn format_breakpoint(&self, _spec: &str) -> String {
String::new()
}
fn run_command(&self) -> &'static str;
fn quit_command(&self) -> &'static str {
"quit"
}
fn help_command(&self) -> &'static str {
"help"
}
fn adapters(&self) -> Vec<(&'static str, &'static str)>;
fn parse_help(&self, raw: &str) -> String;
fn profile_output(&self) -> Option<String> {
None
}
fn clean(&self, cmd: &str, output: &str) -> CleanResult {
let _ = cmd;
CleanResult {
output: output.to_string(),
events: vec![],
}
}
}
pub fn self_exe() -> String {
std::env::current_exe()
.unwrap_or_else(|_| "dbg".into())
.display()
.to_string()
}
pub fn shell_escape(s: &str) -> String {
if s.is_empty() {
return "''".to_string();
}
if s.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'.' || b == b'/' || b == b'-' || b == b'_' || b == b'=' || b == b':') {
return s.to_string();
}
format!("'{}'", s.replace('\'', "'\\''"))
}
pub struct Registry {
backends: Vec<Box<dyn Backend>>,
type_map: HashMap<String, usize>,
}
impl Registry {
pub fn new() -> Self {
Self {
backends: Vec::new(),
type_map: HashMap::new(),
}
}
pub fn register(&mut self, backend: Box<dyn Backend>) {
let idx = self.backends.len();
self.type_map.insert(backend.name().to_string(), idx);
for t in backend.types() {
self.type_map.insert(t.to_string(), idx);
}
self.backends.push(backend);
}
pub fn get(&self, type_name: &str) -> Option<&dyn Backend> {
self.type_map
.get(type_name)
.map(|&idx| self.backends[idx].as_ref())
}
pub fn available_types(&self) -> Vec<&str> {
let mut types: Vec<&str> = self.type_map.keys().map(|s| s.as_str()).collect();
types.sort();
types
}
pub fn all_backends(&self) -> &[Box<dyn Backend>] {
&self.backends
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shell_escape_simple_passthrough() {
assert_eq!(shell_escape("./myapp"), "./myapp");
assert_eq!(shell_escape("target/debug/foo"), "target/debug/foo");
assert_eq!(shell_escape("a-b_c.d"), "a-b_c.d");
}
#[test]
fn shell_escape_empty() {
assert_eq!(shell_escape(""), "''");
}
#[test]
fn shell_escape_spaces() {
assert_eq!(shell_escape("my app"), "'my app'");
assert_eq!(shell_escape("/path/to/my app"), "'/path/to/my app'");
}
#[test]
fn shell_escape_single_quotes() {
assert_eq!(shell_escape("it's"), "'it'\\''s'");
}
#[test]
fn shell_escape_special_chars() {
assert_eq!(shell_escape("$(rm -rf /)"), "'$(rm -rf /)'");
assert_eq!(shell_escape("foo;bar"), "'foo;bar'");
assert_eq!(shell_escape("a&b"), "'a&b'");
assert_eq!(shell_escape("a|b"), "'a|b'");
}
#[test]
fn shell_escape_backticks() {
assert_eq!(shell_escape("`whoami`"), "'`whoami`'");
}
}