use crate::PlannedChange;
use ambassador::Delegate;
use rsynko_manager::ChangeKind;
use rsynko_rsync::*;
use std::cell::RefCell;
use std::collections::VecDeque;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct RsyncSyntax;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RsyncEndpoint {
pub user: Option<String>,
pub host: Option<String>,
pub path: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SyncCommand {
pub program: String,
pub arguments: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SyncObservation {
Change(PlannedChange),
Progress {
transferred: u64,
percent: u16,
},
Nothing,
}
impl RsyncSorts for RsyncSyntax {
type Endpoint = RsyncEndpoint;
type Command = SyncCommand;
type Observation = SyncObservation;
type Change = PlannedChange;
}
impl RsyncEndpointAlg for RsyncSyntax {
fn endpoint(
&self,
user: Option<String>,
host: Option<String>,
path: impl Into<String>,
) -> Self::Endpoint {
RsyncEndpoint {
user,
host,
path: path.into(),
}
}
}
impl RsyncEndpointViewAlg for RsyncSyntax {
fn endpoint_user<'a>(&self, endpoint: &'a Self::Endpoint) -> Option<&'a str> {
endpoint.user.as_deref()
}
fn endpoint_host<'a>(&self, endpoint: &'a Self::Endpoint) -> Option<&'a str> {
endpoint.host.as_deref()
}
fn endpoint_path<'a>(&self, endpoint: &'a Self::Endpoint) -> &'a str {
&endpoint.path
}
}
impl SyncCommandAlg for RsyncSyntax {
fn sync_command(
&self,
program: impl Into<String>,
arguments: impl IntoIterator<Item = String>,
) -> Self::Command {
SyncCommand {
program: program.into(),
arguments: arguments.into_iter().collect(),
}
}
}
impl SyncCommandViewAlg for RsyncSyntax {
fn command_program<'a>(&self, command: &'a Self::Command) -> &'a str {
&command.program
}
fn command_arguments<'a>(&self, command: &'a Self::Command) -> impl Iterator<Item = &'a str> {
command.arguments.iter().map(String::as_str)
}
}
impl SyncChangeAlg for RsyncSyntax {
fn sync_change(
&self,
path: impl Into<String>,
kind: ChangeKind,
size: Option<u64>,
) -> Self::Change {
PlannedChange::new(path.into(), kind, size)
}
}
impl SyncObservationAlg for RsyncSyntax {
fn observed_change(&self, change: Self::Change) -> Self::Observation {
SyncObservation::Change(change)
}
fn observed_progress(&self, transferred: u64, percent: u16) -> Self::Observation {
SyncObservation::Progress {
transferred,
percent,
}
}
fn observed_nothing(&self) -> Self::Observation {
SyncObservation::Nothing
}
}
impl SyncObservationViewAlg for RsyncSyntax {
fn observation_change<'a>(
&self,
observation: &'a Self::Observation,
) -> Option<&'a Self::Change> {
match observation {
SyncObservation::Change(change) => Some(change),
SyncObservation::Progress { .. } | SyncObservation::Nothing => None,
}
}
fn observation_progress(&self, observation: &Self::Observation) -> Option<(u64, u16)> {
match observation {
SyncObservation::Progress {
transferred,
percent,
} => Some((*transferred, *percent)),
SyncObservation::Change(_) | SyncObservation::Nothing => None,
}
}
}
#[allow(
clippy::duplicated_attributes,
reason = "one delegation per capability, not per target"
)]
#[derive(Clone, Debug, Default, Delegate)]
#[delegate(RsyncEndpointAlg, target = "syntax")]
#[delegate(RsyncEndpointViewAlg, target = "syntax")]
#[delegate(SyncCommandAlg, target = "syntax")]
#[delegate(SyncCommandViewAlg, target = "syntax")]
#[delegate(SyncChangeAlg, target = "syntax")]
#[delegate(SyncObservationAlg, target = "syntax")]
#[delegate(SyncObservationViewAlg, target = "syntax")]
pub struct ReferenceSyncEnv {
syntax: RsyncSyntax,
transcript: Vec<String>,
refusal: Option<String>,
commands: RefCell<Vec<SyncCommand>>,
watched: RefCell<Vec<SyncObservation>>,
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("the reference transfer refused: {0}")]
pub struct ReferenceSyncError(pub String);
impl RsyncSorts for ReferenceSyncEnv {
type Endpoint = RsyncEndpoint;
type Command = SyncCommand;
type Observation = SyncObservation;
type Change = PlannedChange;
}
impl ReferenceSyncEnv {
pub fn register_transcript(&mut self, lines: impl IntoIterator<Item = String>) {
self.transcript = lines.into_iter().collect();
}
pub fn refuse_transfers(&mut self, reason: impl Into<String>) {
self.refusal = Some(reason.into());
}
#[must_use]
pub fn commands(&self) -> Vec<SyncCommand> {
self.commands.borrow().clone()
}
#[must_use]
pub fn watched(&self) -> Vec<SyncObservation> {
self.watched.borrow().clone()
}
}
impl SyncRunAlg for ReferenceSyncEnv {
type Error = ReferenceSyncError;
type Run = VecDeque<String>;
fn start_sync(&self, command: &Self::Command) -> Result<Self::Run, Self::Error> {
self.commands.borrow_mut().push(command.clone());
match &self.refusal {
Some(reason) => Err(ReferenceSyncError(reason.clone())),
None => Ok(self.transcript.iter().cloned().collect()),
}
}
fn next_sync_line(&self, run: &mut Self::Run) -> Result<Option<String>, Self::Error> {
Ok(run.pop_front())
}
fn finish_sync(&self, _run: Self::Run) -> Result<(), Self::Error> {
Ok(())
}
}
impl SyncWatchAlg for ReferenceSyncEnv {
fn watch_sync(&self, observation: &Self::Observation) {
self.watched.borrow_mut().push(observation.clone());
}
}