Coverage Report

Created: 2026-09-05 23:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
super-stt-app/src/core/app/handlers/models_page/install.rs
Line
Count
Source
1
// SPDX-License-Identifier: GPL-3.0-only
2
3
use crate::core::app::AppModel;
4
use crate::ui::messages::{BackendMessage, Message, ModelsPageMessage};
5
use cosmic::prelude::*;
6
7
impl AppModel {
8
0
    pub(in crate::core::app) fn handle_models_install_lifecycle(
9
0
        &mut self,
10
0
        message: ModelsPageMessage,
11
0
    ) -> Task<cosmic::Action<Message>> {
12
0
        match message {
13
            // Registry / Download-tab messages
14
0
            ModelsPageMessage::InstallBackend(source) => {
15
                // Clear a prior "failed to start" so the card reads "Installing…".
16
0
                self.registry.install_errors.remove(&source);
17
0
                let s = source.clone();
18
0
                Task::perform(
19
0
                    async move { crate::daemon::registry::install_by_source(&s).await },
20
0
                    move |res| {
21
0
                        cosmic::Action::App(Message::ModelsPage(match res {
22
0
                            Ok(a) => ModelsPageMessage::InstallAccepted {
23
0
                                source: source.clone(),
24
0
                                install_id: a.install_id,
25
0
                            },
26
0
                            Err(e) => ModelsPageMessage::InstallFailedToStart {
27
0
                                source: source.clone(),
28
0
                                error: e.to_string(),
29
0
                            },
30
                        }))
31
0
                    },
32
                )
33
            }
34
35
0
            ModelsPageMessage::InstallBackendFromRepoUrl(url) => {
36
0
                self.registry.install_errors.remove(&url);
37
0
                let u = url.clone();
38
0
                Task::perform(
39
0
                    async move { crate::daemon::registry::install_by_repo_url(&u).await },
40
0
                    move |res| {
41
0
                        cosmic::Action::App(Message::ModelsPage(match res {
42
0
                            Ok(a) => ModelsPageMessage::InstallAccepted {
43
0
                                source: url.clone(),
44
0
                                install_id: a.install_id,
45
0
                            },
46
0
                            Err(e) => ModelsPageMessage::InstallFailedToStart {
47
0
                                source: url.clone(),
48
0
                                error: e.to_string(),
49
0
                            },
50
                        }))
51
0
                    },
52
                )
53
            }
54
55
0
            ModelsPageMessage::InstallAccepted { source, install_id } => {
56
                use crate::state::registry::InstallStatus;
57
                use super_stt_shared::registry::events::InstallPhase;
58
                // The request was accepted — retire any prior start error.
59
0
                self.registry.install_errors.remove(&source);
60
0
                self.registry.installs.insert(
61
0
                    source,
62
0
                    InstallStatus {
63
0
                        install_id,
64
0
                        phase: InstallPhase::Downloading,
65
0
                        bytes_done: 0,
66
0
                        bytes_total: None,
67
0
                        error: None,
68
0
                    },
69
                );
70
0
                Task::none()
71
            }
72
73
0
            ModelsPageMessage::InstallFailedToStart { source, error } => {
74
0
                log::error!("install({source}) failed to start: {error}");
75
                // Drop the pending marker (there is no background install) and
76
                // record the reason so the Browse card shows "Failed" + a note
77
                // instead of silently snapping back to the Install button.
78
0
                self.registry.installs.remove(&source);
79
0
                self.registry.install_errors.insert(source, error);
80
0
                Task::none()
81
            }
82
83
0
            ModelsPageMessage::UpdateBackend(source) => {
84
0
                self.registry.install_errors.remove(&source);
85
0
                let s = source.clone();
86
0
                Task::perform(
87
0
                    async move { crate::daemon::registry::update(&s).await },
88
0
                    move |res| {
89
0
                        cosmic::Action::App(match res {
90
0
                            Ok(r) if r.noop => {
91
0
                                log::info!("update({source}) noop — already at {}", r.to_version);
92
                                // Nothing to install, but something to correct:
93
                                // the only way to reach this is an Update entry
94
                                // drawn from a stale `installed_version`. Reload
95
                                // the catalog that decides it, or the entry
96
                                // stays and every click no-ops again.
97
0
                                Message::ModelsPage(ModelsPageMessage::InstallCompleted { source })
98
                            }
99
0
                            Ok(r) => Message::ModelsPage(ModelsPageMessage::InstallAccepted {
100
0
                                source: source.clone(),
101
0
                                install_id: r.install_id.unwrap_or_default(),
102
0
                            }),
103
0
                            Err(e) => {
104
0
                                Message::ModelsPage(ModelsPageMessage::InstallFailedToStart {
105
0
                                    source: source.clone(),
106
0
                                    error: e.to_string(),
107
0
                                })
108
                            }
109
                        })
110
0
                    },
111
                )
112
            }
113
114
0
            _ => Task::none(),
115
        }
116
0
    }
117
118
    /// Uninstall lifecycle: kick off the request and surface its failure.
119
0
    pub(in crate::core::app) fn handle_models_uninstall(
120
0
        &mut self,
121
0
        message: ModelsPageMessage,
122
0
    ) -> Task<cosmic::Action<Message>> {
123
0
        match message {
124
0
            ModelsPageMessage::UninstallBackend(source) => {
125
                // Clear any stale failure so the row reads "in progress" on retry.
126
0
                self.registry.uninstall_errors.remove(&source);
127
0
                let s = source.clone();
128
0
                Task::perform(
129
0
                    async move { crate::daemon::registry::uninstall(&s).await },
130
0
                    move |res| {
131
0
                        cosmic::Action::App(match res {
132
0
                            Ok(_) => Message::Backend(BackendMessage::BackendsReload),
133
0
                            Err(error) => Message::ModelsPage(ModelsPageMessage::UninstallFailed {
134
0
                                source: source.clone(),
135
0
                                error: error.to_string(),
136
0
                            }),
137
                        })
138
0
                    },
139
                )
140
            }
141
142
0
            ModelsPageMessage::UninstallFailed { source, error } => {
143
0
                log::error!("uninstall({source}) failed: {error}");
144
0
                self.registry.uninstall_errors.insert(source, error);
145
0
                Task::none()
146
            }
147
148
0
            _ => Task::none(),
149
        }
150
0
    }
151
152
0
    pub(in crate::core::app) fn handle_models_install_progress(
153
0
        &mut self,
154
0
        message: ModelsPageMessage,
155
0
    ) -> Task<cosmic::Action<Message>> {
156
0
        match message {
157
            ModelsPageMessage::InstallProgress {
158
0
                install_id,
159
0
                source,
160
0
                phase,
161
0
                bytes_done,
162
0
                bytes_total,
163
            } => {
164
0
                if let Some(s) = self.registry.installs.get_mut(&source)
165
0
                    && s.install_id == install_id
166
                {
167
0
                    s.phase = phase;
168
0
                    if let Some(d) = bytes_done {
169
0
                        s.bytes_done = d;
170
0
                    }
171
0
                    if let Some(t) = bytes_total {
172
0
                        s.bytes_total = Some(t);
173
0
                    }
174
0
                }
175
0
                Task::none()
176
            }
177
178
0
            ModelsPageMessage::InstallCompleted { source } => {
179
0
                self.registry.installs.remove(&source);
180
0
                self.registry.install_errors.remove(&source);
181
                // Both catalogs, because they carry different halves of what
182
                // just changed. The backends list is what the Installed tab
183
                // draws; the registry catalog carries `installed_version`, and
184
                // that is what decides whether an Update entry is offered. Left
185
                // stale, the Update the user just ran stays on the menu and
186
                // does nothing when clicked. No index refresh: the annotation
187
                // is computed from local install state, so the cached index is
188
                // enough and a network round-trip would only slow this down.
189
0
                crate::core::app::handlers::tasks::reload_backend_catalogs()
190
            }
191
192
            ModelsPageMessage::InstallFailed {
193
0
                install_id,
194
0
                source,
195
0
                phase,
196
0
                error,
197
            } => {
198
0
                if let Some(s) = self.registry.installs.get_mut(&source)
199
0
                    && s.install_id == install_id
200
0
                {
201
0
                    s.error = Some(error);
202
0
                    s.phase = phase;
203
0
                }
204
0
                Task::none()
205
            }
206
207
0
            _ => Task::none(),
208
        }
209
0
    }
210
}