1use std::{
4 path::{Path, PathBuf},
5 str::FromStr,
6};
7
8use crate::Error;
9
10#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
12pub enum DirOrFileType {
13 Dir,
15 File,
17}
18
19#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct DirOrFile {
25 path: PathBuf,
26 pub typ: DirOrFileType,
28}
29
30impl AsRef<Path> for DirOrFile {
31 fn as_ref(&self) -> &Path {
32 &self.path
33 }
34}
35
36impl TryFrom<PathBuf> for DirOrFile {
37 type Error = Error;
38
39 fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
45 if value.is_dir() {
46 Ok(Self {
47 path: value,
48 typ: DirOrFileType::Dir,
49 })
50 } else if value.is_file() {
51 Ok(Self {
52 path: value,
53 typ: DirOrFileType::File,
54 })
55 } else {
56 Err(crate::Error::PathIsNotDirOrFile { path: value })
57 }
58 }
59}
60
61impl TryFrom<&Path> for DirOrFile {
62 type Error = Error;
63
64 fn try_from(value: &Path) -> Result<Self, Self::Error> {
70 Self::try_from(value.to_path_buf())
71 }
72}
73
74impl FromStr for DirOrFile {
75 type Err = Error;
76
77 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 Self::try_from(PathBuf::from(s))
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use tempfile::{NamedTempFile, TempDir};
90 use testresult::TestResult;
91
92 use super::*;
93
94 #[test]
95 fn dir_or_file_from_str_is_dir() -> TestResult {
96 let temp = TempDir::new()?;
97 let Some(path) = temp.path().to_str() else {
98 return Err("Could not convert temporary dir to string slice".into());
99 };
100
101 let dir = DirOrFile::from_str(path)?;
102 assert_eq!(dir.typ, DirOrFileType::Dir);
103 assert_eq!(dir.as_ref(), temp.path());
104 Ok(())
105 }
106
107 #[test]
108 fn dir_or_file_from_str_is_file() -> TestResult {
109 let temp = NamedTempFile::new()?;
110 let Some(path) = temp.path().to_str() else {
111 return Err("Could not convert temporary dir to string slice".into());
112 };
113
114 let dir = DirOrFile::from_str(path)?;
115 assert_eq!(dir.typ, DirOrFileType::File);
116 assert_eq!(dir.as_ref(), temp.path());
117 Ok(())
118 }
119
120 #[test]
121 #[cfg(target_os = "linux")]
122 fn dir_or_file_from_str_fails_on_not_a_dir_or_a_file() -> TestResult {
123 let result = DirOrFile::from_str("/dev/urandom");
124 match result {
125 Ok(path) => {
126 return Err(format!(
127 "Succeeded to create a DirOrFile from {path:?} but should have failed"
128 )
129 .into());
130 }
131 Err(Error::PathIsNotDirOrFile { .. }) => {}
132 Err(error) => {
133 return Err(format!(
134 "Should have returned Error::PathIsNotDirOrFile, but returned: {error}"
135 )
136 .into());
137 }
138 }
139 Ok(())
140 }
141}