use std::borrow::Cow;
use std::path::Path;
use crate::logging::debug;
use crate::fs::helpers::os_str_to_str;
use crate::rpc::res::Response;
use crate::transport::Transport;
use crate::transport::serial::rpc::CommandIndex;
use crate::{
error::{Error, Result},
proto,
rpc::req::Request,
transport::TransportRaw,
};
pub trait FsRead {
fn fs_read(&mut self, path: impl AsRef<Path>) -> Result<Cow<'static, [u8]>>;
fn fs_read_to_string(&mut self, path: impl AsRef<Path>) -> Result<Cow<'static, str>> {
let bytes = self.fs_read(path)?;
match bytes {
Cow::Borrowed(bytes) => std::str::from_utf8(bytes)
.map(Cow::Borrowed)
.map_err(|e| e.to_string()),
Cow::Owned(bytes) => String::from_utf8(bytes)
.map(Cow::Owned)
.map_err(|e| e.to_string()),
}
.map_err(|error_text| {
std::io::Error::new(std::io::ErrorKind::InvalidData, error_text).into()
})
}
fn fs_read_to_string_lossy(&mut self, path: impl AsRef<Path>) -> Result<Cow<'static, str>> {
#[inline(always)]
fn string_from_utf8_lossy(buf: Vec<u8>) -> String {
match String::from_utf8_lossy(&buf) {
Cow::Owned(s) => s,
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(buf) },
}
}
let bytes = self.fs_read(path)?;
match bytes {
Cow::Borrowed(bytes) => Ok(String::from_utf8_lossy(bytes)),
Cow::Owned(bytes) => Ok(Cow::Owned(string_from_utf8_lossy(bytes))),
}
}
}
impl<T> FsRead for T
where
T: TransportRaw<proto::Main, proto::Main, Err = Error> + CommandIndex + std::fmt::Debug,
{
fn fs_read(&mut self, path: impl AsRef<Path>) -> Result<Cow<'static, [u8]>> {
let path = os_str_to_str(path.as_ref().as_os_str())?;
#[cfg(feature = "fs-read-metadata")]
let size: Option<u32> = self
.send_and_receive(Request::StorageMetadata(path.to_string()))?
.try_into()?;
#[cfg(feature = "fs-read-metadata")]
let mut buf = match size {
Some(size) => Vec::with_capacity(size as usize), None => vec![],
};
#[cfg(not(feature = "fs-read-metadata"))]
let mut buf = vec![];
debug!("init read chain");
self.send(Request::StorageRead(path.to_string()))?;
loop {
let response = self.receive_raw()?;
debug!("read rpc chunk");
let has_next = response.has_next;
let response: Option<Cow<'static, [u8]>> = Response::try_from(response)?.try_into()?;
match response {
None => {
return Err(std::io::Error::other("Failed to read file").into());
}
Some(data) => {
buf.extend_from_slice(data.as_ref());
}
}
if !has_next {
break;
}
}
Ok(buf.into())
}
}