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(&self, user: Option<String>, host: Option<String>, path: impl Into<String>) -> Self::Endpoint {
59 RsyncEndpoint { user, host, path: path.into() }
60 }
61}
62
63impl RsyncEndpointViewAlg for RsyncSyntax {
64 fn endpoint_user<'a>(&self, endpoint: &'a Self::Endpoint) -> Option<&'a str> {
65 endpoint.user.as_deref()
66 }
67
68 fn endpoint_host<'a>(&self, endpoint: &'a Self::Endpoint) -> Option<&'a str> {
69 endpoint.host.as_deref()
70 }
71
72 fn endpoint_path<'a>(&self, endpoint: &'a Self::Endpoint) -> &'a str {
73 &endpoint.path
74 }
75}
76
77impl SyncCommandAlg for RsyncSyntax {
78 fn sync_command(&self, program: impl Into<String>, arguments: impl IntoIterator<Item = String>) -> Self::Command {
79 SyncCommand { program: program.into(), arguments: arguments.into_iter().collect() }
80 }
81}
82
83impl SyncCommandViewAlg for RsyncSyntax {
84 fn command_program<'a>(&self, command: &'a Self::Command) -> &'a str {
85 &command.program
86 }
87
88 fn command_arguments<'a>(&self, command: &'a Self::Command) -> impl Iterator<Item = &'a str> {
89 command.arguments.iter().map(String::as_str)
90 }
91}
92
93impl SyncChangeAlg for RsyncSyntax {
94 fn sync_change(&self, path: impl Into<String>, kind: ChangeKind, size: Option<u64>) -> Self::Change {
95 PlannedChange::new(path.into(), kind, size)
96 }
97}
98
99impl SyncObservationAlg for RsyncSyntax {
100 fn observed_change(&self, change: Self::Change) -> Self::Observation {
101 SyncObservation::Change(change)
102 }
103
104 fn observed_progress(&self, transferred: u64, percent: u16) -> Self::Observation {
105 SyncObservation::Progress { transferred, percent }
106 }
107
108 fn observed_nothing(&self) -> Self::Observation {
109 SyncObservation::Nothing
110 }
111}
112
113impl SyncObservationViewAlg for RsyncSyntax {
114 fn observation_change<'a>(&self, observation: &'a Self::Observation) -> Option<&'a Self::Change> {
115 match observation {
116 SyncObservation::Change(change) => Some(change),
117 SyncObservation::Progress { .. } | SyncObservation::Nothing => None,
118 }
119 }
120
121 fn observation_progress(&self, observation: &Self::Observation) -> Option<(u64, u16)> {
122 match observation {
123 SyncObservation::Progress { transferred, percent } => Some((*transferred, *percent)),
124 SyncObservation::Change(_) | SyncObservation::Nothing => None,
125 }
126 }
127}
128
129#[allow(clippy::duplicated_attributes, reason = "one delegation per capability, not per target")]
132#[derive(Clone, Debug, Default, Delegate)]
133#[delegate(RsyncEndpointAlg, target = "syntax")]
134#[delegate(RsyncEndpointViewAlg, target = "syntax")]
135#[delegate(SyncCommandAlg, target = "syntax")]
136#[delegate(SyncCommandViewAlg, target = "syntax")]
137#[delegate(SyncChangeAlg, target = "syntax")]
138#[delegate(SyncObservationAlg, target = "syntax")]
139#[delegate(SyncObservationViewAlg, target = "syntax")]
140pub struct ReferenceSyncEnv {
141 syntax: RsyncSyntax,
142 transcript: Vec<String>,
143 refusal: Option<String>,
144 commands: RefCell<Vec<SyncCommand>>,
145 watched: RefCell<Vec<SyncObservation>>,
146}
147
148#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
150#[error("the reference transfer refused: {0}")]
151pub struct ReferenceSyncError(pub String);
152
153impl RsyncSorts for ReferenceSyncEnv {
154 type Endpoint = RsyncEndpoint;
155 type Command = SyncCommand;
156 type Observation = SyncObservation;
157 type Change = PlannedChange;
158}
159
160impl ReferenceSyncEnv {
161 pub fn register_transcript(&mut self, lines: impl IntoIterator<Item = String>) {
163 self.transcript = lines.into_iter().collect();
164 }
165
166 pub fn refuse_transfers(&mut self, reason: impl Into<String>) {
168 self.refusal = Some(reason.into());
169 }
170
171 #[must_use]
173 pub fn commands(&self) -> Vec<SyncCommand> {
174 self.commands.borrow().clone()
175 }
176
177 #[must_use]
179 pub fn watched(&self) -> Vec<SyncObservation> {
180 self.watched.borrow().clone()
181 }
182}
183
184impl SyncRunAlg for ReferenceSyncEnv {
185 type Error = ReferenceSyncError;
186 type Run = VecDeque<String>;
187
188 fn start_sync(&self, command: &Self::Command) -> Result<Self::Run, Self::Error> {
189 self.commands.borrow_mut().push(command.clone());
190 match &self.refusal {
191 Some(reason) => Err(ReferenceSyncError(reason.clone())),
192 None => Ok(self.transcript.iter().cloned().collect()),
193 }
194 }
195
196 fn next_sync_line(&self, run: &mut Self::Run) -> Result<Option<String>, Self::Error> {
197 Ok(run.pop_front())
198 }
199
200 fn finish_sync(&self, _run: Self::Run) -> Result<(), Self::Error> {
201 Ok(())
202 }
203}
204
205impl SyncWatchAlg for ReferenceSyncEnv {
206 fn watch_sync(&self, observation: &Self::Observation) {
207 self.watched.borrow_mut().push(observation.clone());
208 }
209}