credence_lib/configuration/
coordinate.rs

1use super::{super::coordinator::*, error::*};
2
3use {
4    compris::resolve::*,
5    httpdate::*,
6    kutil::{cli::depict::*, http::*},
7    notify,
8    std::{fs::*, io, path::*},
9};
10
11//
12// CoordinateConfiguration
13//
14
15/// File coordinator configuration.
16#[derive(Clone, Debug, Depict, Resolve)]
17pub struct CoordinateConfiguration {
18    /// Paths to watch. When empty coordination will be disabled.
19    #[resolve]
20    #[depict(iter(item), as(debug), style(string))]
21    pub paths: Vec<PathBuf>,
22
23    /// Coordinator path. When [None] coordination will be disabled.
24    #[resolve]
25    #[depict(option, as(debug), style(string))]
26    pub coordinator: Option<PathBuf>,
27
28    /// Whether to follow symlinks.
29    #[resolve(key = "follow-symlinks")]
30    #[depict(style(symbol))]
31    pub follow_symlinks: bool,
32
33    /// Whether to compare contents.
34    #[resolve(key = "compare-contents")]
35    #[depict(style(symbol))]
36    pub compare_contents: bool,
37
38    /// Message queue size.
39    #[resolve(key = "queue-size")]
40    #[depict(style(number))]
41    pub queue_size: usize,
42}
43
44impl CoordinateConfiguration {
45    /// Validate.
46    pub fn validate<PathT>(&mut self, base_path: PathT, default_paths: Vec<PathBuf>) -> Result<(), ConfigurationError>
47    where
48        PathT: AsRef<Path>,
49    {
50        let base_path = base_path.as_ref();
51
52        if let Some(coordinator) = &self.coordinator
53            && !coordinator.is_absolute()
54        {
55            self.coordinator = Some(base_path.join(&coordinator));
56        }
57
58        if self.paths.is_empty() {
59            self.paths = default_paths;
60        } else {
61            let mut paths = Vec::with_capacity(self.paths.len());
62            for path in &self.paths {
63                paths.push(if path.is_absolute() { path.clone() } else { base_path.join(path) });
64            }
65            self.paths = paths;
66        }
67
68        Ok(())
69    }
70
71    /// Construct a [Coordinator] if configured.
72    pub fn new_coordinator(&self) -> notify::Result<Option<Coordinator>> {
73        Ok(
74            if let Some(coordinator) = &self.coordinator
75                && !self.paths.is_empty()
76            {
77                if let Some(parent) = coordinator.parent() {
78                    tracing::info!("creating directory: {}", parent.display());
79                    create_dir_all(parent)?;
80                }
81
82                let mut coordinator = Coordinator::new(
83                    coordinator.clone(),
84                    self.follow_symlinks,
85                    self.compare_contents,
86                    self.queue_size,
87                )?;
88
89                for path in &self.paths {
90                    if path.exists() {
91                        coordinator.add(path)?;
92                    } else {
93                        tracing::info!("path does not exist: {}", path.display());
94                    }
95                }
96
97                Some(coordinator)
98            } else {
99                None
100            },
101        )
102    }
103
104    /// Coordinator modified timestamp.
105    pub fn coordinator_modified(&self) -> io::Result<Option<HttpDate>> {
106        Ok(match &self.coordinator {
107            Some(coordinator) => {
108                if coordinator.exists() {
109                    Some(file_modified(coordinator)?)
110                } else {
111                    None
112                }
113            }
114
115            None => None,
116        })
117    }
118}
119
120impl Default for CoordinateConfiguration {
121    fn default() -> Self {
122        Self {
123            paths: Default::default(),
124            coordinator: Some(PathBuf::from(".coordinator")),
125            follow_symlinks: true,
126            compare_contents: false,
127            queue_size: 128,
128        }
129    }
130}