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