1use crate::PlannedChange;
4use ambassador::Delegate;
5use rsynko_manager::ChangeKind;
6use rsynko_rsync::*;
7use std::cell::RefCell;
8use std::collections::VecDeque;
9
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
12pub struct RsyncSyntax;
13
14#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct RsyncEndpoint {
17 pub user: Option<String>,
19 pub host: Option<String>,
21 pub path: String,
23}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct SyncCommand {
28 pub program: String,
30 pub arguments: Vec<String>,
32}
33
34#[derive(Clone, Debug, PartialEq, Eq)]
36pub enum SyncObservation {
37 Change(PlannedChange),
39 Progress {
41 transferred: u64,
43 percent: u16,
45 },
46 Nothing,
48}
49
50impl RsyncSorts for RsyncSyntax {
51 type Endpoint = RsyncEndpoint;
52 type Command = SyncCommand;
53 type Observation = SyncObservation;
54 type Change = PlannedChange;
55}
56
57impl RsyncEndpointAlg for RsyncSyntax {
58 fn endpoint(
59 &self,
60 user: Option<String>,
61 host: Option<String>,
62 path: impl Into<String>,
63 ) -> Self::Endpoint {
64 RsyncEndpoint {
65 user,
66 host,
67 path: path.into(),
68 }
69 }
70}
71
72impl RsyncEndpointViewAlg for RsyncSyntax {
73 fn endpoint_user<'a>(&self, endpoint: &'a Self::Endpoint) -> Option<&'a str> {
74 endpoint.user.as_deref()
75 }
76
77 fn endpoint_host<'a>(&self, endpoint: &'a Self::Endpoint) -> Option<&'a str> {
78 endpoint.host.as_deref()
79 }
80
81 fn endpoint_path<'a>(&self, endpoint: &'a Self::Endpoint) -> &'a str {
82 &endpoint.path
83 }
84}
85
86impl SyncCommandAlg for RsyncSyntax {
87 fn sync_command(
88 &self,
89 program: impl Into<String>,
90 arguments: impl IntoIterator<Item = String>,
91 ) -> Self::Command {
92 SyncCommand {
93 program: program.into(),
94 arguments: arguments.into_iter().collect(),
95 }
96 }
97}
98
99impl SyncCommandViewAlg for RsyncSyntax {
100 fn command_program<'a>(&self, command: &'a Self::Command) -> &'a str {
101 &command.program
102 }
103
104 fn command_arguments<'a>(&self, command: &'a Self::Command) -> impl Iterator<Item = &'a str> {
105 command.arguments.iter().map(String::as_str)
106 }
107}
108
109impl SyncChangeAlg for RsyncSyntax {
110 fn sync_change(
111 &self,
112 path: impl Into<String>,
113 kind: ChangeKind,
114 size: Option<u64>,
115 ) -> Self::Change {
116 PlannedChange::new(path.into(), kind, size)
117 }
118}
119
120impl SyncObservationAlg for RsyncSyntax {
121 fn observed_change(&self, change: Self::Change) -> Self::Observation {
122 SyncObservation::Change(change)
123 }
124
125 fn observed_progress(&self, transferred: u64, percent: u16) -> Self::Observation {
126 SyncObservation::Progress {
127 transferred,
128 percent,
129 }
130 }
131
132 fn observed_nothing(&self) -> Self::Observation {
133 SyncObservation::Nothing
134 }
135}
136
137impl SyncObservationViewAlg for RsyncSyntax {
138 fn observation_change<'a>(
139 &self,
140 observation: &'a Self::Observation,
141 ) -> Option<&'a Self::Change> {
142 match observation {
143 SyncObservation::Change(change) => Some(change),
144 SyncObservation::Progress { .. } | SyncObservation::Nothing => None,
145 }
146 }
147
148 fn observation_progress(&self, observation: &Self::Observation) -> Option<(u64, u16)> {
149 match observation {
150 SyncObservation::Progress {
151 transferred,
152 percent,
153 } => Some((*transferred, *percent)),
154 SyncObservation::Change(_) | SyncObservation::Nothing => None,
155 }
156 }
157}
158
159#[allow(
162 clippy::duplicated_attributes,
163 reason = "one delegation per capability, not per target"
164)]
165#[derive(Clone, Debug, Default, Delegate)]
166#[delegate(RsyncEndpointAlg, target = "syntax")]
167#[delegate(RsyncEndpointViewAlg, target = "syntax")]
168#[delegate(SyncCommandAlg, target = "syntax")]
169#[delegate(SyncCommandViewAlg, target = "syntax")]
170#[delegate(SyncChangeAlg, target = "syntax")]
171#[delegate(SyncObservationAlg, target = "syntax")]
172#[delegate(SyncObservationViewAlg, target = "syntax")]
173pub struct ReferenceSyncEnv {
174 syntax: RsyncSyntax,
175 transcript: Vec<String>,
176 refusal: Option<String>,
177 commands: RefCell<Vec<SyncCommand>>,
178 watched: RefCell<Vec<SyncObservation>>,
179}
180
181#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
183#[error("the reference transfer refused: {0}")]
184pub struct ReferenceSyncError(pub String);
185
186impl RsyncSorts for ReferenceSyncEnv {
187 type Endpoint = RsyncEndpoint;
188 type Command = SyncCommand;
189 type Observation = SyncObservation;
190 type Change = PlannedChange;
191}
192
193impl ReferenceSyncEnv {
194 pub fn register_transcript(&mut self, lines: impl IntoIterator<Item = String>) {
196 self.transcript = lines.into_iter().collect();
197 }
198
199 pub fn refuse_transfers(&mut self, reason: impl Into<String>) {
201 self.refusal = Some(reason.into());
202 }
203
204 #[must_use]
206 pub fn commands(&self) -> Vec<SyncCommand> {
207 self.commands.borrow().clone()
208 }
209
210 #[must_use]
212 pub fn watched(&self) -> Vec<SyncObservation> {
213 self.watched.borrow().clone()
214 }
215}
216
217impl SyncRunAlg for ReferenceSyncEnv {
218 type Error = ReferenceSyncError;
219 type Run = VecDeque<String>;
220
221 fn start_sync(&self, command: &Self::Command) -> Result<Self::Run, Self::Error> {
222 self.commands.borrow_mut().push(command.clone());
223 match &self.refusal {
224 Some(reason) => Err(ReferenceSyncError(reason.clone())),
225 None => Ok(self.transcript.iter().cloned().collect()),
226 }
227 }
228
229 fn next_sync_line(&self, run: &mut Self::Run) -> Result<Option<String>, Self::Error> {
230 Ok(run.pop_front())
231 }
232
233 fn finish_sync(&self, _run: Self::Run) -> Result<(), Self::Error> {
234 Ok(())
235 }
236}
237
238impl SyncWatchAlg for ReferenceSyncEnv {
239 fn watch_sync(&self, observation: &Self::Observation) {
240 self.watched.borrow_mut().push(observation.clone());
241 }
242}