windows-rs
windows-rs copied to clipboard
Is there support for the `std::io::Read` trait somehow?
I'm trying to use windows::Web::Http::HttpClient
to download a file. I'm using HttpClient::GetInputStreamAsync
to get an IInputStream
and would like to somehow pass that to a function that expects something that implements the std::io::Read
trait. Is that somehow possible? Or am I entirely on the wrong track here? It seems to me that somehow on some very general level I'm getting a stream from the Windows API here, and it would be nice if that composed nicely with the standard trait for reading from streams in Rust :)
That would actually require a trait with an async fn
, which—to my knowledge—is not supported (IInputStream
only exposes a ReadAsync
method).
I'm not sure if we really need async in this case, since IAsyncOperation
has a sync get
method. I think the actual problem when implementing io::Read
for IInputStream
is the fact that you can't create a buffer(-view), that ReadAsync
could read into.
However, there is an argument to be made that io::Read
should be implemented for DataReader
(and similarly io::Write
for DataWriter
- or the respective interface).
Proof of concept implementation
// for demonstration
#![feature(io_read_to_string)]
use std::io;
use windows::{
core::Result,
w,
Foundation::Uri,
Storage::Streams::{DataReader, IInputStream, InputStreamOptions},
Web::Http::{HttpClient, IHttpContent},
};
struct DataReaderWrap(DataReader);
impl io::Read for DataReaderWrap {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut bytes =
self.0
.LoadAsync(buf.len() as u32)
.map_err(|e| io::Error::from_raw_os_error(e.code().0))?
.get()
.map_err(|e| io::Error::from_raw_os_error(e.code().0))? as usize;
bytes = bytes.min(buf.len());
self.0
.ReadBytes(&mut buf[0..bytes])
.map_err(|e| io::Error::from_raw_os_error(e.code().0))
.map(|_| bytes)
}
}
fn main() -> Result<()> {
let uri = Uri::CreateUri(w!("https://www.rust-lang.org/"))?;
let http: HttpClient = HttpClient::new()?;
let content: IHttpContent = http.GetAsync(&uri)?.get()?.Content()?;
let is: IInputStream = content.ReadAsInputStreamAsync()?.get()?;
let reader = DataReader::CreateDataReader(&is)?;
// not needed but this might improve performance
reader.SetInputStreamOptions(InputStreamOptions::ReadAhead)?;
println!("{:?}", io::read_to_string(DataReaderWrap(reader)));
Ok(())
}
@Nerixyz Fantastic, that piece of code solved my problem completely, thanks so much! And agreed, it would be nice if that was included by default in the crate.
Neat, thanks for the example @Nerixyz! I'll think about whether this trait can offered automatically.