Skip to main content

ito_core/
spec_repository.rs

1//! Filesystem-backed promoted spec repository.
2
3use std::path::Path;
4
5use chrono::{DateTime, Utc};
6
7use ito_common::paths;
8use ito_domain::errors::DomainError;
9use ito_domain::specs::{SpecDocument, SpecRepository, SpecSummary};
10
11/// Filesystem-backed promoted spec repository.
12pub struct FsSpecRepository<'a> {
13    ito_path: &'a Path,
14}
15
16impl<'a> FsSpecRepository<'a> {
17    /// Create a filesystem-backed promoted spec repository.
18    pub fn new(ito_path: &'a Path) -> Self {
19        Self { ito_path }
20    }
21
22    fn spec_ids(&self) -> Result<Vec<String>, DomainError> {
23        let fs = ito_common::fs::StdFs;
24        let mut ids = ito_domain::discovery::list_spec_dir_names(&fs, self.ito_path)?;
25        ids.sort();
26        Ok(ids)
27    }
28
29    fn last_modified(&self, path: &Path) -> DateTime<Utc> {
30        let Ok(metadata) = std::fs::metadata(path) else {
31            return Utc::now();
32        };
33        let Ok(modified) = metadata.modified() else {
34            return Utc::now();
35        };
36        DateTime::<Utc>::from(modified)
37    }
38}
39
40impl SpecRepository for FsSpecRepository<'_> {
41    fn list(&self) -> Result<Vec<SpecSummary>, DomainError> {
42        let ids = self.spec_ids()?;
43        let mut specs = Vec::with_capacity(ids.len());
44        for id in ids {
45            let path = paths::spec_markdown_path(self.ito_path, &id);
46            if !path.is_file() {
47                continue;
48            }
49            specs.push(SpecSummary {
50                id,
51                path: path.clone(),
52                last_modified: self.last_modified(&path),
53            });
54        }
55        Ok(specs)
56    }
57
58    fn get(&self, id: &str) -> Result<SpecDocument, DomainError> {
59        let path = paths::spec_markdown_path(self.ito_path, id);
60        let markdown = ito_common::io::read_to_string(&path).map_err(|err| {
61            DomainError::io(
62                "reading promoted spec",
63                std::io::Error::other(err.to_string()),
64            )
65        })?;
66        Ok(SpecDocument {
67            id: id.to_string(),
68            path: path.clone(),
69            markdown,
70            last_modified: self.last_modified(&path),
71        })
72    }
73}