Skip to main content

cli/lib/readers/
mod.rs

1//! File readers for upstream `lib/readers`.
2
3pub mod file_system_reader;
4pub mod reader;
5
6use std::fs;
7use std::io;
8use std::path::PathBuf;
9
10use crate::error::{CliError, Result};
11
12#[derive(Debug)]
13pub struct ReaderFileLackPermissionsError(pub io::Error);
14
15pub trait Reader {
16    fn read(&self, name: &str) -> Result<String>;
17}
18
19#[derive(Clone, Debug)]
20pub struct FileSystemReader {
21    directory: PathBuf,
22}
23
24impl FileSystemReader {
25    pub fn new(directory: impl Into<PathBuf>) -> Self {
26        Self {
27            directory: directory.into(),
28        }
29    }
30
31    pub fn read_any_of(&self, filenames: &[&str]) -> Result<Option<String>> {
32        let mut first_permission_error = None;
33
34        for filename in filenames {
35            match self.read(filename) {
36                Ok(content) => return Ok(Some(content)),
37                Err(CliError::Io(error)) if error.kind() == io::ErrorKind::NotFound => {}
38                Err(CliError::Io(error)) if is_permission_error(&error) => {
39                    first_permission_error.get_or_insert(error);
40                }
41                Err(error) => return Err(error),
42            }
43        }
44
45        if let Some(error) = first_permission_error {
46            Err(CliError::Io(error))
47        } else {
48            Ok(None)
49        }
50    }
51
52    pub fn directory(&self) -> &std::path::Path {
53        &self.directory
54    }
55}
56
57impl Reader for FileSystemReader {
58    fn read(&self, name: &str) -> Result<String> {
59        fs::read_to_string(self.directory.join(name)).map_err(CliError::from)
60    }
61}
62
63fn is_permission_error(error: &io::Error) -> bool {
64    matches!(
65        error.kind(),
66        io::ErrorKind::PermissionDenied | io::ErrorKind::ReadOnlyFilesystem
67    )
68}