1
use chrono::{DateTime, Utc};
2
use std::{
3
    ffi::OsStr,
4
    fs::DirEntry,
5
    path::{Path, PathBuf},
6
};
7

            
8
pub(crate) struct IndexPath(PathBuf);
9

            
10
impl IndexPath {
11
    pub(crate) fn to_path_buf(&self) -> PathBuf {
12
        self.0.clone()
13
    }
14
26
    fn entries(&self) -> std::io::Result<impl Iterator<Item = DirEntry>> {
15
26
        std::fs::read_dir(self.0.parent().unwrap()).map(|iter| {
16
110
            iter.filter_map(|e| e.ok()).filter(|e| {
17
110
                if e.file_name() == INDEX_NAME {
18
26
                    return false;
19
84
                }
20
84
                // skip entries that are not files
21
84
                e.file_type().map(|ft| ft.is_file()).unwrap_or_default()
22
110
            })
23
26
        })
24
26
    }
25
16
    pub(crate) fn file_paths(&self) -> std::io::Result<impl Iterator<Item = PathBuf>> {
26
64
        self.entries().map(|f| f.map(|e| e.path()))
27
16
    }
28
10
    pub(crate) fn files(&self) -> std::io::Result<impl Iterator<Item = String>> {
29
10
        self.entries()
30
20
            .map(|f| f.filter_map(|path| path.file_name().to_str().map(|s| s.to_string())))
31
10
    }
32
16
    pub(crate) fn dir_name(&self) -> &OsStr {
33
16
        self.0.parent().unwrap().file_name().unwrap()
34
16
    }
35
16
    pub(crate) fn date(&self) -> DateTime<Utc> {
36
16
        DateTime::parse_from_rfc3339(self.dir_name().to_str().unwrap())
37
16
            .unwrap()
38
16
            .into()
39
16
    }
40
}
41

            
42
40
#[derive(Debug, thiserror::Error)]
43
#[error("{0}")]
44
pub(crate) struct InvalidIndexPath(String, PathBuf);
45

            
46
const INDEX_NAME: &str = "index.ttl";
47

            
48
impl TryFrom<PathBuf> for IndexPath {
49
    type Error = InvalidIndexPath;
50

            
51
86
    fn try_from(dir: PathBuf) -> Result<Self, Self::Error> {
52
86
        let file_name = if let Some(file_name) = dir.file_name() {
53
76
            file_name
54
        } else {
55
10
            return Err(InvalidIndexPath(
56
10
                format!("{} does not contain a file name.", dir.display()),
57
10
                dir,
58
10
            ));
59
        };
60
76
        let file_name = if let Some(file_name) = file_name.to_str() {
61
66
            file_name
62
        } else {
63
10
            return Err(InvalidIndexPath(
64
10
                format!(
65
10
                    "The name {} is not valid unicode",
66
10
                    file_name.to_string_lossy()
67
10
                ),
68
10
                dir,
69
10
            ));
70
        };
71
66
        if DateTime::parse_from_rfc3339(file_name).is_err() {
72
10
            return Err(InvalidIndexPath(
73
10
                format!("{} is not a valid rfc339 date", file_name),
74
10
                dir,
75
10
            ));
76
56
        }
77
56
        let path = dir.join(INDEX_NAME);
78
56
        if !path.exists() {
79
10
            return Err(InvalidIndexPath(
80
10
                format!("{} does not exist", path.display()),
81
10
                dir,
82
10
            ));
83
46
        }
84
46
        Ok(Self(path))
85
86
    }
86
}
87
impl TryFrom<&Path> for IndexPath {
88
    type Error = InvalidIndexPath;
89
70
    fn try_from(value: &Path) -> Result<Self, Self::Error> {
90
70
        IndexPath::try_from(value.to_path_buf())
91
70
    }
92
}
93

            
94
impl AsRef<Path> for IndexPath {
95
30
    fn as_ref(&self) -> &Path {
96
30
        &self.0
97
30
    }
98
}