windows-rs icon indicating copy to clipboard operation
windows-rs copied to clipboard

Is there support for the `std::io::Read` trait somehow?

Open davidanthoff opened this issue 2 years ago • 4 comments

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 :)

davidanthoff avatar Aug 06 '22 05:08 davidanthoff

That would actually require a trait with an async fn, which—to my knowledge—is not supported (IInputStream only exposes a ReadAsync method).

tim-weis avatar Aug 06 '22 09:08 tim-weis

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 avatar Aug 06 '22 11:08 Nerixyz

@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.

davidanthoff avatar Aug 06 '22 17:08 davidanthoff

Neat, thanks for the example @Nerixyz! I'll think about whether this trait can offered automatically.

kennykerr avatar Aug 22 '22 14:08 kennykerr