Skip to main content

rust_releases_io/client/
fs_client.rs

1use crate::client::errors::IoError;
2use crate::{
3    Document, IsStaleError, ResourceFile, RetrievalLocation, RetrievedDocument, RustReleasesClient,
4};
5use std::path::Path;
6use std::{fs, io};
7
8const DEFAULT_MEMORY_SIZE: usize = 4096;
9
10/// A client to fetch resources from the local file system.
11///
12/// The full file path of the resource to be fetched must be given to
13/// [`FsClient::fetch`].
14#[derive(Debug, Default)]
15pub struct FsClient;
16
17impl RustReleasesClient for FsClient {
18    type Error = FsClientError;
19
20    fn fetch(&self, resource: ResourceFile) -> Result<RetrievedDocument, Self::Error> {
21        // disadvantage of the current API is that the resource file will require the path to
22        // be representable as a &str.
23        let path = Path::new(resource.url);
24
25        let file =
26            fs::File::open(path).map_err(|e| IoError::inaccessible(e, path.to_path_buf()))?;
27        let mut reader = io::BufReader::new(file);
28
29        let document = read_document(&mut reader)?;
30
31        Ok(RetrievedDocument::new(
32            document,
33            RetrievalLocation::Path(path.to_path_buf()),
34        ))
35    }
36}
37
38fn read_document(reader: &mut impl io::BufRead) -> Result<Document, FsClientError> {
39    let mut buffer = Vec::with_capacity(DEFAULT_MEMORY_SIZE);
40
41    let bytes_read = reader
42        .read_to_end(&mut buffer)
43        .map_err(IoError::auxiliary)?;
44
45    if bytes_read == 0 {
46        return Err(FsClientError::EmptyFile);
47    }
48
49    Ok(Document::new(buffer))
50}
51
52/// A list of errors which may be produced by [`CachedClient::fetch`].
53#[derive(Debug, thiserror::Error)]
54#[non_exhaustive]
55pub enum FsClientError {
56    /// Returned if the fetched file was empty.
57    #[error("Received empty file")]
58    EmptyFile,
59
60    /// Returned in case of an `std::io::Error`.
61    #[error(transparent)]
62    Io(#[from] IoError),
63
64    /// Returned in case it wasn't possible to check whether the cache file is
65    /// stale or not.
66    #[error(transparent)]
67    IsStale(#[from] IsStaleError),
68}