v2/
io.rs

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
103// pub fn read_csv_and_then<T, P, F, U, E>(path: P, f: F) -> anyhow::Result<Vec<U>>
104// where
105//     T: DeserializeOwned,
106//     P: AsRef<Path>,
107//     F: Fn(T) -> Result<U, E>,
108//     E: std::error::Error + Send + Sync + 'static,
109// {
110//     let file = File::open(path)?;
111//     let mut rdr = csv::Reader::from_reader(file);
112//     Ok(rdr
113//         .deserialize::<T>()
114//         .into_iter()
115//         .process_results(|iter| iter.map(f).try_collect())??)
116// }
117
118pub 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}