1pub 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}