use crate::{
Result, RustADBError,
models::{ADBCommand, ADBLocalCommand, AdbRequestStatus, SyncCommand},
server_device::ADBServerDevice,
};
use std::{
convert::TryInto,
io::{self, BufReader, BufWriter, Read, Write},
str::{self, FromStr},
time::SystemTime,
};
struct ADBSendCommandWriter<W: Write> {
inner: W,
}
impl<W: Write> ADBSendCommandWriter<W> {
pub const fn new(inner: W) -> Self {
Self { inner }
}
}
impl<W: Write> Write for ADBSendCommandWriter<W> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let chunk_len = u32::try_from(buf.len()).map_err(io::Error::other)?;
let mut buffer = Vec::with_capacity(8 + buf.len());
buffer.extend_from_slice(b"DATA");
buffer.extend_from_slice(&chunk_len.to_le_bytes());
buffer.extend_from_slice(buf);
self.inner.write_all(&buffer)?;
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
self.inner.flush()
}
}
const BUFFER_SIZE: usize = 65535;
impl ADBServerDevice {
pub fn push<R: Read, A: AsRef<str>>(&mut self, stream: R, path: A) -> Result<()> {
log::info!("Sending data to {}", path.as_ref());
self.set_serial_transport()?;
self.transport
.send_adb_request(&ADBCommand::Local(ADBLocalCommand::Sync))?;
self.transport.send_sync_request(&SyncCommand::Send)?;
self.handle_send_command(stream, path)
}
fn handle_send_command<R: Read, S: AsRef<str>>(&self, input: R, to: S) -> Result<()> {
let to = to.as_ref().to_string() + ",0777";
let mut raw_connection = self.transport.get_raw_connection()?;
let to_as_bytes = to.as_bytes();
let mut buffer = Vec::with_capacity(4 + to_as_bytes.len());
buffer.extend_from_slice(&(u32::try_from(to.len())?).to_le_bytes());
buffer.extend_from_slice(to_as_bytes);
raw_connection.write_all(&buffer)?;
let writer = ADBSendCommandWriter::new(raw_connection);
std::io::copy(
&mut BufReader::with_capacity(BUFFER_SIZE, input),
&mut BufWriter::with_capacity(BUFFER_SIZE, writer),
)?;
let Ok(last_modified) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) else {
return Err(RustADBError::ADBRequestFailed(
"SystemTime before UNIX EPOCH!".into(),
));
};
let mut done_buffer = Vec::with_capacity(8);
done_buffer.extend_from_slice(b"DONE");
done_buffer.extend_from_slice(&last_modified.as_secs().to_le_bytes());
raw_connection.write_all(&done_buffer)?;
let mut request_status = [0; 4];
raw_connection.read_exact(&mut request_status)?;
match AdbRequestStatus::from_str(str::from_utf8(&request_status)?)? {
AdbRequestStatus::Fail => {
let length = self.transport.get_body_length()?;
let mut body = vec![
0;
length
.try_into()
.map_err(|_| RustADBError::ConversionError)?
];
if length > 0 {
self.transport.get_raw_connection()?.read_exact(&mut body)?;
}
Err(RustADBError::ADBRequestFailed(String::from_utf8(body)?))
}
AdbRequestStatus::Okay => Ok(()),
}
}
}