use std::path::PathBuf;
use adbutils::pidcat::{PidcatOptions, Priority};
use adbutils::{AdbClient, AdbDevice, Result};
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "adbutils", about = "Async adb utilities (Rust port of adbutils)")]
struct Cli {
#[arg(short, long, global = true)]
serial: Option<String>,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Version,
Devices {
#[arg(short = 'l', long)]
extended: bool,
},
Shell {
#[arg(trailing_var_arg = true, required = true)]
args: Vec<String>,
},
Screenshot { output: PathBuf },
Push { local: PathBuf, remote: String },
Pull { remote: String, local: PathBuf },
Forward { local: String, remote: String },
ForwardList,
Install { path_or_url: String },
Screenrecord { output: PathBuf },
Getprop { name: String },
Current,
Packages {
#[arg(short = '3', long)]
third_party: bool,
},
Pidcat {
filters: Vec<String>,
#[arg(short = 'l', long, default_value = "V")]
level: char,
#[arg(short, long)]
clear: bool,
},
}
async fn resolve_device(client: &AdbClient, serial: &Option<String>) -> Result<AdbDevice> {
match serial {
Some(s) => Ok(client.device(s.clone())),
None => client.any_device().await,
}
}
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
let adb = AdbClient::default();
match cli.command {
Command::Version => {
println!("{}", adb.server_version().await?);
}
Command::Devices { extended } => {
for info in adb.list(extended).await? {
if extended {
println!("{}\t{}\t{:?}", info.serial, info.state, info.tags);
} else {
println!("{}\t{}", info.serial, info.state);
}
}
}
Command::Shell { args } => {
let d = resolve_device(&adb, &cli.serial).await?;
print!("{}", d.shell(args).await?);
println!();
}
Command::Screenshot { output } => {
let d = resolve_device(&adb, &cli.serial).await?;
let img = d.screenshot(None, true).await?;
img.save(&output)?;
println!("saved {}", output.display());
}
Command::Push { local, remote } => {
let d = resolve_device(&adb, &cli.serial).await?;
let n = d.sync().push(&local, &remote, 0o644, false).await?;
println!("pushed {n} bytes to {remote}");
}
Command::Pull { remote, local } => {
let d = resolve_device(&adb, &cli.serial).await?;
let n = d.sync().pull(&remote, &local, false).await?;
println!("pulled {n} bytes to {}", local.display());
}
Command::Forward { local, remote } => {
let d = resolve_device(&adb, &cli.serial).await?;
d.forward(&local, &remote, false).await?;
println!("{local} -> {remote}");
}
Command::ForwardList => {
let d = resolve_device(&adb, &cli.serial).await?;
for f in d.forward_list().await? {
println!("{}\t{}\t{}", f.serial, f.local, f.remote);
}
}
Command::Install { path_or_url } => {
let d = resolve_device(&adb, &cli.serial).await?;
d.install(&path_or_url, false, false, &["-r", "-t"]).await?;
println!("installed");
}
Command::Screenrecord { output } => {
let d = resolve_device(&adb, &cli.serial).await?;
let rec = d.start_recording(&output).await?;
println!("recording... press Ctrl-C to stop");
tokio::signal::ctrl_c().await?;
rec.stop().await?;
println!("saved {}", output.display());
}
Command::Getprop { name } => {
let d = resolve_device(&adb, &cli.serial).await?;
println!("{}", d.getprop(&name).await?);
}
Command::Current => {
let d = resolve_device(&adb, &cli.serial).await?;
let info = d.app_current().await?;
println!("{}/{} pid={}", info.package, info.activity, info.pid);
}
Command::Packages { third_party } => {
let d = resolve_device(&adb, &cli.serial).await?;
let filters: &[&str] = if third_party { &["-3"] } else { &[] };
for p in d.list_packages(filters).await? {
println!("{p}");
}
}
Command::Pidcat { filters, level, clear } => {
let d = resolve_device(&adb, &cli.serial).await?;
let min_priority = Priority::from_letter(level.to_ascii_uppercase());
let opts = PidcatOptions { min_priority, tag_filters: filters, clear };
d.pidcat(opts, |line| println!("{line}")).await?;
}
}
Ok(())
}