git_bug/replica/entity/lamport/
persistent.rs1use std::{
14 fs::{File, OpenOptions},
15 io::{Read, Write},
16 path::{Path, PathBuf},
17};
18
19use super::{Clock, mem::MemClock};
20
21#[derive(Debug)]
25pub struct PersistedClock {
26 clock: MemClock,
27 file_path: PathBuf,
28}
29
30impl Clock for PersistedClock {
31 type IncrementError = write::Error;
32 type WitnessError = write::Error;
33
34 fn time(&self) -> super::Time {
35 self.clock.time()
36 }
37
38 fn increment(&mut self) -> Result<super::Time, Self::IncrementError> {
39 let previous = self.clock.increment().expect("This error is infallible");
40 self.save_to_file(false).map(|()| previous)
41 }
42
43 fn witness(&mut self, time: super::Time) -> Result<(), Self::WitnessError> {
44 self.clock.witness(time).expect("The error is infallible");
45 self.save_to_file(false)
46 }
47}
48
49pub mod write {
51 #![allow(missing_docs)]
52 use std::{io, num::ParseIntError, path::PathBuf};
53
54 #[derive(Debug, thiserror::Error)]
55 pub enum Error {
57 #[error("Failed to write this clock to {path}, because of {error}")]
58 Write { path: PathBuf, error: io::Error },
60
61 #[error("Failed to open the path '{path}' for clock, because of {error}")]
62 Open { path: PathBuf, error: io::Error },
64
65 #[error("Failed to read the contents of the path {path} for clock, because of {error}")]
66 Read { path: PathBuf, error: io::Error },
68
69 #[error(
70 "Failed to parse the contents ('{contents}') of the path {path} as clock value, \
71 because of {error}"
72 )]
73 Parse {
75 path: PathBuf,
76 error: ParseIntError,
77 contents: String,
78 },
79 }
80}
81
82impl PersistedClock {
83 fn open_file(path: &Path, options: &mut OpenOptions) -> Result<File, write::Error> {
84 options.open(path).map_err(|err| write::Error::Open {
85 error: err,
86 path: path.to_owned(),
87 })
88 }
89
90 pub fn new(path: PathBuf) -> Result<Self, write::Error> {
98 let mut me = Self {
99 clock: MemClock::new(),
100 file_path: path,
101 };
102
103 me.save_to_file(true)?;
104
105 Ok(me)
106 }
107
108 pub fn from_path(path: PathBuf) -> Result<Self, write::Error> {
113 let me = Self {
114 clock: MemClock::new_with_value(Self::time_from_file(&path)?),
115 file_path: path,
116 };
117
118 Ok(me)
119 }
120
121 fn time_from_file(path: &Path) -> Result<u64, write::Error> {
122 let mut file = Self::open_file(path, OpenOptions::new().read(true))?;
123
124 let mut contents = String::new();
125 file.read_to_string(&mut contents)
126 .map_err(|err| write::Error::Read {
127 error: err,
128 path: path.to_owned(),
129 })?;
130
131 let value: u64 = contents.parse().map_err(|err| write::Error::Parse {
132 error: err,
133 path: path.to_owned(),
134 contents,
135 })?;
136
137 Ok(value)
138 }
139
140 pub fn save_to_file(&mut self, create: bool) -> Result<(), write::Error> {
152 if self.file_path.exists() {
153 assert!(
154 Self::time_from_file(&self.file_path)? <= self.time().0,
155 "The time should have not been changed under our feet."
156 );
157 }
158
159 let mut file = Self::open_file(
160 &self.file_path,
161 OpenOptions::new().write(true).create(create),
162 )?;
163
164 write!(file, "{}", self.time().0).map_err(|err| write::Error::Write {
165 error: err,
166 path: self.file_path.clone(),
167 })
168 }
169}