rsynko_manager/
attention.rs1use crate::*;
2use alux_ext::ext;
3use rsynko_session::Wanted;
4use std::path::PathBuf;
5use std::time::Duration;
6
7const SUMMARY_COLUMNS: usize = 160;
9
10const DETAIL_COLUMNS: usize = 500;
12
13#[ext(name = ManagerAttentionExt)]
19pub impl<This> This
20where
21 This: QueueCatalogAlg + SafeExitAlg + TransferStateAlg + RehearsalStateAlg,
22 This::Entry: QueueEntryAlg,
23 This::Change: PlannedChangeAlg,
24 This::Id: Copy + Eq,
25{
26 fn wanting_work(&self) -> Vec<This::Id> {
28 self.queue_ids()
29 .filter(|id| self.entry_phase(*id) == Some(TransferPhase::Waiting))
30 .collect()
31 }
32
33 fn wanted(&self, id: This::Id) -> Wanted {
35 if self.exit_requested() {
37 return Wanted::Unwanted;
38 }
39 match self.entry_phase(id) {
40 None => Wanted::Unwanted,
41 Some(TransferPhase::Paused) => Wanted::Held,
42 Some(_) => Wanted::Running,
43 }
44 }
45
46 fn rehearses(&self, id: This::Id) -> bool {
48 self.queue_entry(id).and_then(QueueEntryAlg::dry_run) == Some(true)
49 }
50
51 fn begun(&mut self, id: This::Id, holdable: bool) {
56 if self.rehearses(id) {
57 self.apply_rehearsal_event(id, RehearsalObservationOp::Started {});
58 return;
59 }
60 self.apply_transfer_event(id, TransferObservationOp::Started {});
61 self.apply_transfer_event(
62 id,
63 TransferObservationOp::PauseCapability {
64 supported: holdable,
65 },
66 );
67 }
68
69 fn ran_for(&mut self, id: This::Id, elapsed: Duration) {
73 if !self.rehearses(id) {
74 self.apply_transfer_event(id, TransferObservationOp::Elapsed { elapsed });
75 }
76 }
77
78 fn refused(&mut self, id: This::Id, detail: &str) {
80 if self.rehearses(id) {
81 self.apply_rehearsal_event(
82 id,
83 RehearsalObservationOp::Failed {
84 message: summary_line(detail),
85 },
86 );
87 return;
88 }
89 self.apply_transfer_event(
90 id,
91 TransferObservationOp::ProgramFailed {
92 summary: summary_line(detail),
93 detail: detail_text(detail),
94 },
95 );
96 }
97
98 fn performed(&mut self, id: This::Id, destination: PathBuf, changes: Vec<This::Change>) {
103 if self.rehearses(id) {
104 self.apply_rehearsal_event(id, RehearsalObservationOp::Reported { changes });
105 return;
106 }
107 let bytes = changes
108 .iter()
109 .filter_map(PlannedChangeAlg::change_size)
110 .fold(0_u64, u64::saturating_add);
111 self.apply_transfer_event(id, TransferObservationOp::Completed { destination, bytes });
112 }
113
114 fn destination_is_taken(&self, id: This::Id) -> bool {
120 let Some(destination) = self.queue_entry(id).and_then(QueueEntryAlg::output) else {
121 return false;
122 };
123 self.queue_ids()
124 .filter(|other| *other != id)
125 .filter(|other| {
126 self.entry_phase(*other)
127 .is_some_and(TransferPhase::is_running)
128 })
129 .any(|other| {
130 self.queue_entry(other).and_then(QueueEntryAlg::output) == Some(destination)
131 })
132 }
133
134 fn entry_phase(&self, id: This::Id) -> Option<TransferPhase> {
136 self.queue_entry(id).map(QueueEntryAlg::phase)
137 }
138}
139
140pub fn summary_line(detail: &str) -> String {
142 detail
143 .lines()
144 .next()
145 .unwrap_or_default()
146 .chars()
147 .take(SUMMARY_COLUMNS)
148 .collect()
149}
150
151pub fn detail_text(detail: &str) -> String {
153 detail.chars().take(DETAIL_COLUMNS).collect()
154}