use std::path::{Path, PathBuf};
use std::process::Command;
use crate::error::{Error, Result};
pub fn find_lldb_dap(explicit_path: Option<&Path>) -> Result<PathBuf> {
if let Some(path) = explicit_path {
if path.exists() && is_executable(path) {
return Ok(path.to_path_buf());
}
return Err(Error::LldbNotFound(format!(
"lldb-dap not found at {:?}",
path
)));
}
if let Ok(output) = Command::new("which").arg("lldb-dap").output() {
if output.status.success() {
let path_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
let path = PathBuf::from(&path_str);
if path.exists() && is_executable(&path) {
return Ok(path);
}
}
}
#[cfg(target_os = "macos")]
{
let homebrew_path = PathBuf::from("/opt/homebrew/opt/llvm/bin/lldb-dap");
if homebrew_path.exists() && is_executable(&homebrew_path) {
return Ok(homebrew_path);
}
let homebrew_intel = PathBuf::from("/usr/local/opt/llvm/bin/lldb-dap");
if homebrew_intel.exists() && is_executable(&homebrew_intel) {
return Ok(homebrew_intel);
}
if let Ok(output) = Command::new("xcrun").args(["-f", "lldb-dap"]).output() {
if output.status.success() {
let path_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
let path = PathBuf::from(&path_str);
if path.exists() && is_executable(&path) {
return Ok(path);
}
}
}
}
#[cfg(target_os = "linux")]
{
let linux_paths = [
"/usr/bin/lldb-dap",
"/usr/local/bin/lldb-dap",
"/usr/lib/llvm-18/bin/lldb-dap",
"/usr/lib/llvm-17/bin/lldb-dap",
"/usr/lib/llvm-16/bin/lldb-dap",
"/usr/lib/llvm-15/bin/lldb-dap",
];
for path_str in linux_paths {
let path = PathBuf::from(path_str);
if path.exists() && is_executable(&path) {
return Ok(path);
}
}
}
Err(Error::LldbNotFound(
"lldb-dap not found in PATH or common locations. \
Install LLVM or set DETRIX_LLDB_DAP_PATH."
.to_string(),
))
}
#[cfg(unix)]
fn is_executable(path: &Path) -> bool {
use std::os::unix::fs::PermissionsExt;
path.metadata()
.map(|m| m.permissions().mode() & 0o111 != 0)
.unwrap_or(false)
}
#[cfg(not(unix))]
fn is_executable(path: &Path) -> bool {
path.exists()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_find_lldb_dap_explicit_nonexistent() {
let result = find_lldb_dap(Some(Path::new("/nonexistent/lldb-dap")));
assert!(result.is_err());
if let Err(Error::LldbNotFound(msg)) = result {
assert!(msg.contains("not found"));
}
}
#[test]
fn test_find_lldb_dap_discovery() {
let _ = find_lldb_dap(None);
}
}