root_io/core/
data_source.rs

1use std::fs::File;
2use std::io::{Read, Seek, SeekFrom};
3#[cfg(not(target_arch = "wasm32"))]
4use std::path::Path;
5use std::path::PathBuf;
6
7use failure::Error;
8use reqwest::{
9    header::{RANGE, USER_AGENT},
10    Client, Url,
11};
12
13/// The source from where the Root file is read. Construct it using
14/// `.into()` on a `Url` or `Path`. The latter is not availible for
15/// the `wasm32` target.
16#[derive(Debug, Clone)]
17pub struct Source(SourceInner);
18
19// This inner enum hides the differentiation between the local and
20// remote files from the public API
21#[derive(Debug, Clone)]
22enum SourceInner {
23    /// A local source, i.e. a file on disc.
24    Local(PathBuf),
25    Remote {
26        client: Client,
27        url: Url,
28    },
29}
30
31impl Source {
32    pub fn new<T: Into<Self>>(thing: T) -> Self {
33        thing.into()
34    }
35
36    pub async fn fetch(&self, start: u64, len: u64) -> Result<Vec<u8>, Error> {
37        match &self.0 {
38            SourceInner::Local(path) => {
39                let mut f = File::open(&path)?;
40                f.seek(SeekFrom::Start(start))?;
41                let mut buf = vec![0; len as usize];
42                f.read_exact(&mut buf)?;
43                Ok(buf)
44            }
45            SourceInner::Remote { client, url } => {
46                let rsp = client
47                    .get(url.clone())
48                    .header(USER_AGENT, "alice-rs")
49                    .header(RANGE, format!("bytes={}-{}", start, start + len - 1))
50                    .send()
51                    .await?
52                    .error_for_status()?;
53                let bytes = rsp.bytes().await?;
54                Ok(bytes.as_ref().to_vec())
55            }
56        }
57    }
58}
59
60impl From<Url> for Source {
61    fn from(url: Url) -> Self {
62        Self(SourceInner::Remote {
63            client: Client::new(),
64            url,
65        })
66    }
67}
68
69// Disallow the construction of a local source object on wasm since
70// wasm does not have a (proper) file system.
71#[cfg(not(target_arch = "wasm32"))]
72impl From<&Path> for Source {
73    fn from(path: &Path) -> Self {
74        path.to_path_buf().into()
75    }
76}
77
78#[cfg(not(target_arch = "wasm32"))]
79impl From<PathBuf> for Source {
80    fn from(path_buf: PathBuf) -> Self {
81        Self(SourceInner::Local(path_buf))
82    }
83}