feat: create an USBTransport from a rusb::Device (#66)
This allows to be more flexible on USB device we want to connect to, without making public API to complex
This commit is contained in:
10
.github/workflows/rust-quality.yml
vendored
10
.github/workflows/rust-quality.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup component add clippy
|
||||
- name: Run clippy
|
||||
run : cargo clippy --all-features
|
||||
run: cargo clippy --all-features
|
||||
|
||||
fmt:
|
||||
name: "fmt"
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run formatter
|
||||
run : cargo fmt --all --check
|
||||
run: cargo fmt --all --check
|
||||
|
||||
doc:
|
||||
name: "doc"
|
||||
@@ -29,7 +29,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run doc
|
||||
run : cargo doc --all-features --keep-going
|
||||
run: cargo doc --all-features --no-deps
|
||||
env:
|
||||
RUSTDOCFLAGS: "-D warnings"
|
||||
|
||||
tests:
|
||||
name: "tests"
|
||||
@@ -37,4 +39,4 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
run: cargo test --verbose
|
||||
|
||||
@@ -4,18 +4,18 @@ use std::path::Path;
|
||||
use crate::models::AdbStatResponse;
|
||||
use crate::{RebootType, Result};
|
||||
|
||||
/// Trait representing all features available on devices.
|
||||
/// Trait representing all features available on both [`crate::ADBServerDevice`] and [`crate::ADBUSBDevice`]
|
||||
pub trait ADBDeviceExt {
|
||||
/// Run 'command' in a shell on the device, and write its output and error streams into `output`.
|
||||
/// Runs command in a shell on the device, and write its output and error streams into output.
|
||||
fn shell_command<S: ToString, W: Write>(
|
||||
&mut self,
|
||||
command: impl IntoIterator<Item = S>,
|
||||
output: W,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Start an interactive shell session on the device.
|
||||
/// Input data is read from `reader` and write to `writer`.
|
||||
/// `W` has a 'static bound as it is internally used in a thread.
|
||||
/// Starts an interactive shell session on the device.
|
||||
/// Input data is read from reader and write to writer.
|
||||
/// W has a 'static bound as it is internally used in a thread.
|
||||
fn shell<R: Read, W: Write + Send + 'static>(&mut self, reader: R, writer: W) -> Result<()>;
|
||||
|
||||
/// Display the stat information for a remote file
|
||||
|
||||
@@ -15,13 +15,6 @@ use crate::ADBMessageTransport;
|
||||
use crate::ADBTransport;
|
||||
use crate::{Result, RustADBError, USBTransport};
|
||||
|
||||
/// Represent a device reached and available over USB.
|
||||
#[derive(Debug)]
|
||||
pub struct ADBUSBDevice {
|
||||
private_key: ADBRsaKey,
|
||||
inner: ADBMessageDevice<USBTransport>,
|
||||
}
|
||||
|
||||
pub fn read_adb_private_key<P: AsRef<Path>>(private_key_path: P) -> Result<Option<ADBRsaKey>> {
|
||||
Ok(read_to_string(private_key_path.as_ref()).map(|pk| {
|
||||
match ADBRsaKey::new_from_pkcs8(&pk) {
|
||||
@@ -101,6 +94,13 @@ pub fn get_default_adb_key_path() -> Result<PathBuf> {
|
||||
.ok_or(RustADBError::NoHomeDirectory)
|
||||
}
|
||||
|
||||
/// Represent a device reached and available over USB.
|
||||
#[derive(Debug)]
|
||||
pub struct ADBUSBDevice {
|
||||
private_key: ADBRsaKey,
|
||||
inner: ADBMessageDevice<USBTransport>,
|
||||
}
|
||||
|
||||
impl ADBUSBDevice {
|
||||
/// Instantiate a new [`ADBUSBDevice`]
|
||||
pub fn new(vendor_id: u16, product_id: u16) -> Result<Self> {
|
||||
@@ -112,6 +112,26 @@ impl ADBUSBDevice {
|
||||
vendor_id: u16,
|
||||
product_id: u16,
|
||||
private_key_path: PathBuf,
|
||||
) -> Result<Self> {
|
||||
Self::new_from_transport_inner(USBTransport::new(vendor_id, product_id)?, private_key_path)
|
||||
}
|
||||
|
||||
/// Instantiate a new [`ADBUSBDevice`] from a [`USBTransport`] and an optional private key path.
|
||||
pub fn new_from_transport(
|
||||
transport: USBTransport,
|
||||
private_key_path: Option<PathBuf>,
|
||||
) -> Result<Self> {
|
||||
let private_key_path = match private_key_path {
|
||||
Some(private_key_path) => private_key_path,
|
||||
None => get_default_adb_key_path()?,
|
||||
};
|
||||
|
||||
Self::new_from_transport_inner(transport, private_key_path)
|
||||
}
|
||||
|
||||
fn new_from_transport_inner(
|
||||
transport: USBTransport,
|
||||
private_key_path: PathBuf,
|
||||
) -> Result<Self> {
|
||||
let private_key = match read_adb_private_key(private_key_path)? {
|
||||
Some(pk) => pk,
|
||||
@@ -120,7 +140,7 @@ impl ADBUSBDevice {
|
||||
|
||||
let mut s = Self {
|
||||
private_key,
|
||||
inner: ADBMessageDevice::new(USBTransport::new(vendor_id, product_id)),
|
||||
inner: ADBMessageDevice::new(transport),
|
||||
};
|
||||
|
||||
s.connect()?;
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
};
|
||||
|
||||
impl ADBServerDevice {
|
||||
/// Lists files in `path` on the device.
|
||||
/// Lists files in path on the device.
|
||||
pub fn list<A: AsRef<str>>(&mut self, path: A) -> Result<()> {
|
||||
let serial = self.identifier.clone();
|
||||
self.connect()?
|
||||
|
||||
@@ -69,7 +69,7 @@ impl<R: Read> Read for ADBRecvCommandReader<R> {
|
||||
}
|
||||
|
||||
impl ADBServerDevice {
|
||||
/// Receives `path` to `stream` from the device.
|
||||
/// Receives path to stream from the device.
|
||||
pub fn pull<A: AsRef<str>>(&mut self, path: A, stream: &mut dyn Write) -> Result<()> {
|
||||
let serial = self.identifier.clone();
|
||||
self.connect()?
|
||||
|
||||
@@ -42,7 +42,7 @@ impl<W: Write> Write for ADBSendCommandWriter<W> {
|
||||
}
|
||||
|
||||
impl ADBServerDevice {
|
||||
/// Send `stream` to `path` on the device.
|
||||
/// Send stream to path on the device.
|
||||
pub fn push<R: Read, A: AsRef<str>>(&mut self, stream: R, path: A) -> Result<()> {
|
||||
log::info!("Sending data to {}", path.as_ref());
|
||||
let serial = self.identifier.clone();
|
||||
|
||||
@@ -41,7 +41,7 @@ impl ADBServerDevice {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stat file given as `path` on the device.
|
||||
/// Stat file given as path on the device.
|
||||
pub fn stat<A: AsRef<str>>(&mut self, path: A) -> Result<AdbStatResponse> {
|
||||
let serial = self.identifier.clone();
|
||||
self.connect()?
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use rusb::{
|
||||
constants::LIBUSB_CLASS_VENDOR_SPEC, DeviceHandle, Direction, GlobalContext, TransferType,
|
||||
constants::LIBUSB_CLASS_VENDOR_SPEC, Device, DeviceHandle, Direction, GlobalContext,
|
||||
TransferType,
|
||||
};
|
||||
|
||||
use super::{ADBMessageTransport, ADBTransport};
|
||||
@@ -19,18 +20,35 @@ struct Endpoint {
|
||||
/// Transport running on USB
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct USBTransport {
|
||||
vendor_id: u16,
|
||||
product_id: u16,
|
||||
device: Device<GlobalContext>,
|
||||
handle: Option<Arc<DeviceHandle<GlobalContext>>>,
|
||||
}
|
||||
|
||||
impl USBTransport {
|
||||
/// Instantiate a new [USBTransport]
|
||||
pub fn new(vendor_id: u16, product_id: u16) -> Self {
|
||||
/// Instantiate a new [`USBTransport`].
|
||||
/// Only the first device with given vendor_id and product_id is returned.
|
||||
pub fn new(vendor_id: u16, product_id: u16) -> Result<Self> {
|
||||
for device in rusb::devices()?.iter() {
|
||||
if let Ok(descriptor) = device.device_descriptor() {
|
||||
if descriptor.vendor_id() == vendor_id && descriptor.product_id() == product_id {
|
||||
return Ok(Self::new_from_device(device));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(RustADBError::DeviceNotFound(format!(
|
||||
"cannot find USB device with vendor_id={} and product_id={}",
|
||||
vendor_id, product_id
|
||||
)))
|
||||
}
|
||||
|
||||
/// Instantiate a new [`USBTransport`] from a [`rusb::Device`].
|
||||
///
|
||||
/// Devices can be enumerated using [`rusb::devices()`] and then filtered out to get desired device.
|
||||
pub fn new_from_device(rusb_device: rusb::Device<GlobalContext>) -> Self {
|
||||
Self {
|
||||
device: rusb_device,
|
||||
handle: None,
|
||||
vendor_id,
|
||||
product_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,22 +130,8 @@ impl USBTransport {
|
||||
|
||||
impl ADBTransport for USBTransport {
|
||||
fn connect(&mut self) -> crate::Result<()> {
|
||||
for d in rusb::devices()?.iter() {
|
||||
if let Ok(descriptor) = d.device_descriptor() {
|
||||
if descriptor.vendor_id() == self.vendor_id
|
||||
&& descriptor.product_id() == self.product_id
|
||||
{
|
||||
self.handle = Some(Arc::new(d.open()?));
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(RustADBError::DeviceNotFound(format!(
|
||||
"Cannot find device with vendor id {} and product id {}",
|
||||
self.vendor_id, self.product_id
|
||||
)))
|
||||
self.handle = Some(Arc::new(self.device.open()?));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disconnect(&mut self) -> crate::Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user