use std::path::{Path, PathBuf};
use crate::{android::toolchain::AndroidSdk, toolchain::Host, utils::CommandError};
#[derive(Debug, thiserror::Error)]
pub enum AdbError {
#[error("Android SDK not found or adb not installed")]
NotFound,
#[error("failed to start the adb server: {0}")]
ServerLauncher(#[from] CommandError),
#[error("`{adb} start-server` failed with status {status}", adb = .adb.display())]
ServerStart {
adb: PathBuf,
status: std::process::ExitStatus,
},
}
#[derive(Debug, Clone)]
pub struct Adb {
path: PathBuf,
}
impl Adb {
pub async fn locate(host: &Host) -> Result<Self, AdbError> {
let path = AndroidSdk::adb_path(host).ok_or(AdbError::NotFound)?;
let status = host.run_detached(&path, ["start-server"]).await?;
if !status.success() {
return Err(AdbError::ServerStart { adb: path, status });
}
Ok(Self { path })
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
}
#[cfg(test)]
mod tests {
use std::ffi::OsString;
use super::{Adb, AdbError};
use crate::toolchain::testing::TestMachine;
#[test]
fn without_platform_tools_there_is_no_adb() {
let machine = TestMachine::new();
let sdk = machine.install_android_sdk();
let host = machine.host([(
OsString::from("ANDROID_SDK_ROOT"),
sdk.as_os_str().to_os_string(),
)]);
let error = smol::block_on(Adb::locate(&host)).expect_err("no adb staged");
assert!(matches!(error, AdbError::NotFound), "{error:?}");
}
#[test]
#[cfg(unix)]
fn locating_adb_starts_its_server_first() {
let machine = TestMachine::new();
let sdk = machine.install_android_sdk();
let staged = machine.install_adb();
let host = machine.host([(
OsString::from("ANDROID_SDK_ROOT"),
sdk.as_os_str().to_os_string(),
)]);
let adb = smol::block_on(Adb::locate(&host)).expect("the fake adb starts its server");
assert_eq!(adb.path(), staged);
}
#[test]
#[cfg(unix)]
fn a_failing_server_launch_is_an_error() {
let machine = TestMachine::new();
let sdk = machine.install_android_sdk();
machine.install_adb();
let host = machine.host([
(
OsString::from("ANDROID_SDK_ROOT"),
sdk.as_os_str().to_os_string(),
),
(
OsString::from("WATERUI_FAKE_ADB_START_SERVER_STATUS"),
OsString::from("3"),
),
]);
let error = smol::block_on(Adb::locate(&host)).expect_err("the launcher exits 3");
match error {
AdbError::ServerStart { status, .. } => assert_eq!(status.code(), Some(3)),
other => panic!("expected ServerStart, got {other:?}"),
}
}
}