use std::{ io::{ self, Read, Seek, SeekFrom, }, }; use anyhow::{ Result, anyhow, }; pub struct NetReader { client: reqwest::blocking::Client, url: String, len: u64, cursor: u64, } impl NetReader { pub fn new (url: String) -> Result { let client = reqwest::blocking::Client::new (); let len = client.head (&url).send ()?.content_length ().ok_or_else (|| anyhow! ("Couldn't get content-length of URL"))?; Ok (NetReader { client, url, cursor: 0, len, }) } } impl Read for NetReader { fn read (&mut self, dest: &mut [u8]) -> io::Result { tracing::trace! ("NetReader reading {}-{}", self.cursor, self.cursor + dest.len () as u64); let range_hdr = format! ("bytes={}-{}", self.cursor, self.cursor + dest.len () as u64 - 1); let mut resp = self.client.get (&self.url) .header (reqwest::header::RANGE, range_hdr) .send () .map_err (|e| io::Error::new (io::ErrorKind::ConnectionRefused, e))?; let mut cursor = std::io::Cursor::new (dest); let bytes_moved = resp.copy_to (&mut cursor) .map_err (|e| io::Error::new (io::ErrorKind::ConnectionReset, e))?; self.cursor += bytes_moved; tracing::trace! ("NetReader read {} bytes", bytes_moved); Ok (bytes_moved.try_into ().unwrap ()) } } impl Seek for NetReader { fn seek (&mut self, pos: SeekFrom) -> io::Result { // If your data is as big as 63 bits you'll just have // to suffer I guess let new_cursor = match pos { SeekFrom::Start (x) => x as i64, SeekFrom::Current (x) => self.cursor as i64 + x, SeekFrom::End (x) => self.len as i64 + x, }; let new_cursor = u64::try_from (new_cursor).map_err (|e| io::Error::new (io::ErrorKind::InvalidInput, e))?; self.cursor = new_cursor; Ok (self.cursor) } }