1use std::{
2 borrow::Cow,
3 ffi::OsStr,
4 fs::File,
5 io,
6 marker::PhantomData,
7 path::{Path, PathBuf},
8 str::FromStr,
9};
10
11use itertools::{Itertools, ProcessResults};
12use serde::{de::DeserializeOwned, Deserialize};
13
14#[derive(Debug)]
15pub struct MyPath<T>(PathBuf, PhantomData<T>);
16
17impl<T> Clone for MyPath<T> {
18 fn clone(&self) -> Self {
19 Self(self.0.clone(), PhantomData)
20 }
21}
22
23impl<'de, T> Deserialize<'de> for MyPath<T> {
24 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
25 where
26 D: serde::Deserializer<'de>,
27 {
28 let path = PathBuf::deserialize(deserializer)?;
29 Ok(Self(path, PhantomData))
30 }
31}
32
33impl<T> From<PathBuf> for MyPath<T> {
34 #[inline]
35 fn from(value: PathBuf) -> Self {
36 Self::new(value)
37 }
38}
39
40impl<T> From<String> for MyPath<T> {
41 #[inline]
42 fn from(value: String) -> Self {
43 Self::new(value.into())
44 }
45}
46
47impl<T> FromStr for MyPath<T> {
48 type Err = <PathBuf as FromStr>::Err;
49 #[inline]
50 fn from_str(s: &str) -> Result<Self, Self::Err> {
51 PathBuf::from_str(s).map(Self::new)
52 }
53}
54
55impl<T, S: ?Sized + AsRef<OsStr>> From<&S> for MyPath<T> {
56 #[inline]
57 fn from(value: &S) -> Self {
58 Self::new(value.into())
59 }
60}
61
62impl<T> MyPath<T> {
63 pub fn to_string_lossy(&self) -> Cow<'_, str> {
64 self.0.to_string_lossy()
65 }
66
67 pub fn verified<P: AsRef<Path>>(&self, at: P) -> Result<PathBuf, io::Error> {
68 let path = if !self.0.is_absolute() {
69 at.as_ref().join(&self.0)
70 } else {
71 self.0.clone()
72 };
73 if path.try_exists()? {
74 Ok(path)
75 } else {
76 Err(io::Error::new(
77 io::ErrorKind::NotFound,
78 format!("Path {} does not exist.", path.to_string_lossy()),
79 ))
80 }
81 }
82
83 fn new(buf: PathBuf) -> Self {
84 Self(buf, PhantomData)
85 }
86
87 pub fn verified_child<P: AsRef<Path>>(&self, at: P) -> Result<PathBuf, io::Error> {
88 Ok(self
89 .verified(at)?
90 .parent()
91 .map_or_else(|| "./".into(), |p| p.to_path_buf()))
92 }
93
94 pub fn parse_at<P, F>(self, at: P, f: F) -> anyhow::Result<T>
95 where
96 P: AsRef<Path>,
97 F: Fn(PathBuf) -> anyhow::Result<T>,
98 {
99 f(self.verified(at)?)
100 }
101}
102
103pub fn read_csv_with<T, P, F, U>(path: P, processor: F) -> anyhow::Result<U>
119where
120 T: DeserializeOwned,
121 P: AsRef<Path>,
122 F: FnOnce(ProcessResults<csv::DeserializeRecordsIter<File, T>, csv::Error>) -> U,
123{
124 let file = File::open(path)?;
125 let mut rdr = csv::Reader::from_reader(file);
126 Ok(rdr.deserialize::<T>().process_results(processor)?)
127}
128
129pub fn read_csv<T, P>(path: P) -> anyhow::Result<Vec<T>>
130where
131 T: DeserializeOwned,
132 P: AsRef<Path>,
133{
134 read_csv_with(path, |iter| iter.collect_vec())
135}
136
137#[cfg(test)]
138mod tests {
139 use super::MyPath;
140
141 #[test]
142 fn test_my_path() {
143 let p: MyPath<()> = "./hoge/fuga".into();
144 let q = p.verified("");
145 println!("{q:?}");
146 assert!(q.is_err());
147 let p: MyPath<()> = "./src/".into();
148 assert!(p.verified("").is_ok());
149 let p: MyPath<()> = "./src/io.rs".into();
150 assert!(p.verified("").is_ok());
151 }
152}