credence_lib/configuration/
coordinate.rs

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