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.queue_entry(id).is_some_and(|entry| entry.performer() == Performer::Retrieval);
65                let choice = if chooses_media { "Formats" } else { "Transfer" };
66                breadcrumbs.extend([segment(self.entry_label(id)), segment(choice)]);
67            }
68            ManagerPage::Input(id) => {
69                breadcrumbs.extend([segment(self.entry_label(id)), segment("Input")]);
70            }
71            ManagerPage::Output(id) => {
72                // A file this application names is not the same thing as a path somebody stated.
73                let stated = self.queue_entry(id).is_some_and(|entry| entry.output_naming() == OutputNaming::Stated);
74                let named = if stated { "Output" } else { "File name" };
75                breadcrumbs.extend([segment(self.entry_label(id)), segment(named)]);
76            }
77            ManagerPage::Log(id) => {
78                breadcrumbs.extend([segment(self.entry_label(id)), segment("Log")]);
79            }
80            ManagerPage::Report(id) => {
81                breadcrumbs.extend([segment(self.entry_label(id)), segment("Report")]);
82            }
83            ManagerPage::Command(id) => {
84                breadcrumbs.extend([segment(self.entry_label(id)), segment("Command")]);
85            }
86        }
87        breadcrumbs
88    }
89
90    /// Observes what one identity denotes, or that the collection no longer holds it.
91    fn entry_label(&self, id: This::Id) -> &str {
92        self.queue_entry(id).map_or("Missing entry", QueueEntryAlg::label)
93    }
94}
95
96/// Names one breadcrumb segment.
97fn segment(label: impl Into<String>) -> Breadcrumb {
98    Breadcrumb { label: label.into() }
99}