hawk_cli/models/
files.rs

1use serde::{de, Serialize};
2use std::{fmt, fs, io, path::Path};
3
4pub type Result<T> = std::result::Result<T, FileError>;
5
6#[derive(Debug, Clone)]
7struct PackageJsonError;
8
9#[derive(Debug, Clone)]
10pub enum FileKind {
11    JSON,
12    YAML,
13}
14
15impl FileKind {
16    pub fn from_path(p: &Path) -> std::result::Result<FileKind, FileError> {
17        if !p.is_file() || p.extension().is_none() {
18            return Err(FileError::UnsupportedExtension);
19        }
20
21        match p.extension().unwrap().to_str().unwrap() {
22            "json" => Ok(FileKind::JSON),
23            "yml" | "yaml" => Ok(FileKind::YAML),
24            _ => Err(FileError::UnsupportedExtension),
25        }
26    }
27}
28
29pub trait File<T>
30where
31    T: de::DeserializeOwned,
32{
33    /// Reads file from filesystem. It must be json or yaml.
34    fn load(path: &Path) -> Result<T> {
35        let r = fs::File::open(path)?;
36        let kind = FileKind::from_path(path)?;
37
38        match kind {
39            FileKind::JSON => match serde_json::from_reader(r) {
40                Ok(d) => Ok(d),
41                Err(e) => Err(FileError::from(e)),
42            },
43            FileKind::YAML => match serde_yaml::from_reader(r) {
44                Ok(d) => Ok(d),
45                Err(e) => Err(FileError::from(e)),
46            },
47        }
48    }
49
50    fn write(&self, path: &Path) -> Result<()>
51    where
52        Self: Serialize,
53    {
54        let r = fs::File::create(path)?;
55        let kind = FileKind::from_path(path)?;
56
57        match kind {
58            FileKind::JSON => serde_json::to_writer_pretty(r, self)?,
59            FileKind::YAML => serde_yaml::to_writer(r, self)?,
60        }
61
62        Ok(())
63    }
64}
65
66#[derive(Debug)]
67pub enum FileError {
68    NotFound,
69    UnsupportedExtension,
70    InvalidYAMLSyntax(serde_yaml::Error),
71    InvalidJSONSyntax(serde_json::Error),
72    IO(io::Error),
73}
74
75impl fmt::Display for FileError {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        let message: String = match self {
78            FileError::NotFound => "No such file or directory".into(),
79            FileError::UnsupportedExtension => {
80                "unsupported file extension (allowed: yaml | yml | json)".into()
81            }
82            FileError::InvalidYAMLSyntax(err) => format!("Invalid YAML syntax: {:?}", err),
83            FileError::InvalidJSONSyntax(err) => format!("Invalid JSON syntax: {:?}", err),
84            FileError::IO(err) => format!("{}", err),
85        };
86
87        write!(f, "{}", message)
88    }
89}
90
91impl From<serde_json::Error> for FileError {
92    fn from(e: serde_json::Error) -> Self {
93        FileError::InvalidJSONSyntax(e)
94    }
95}
96
97impl From<serde_yaml::Error> for FileError {
98    fn from(e: serde_yaml::Error) -> Self {
99        FileError::InvalidYAMLSyntax(e)
100    }
101}
102
103impl From<io::Error> for FileError {
104    fn from(e: io::Error) -> Self {
105        match e.kind() {
106            io::ErrorKind::NotFound => FileError::NotFound,
107            _ => FileError::IO(e),
108        }
109    }
110}