Skip to main content

rsynko_manager/
navigation.rs

1use crate::*;
2use alux_ext::ext;
3use ambassador::delegatable_trait;
4
5/// Specifies manager-page navigation.
6#[delegatable_trait]
7pub trait NavigationStateAlg: ManagerSorts {
8    /// Observes the current page.
9    fn page(&self) -> ManagerPage<Self::Id>;
10    /// Sets the current page.
11    fn set_page(&mut self, page: ManagerPage<Self::Id>);
12}
13
14/// Specifies selection of one visible expanded-details control.
15#[delegatable_trait]
16pub trait DetailSelectionAlg {
17    /// Observes the selected details control.
18    fn selected_detail_control(&self) -> Option<DetailControl>;
19    /// Sets the selected details control.
20    fn set_selected_detail_control(&mut self, control: Option<DetailControl>);
21}
22
23/// Specifies manager-wide status messages.
24#[delegatable_trait]
25pub trait ManagerStatusAlg {
26    /// Observes the manager status message while one stands.
27    fn manager_message(&self) -> Option<&str>;
28    /// Sets or clears the manager status message.
29    fn set_manager_message(&mut self, message: Option<String>);
30}
31
32/// Specifies safe-exit state.
33#[delegatable_trait]
34pub trait SafeExitAlg {
35    /// Observes whether safe exit was requested.
36    fn exit_requested(&self) -> bool;
37    /// Records a safe-exit request.
38    fn request_safe_exit(&mut self);
39}
40
41/// Names the collection every page rests under.
42///
43/// Transferring a path is what this application does, so the collection is named after that
44/// rather than after the one source that fetches instead of transferring.
45pub const COLLECTION: &str = "Transfers";
46
47/// Derives the path from the collection to the current page.
48#[ext(name = BreadcrumbExt)]
49pub impl<This> This
50where
51    This: NavigationStateAlg + QueueCatalogAlg,
52    This::Entry: QueueEntryAlg + RequestOptionsAlg,
53    This::Id: Copy,
54{
55    /// Derives one breadcrumb per page the current page rests under, the collection first.
56    fn breadcrumbs(&self) -> Vec<Breadcrumb> {
57        let mut breadcrumbs = vec![segment(COLLECTION)];
58        match self.page() {
59            ManagerPage::Collection => {}
60            ManagerPage::AddSources => breadcrumbs.push(segment("Add sources")),
61            ManagerPage::Details(id) => breadcrumbs.push(segment(self.entry_label(id))),
62            ManagerPage::Formats(id) => {
63                // What a request chooses between is not the same thing for every request.
64                let chooses_media = self
65                    .queue_entry(id)
66                    .is_some_and(|entry| entry.performer() == Performer::Retrieval);
67                let choice = if chooses_media { "Formats" } else { "Transfer" };
68                breadcrumbs.extend([segment(self.entry_label(id)), segment(choice)]);
69            }
70            ManagerPage::Input(id) => {
71                breadcrumbs.extend([segment(self.entry_label(id)), segment("Input")]);
72            }
73            ManagerPage::Output(id) => {
74                // A file this application names is not the same thing as a path somebody stated.
75                let stated = self
76                    .queue_entry(id)
77                    .is_some_and(|entry| entry.output_naming() == OutputNaming::Stated);
78                let named = if stated { "Output" } else { "File name" };
79                breadcrumbs.extend([segment(self.entry_label(id)), segment(named)]);
80            }
81            ManagerPage::Log(id) => {
82                breadcrumbs.extend([segment(self.entry_label(id)), segment("Log")]);
83            }
84            ManagerPage::Report(id) => {
85                breadcrumbs.extend([segment(self.entry_label(id)), segment("Report")]);
86            }
87            ManagerPage::Command(id) => {
88                breadcrumbs.extend([segment(self.entry_label(id)), segment("Command")]);
89            }
90        }
91        breadcrumbs
92    }
93
94    /// Observes what one identity denotes, or that the collection no longer holds it.
95    fn entry_label(&self, id: This::Id) -> &str {
96        self.queue_entry(id)
97            .map_or("Missing entry", QueueEntryAlg::label)
98    }
99}
100
101/// Names one breadcrumb segment.
102fn segment(label: impl Into<String>) -> Breadcrumb {
103    Breadcrumb {
104        label: label.into(),
105    }
106}