2 io
::{BufRead, BufReader, Write}
,
11 view
::{Resizable, ViewWrapper}
,
12 views
::{Dialog, DummyView, LinearLayout, PaddedView, ProgressBar, TextContent, TextView}
,
16 use crate::{abort_install_button, setup::InstallConfig, yes_no_dialog, InstallerState}
;
17 use proxmox_installer_common
::setup
::spawn_low_level_installer
;
19 pub struct InstallProgressView
{
20 view
: PaddedView
<LinearLayout
>,
23 impl InstallProgressView
{
24 pub fn new(siv
: &mut Cursive
) -> Self {
25 let cb_sink
= siv
.cb_sink().clone();
26 let state
= siv
.user_data
::<InstallerState
>().unwrap();
27 let progress_text
= TextContent
::new("starting the installation ..");
30 let progress_text
= progress_text
.clone();
31 let state
= state
.clone();
32 move |counter
: Counter
| Self::progress_task(counter
, cb_sink
, state
, progress_text
)
35 let progress_bar
= ProgressBar
::new().with_task(progress_task
).full_width();
36 let view
= PaddedView
::lrtb(
41 LinearLayout
::vertical()
42 .child(PaddedView
::lrtb(1, 1, 0, 0, progress_bar
))
44 .child(TextView
::new_with_content(progress_text
).center())
45 .child(PaddedView
::lrtb(
50 LinearLayout
::horizontal().child(abort_install_button()),
60 state
: InstallerState
,
61 progress_text
: TextContent
,
63 let mut child
= match spawn_low_level_installer(state
.in_test_mode
) {
66 let _
= cb_sink
.send(Box
::new(move |siv
| {
68 Dialog
::text(err
.to_string())
70 .button("Ok", Cursive
::quit
),
82 .ok_or("failed to get stdin reader")?
;
84 let mut writer
= child
.stdin
.take().ok_or("failed to get stdin writer")?
;
86 serde_json
::to_writer(&mut writer
, &InstallConfig
::from(state
.options
))
87 .map_err(|err
| format
!("failed to serialize install config: {err}"))?
;
88 writeln
!(writer
).map_err(|err
| format
!("failed to write install config: {err}"))?
;
90 let writer
= Arc
::new(Mutex
::new(writer
));
92 for line
in reader
.lines() {
93 let line
= match line
{
95 Err(err
) => return Err(format
!("low-level installer exited early: {err}")),
98 let msg
= match line
.parse
::<UiMessage
>() {
101 // Not a fatal error, so don't abort the installation by returning
102 eprintln
!("low-level installer: {stray}");
107 let result
= match msg
.clone() {
108 UiMessage
::Info(s
) => cb_sink
.send(Box
::new(|siv
| {
109 siv
.add_layer(Dialog
::info(s
).title("Information"));
111 UiMessage
::Error(s
) => cb_sink
.send(Box
::new(|siv
| {
112 siv
.add_layer(Dialog
::info(s
).title("Error"));
114 UiMessage
::Prompt(s
) => cb_sink
.send({
115 let writer
= writer
.clone();
116 Box
::new(move |siv
| Self::show_prompt(siv
, &s
, writer
))
118 UiMessage
::Progress(ratio
, s
) => {
120 progress_text
.set_content(s
);
123 UiMessage
::Finished(success
, msg
) => {
125 progress_text
.set_content(msg
.to_owned());
126 cb_sink
.send(Box
::new(move |siv
| {
127 Self::prepare_for_reboot(siv
, success
, &msg
)
132 if let Err(err
) = result
{
133 eprintln
!("error during message handling: {err}");
134 eprintln
!(" message was: '{msg:?}");
141 if let Err(err
) = inner() {
142 let message
= format
!("installation failed: {err}");
144 .send(Box
::new(|siv
| {
146 Dialog
::text(message
)
148 .button("Exit", Cursive
::quit
),
155 fn prepare_for_reboot(siv
: &mut Cursive
, success
: bool
, msg
: &str) {
156 let title
= if success { "Success" }
else { "Failure" }
;
158 // For rebooting, we just need to quit the installer,
159 // our caller does the actual reboot.
163 .button("Reboot now", Cursive
::quit
),
167 .user_data
::<InstallerState
>()
168 .map(|state
| state
.options
.autoreboot
)
169 .unwrap_or_default();
171 if autoreboot
&& success
{
172 let cb_sink
= siv
.cb_sink();
174 let cb_sink
= cb_sink
.clone();
176 thread
::sleep(Duration
::from_secs(5));
177 let _
= cb_sink
.send(Box
::new(Cursive
::quit
));
183 fn show_prompt
<W
: Write
+ '
static>(siv
: &mut Cursive
, text
: &str, writer
: Arc
<Mutex
<W
>>) {
189 let writer
= writer
.clone();
191 if let Ok(mut writer
) = writer
.lock() {
192 let _
= writeln
!(writer
, "ok");
197 if let Ok(mut writer
) = writer
.lock() {
198 let _
= writeln
!(writer
);
205 impl ViewWrapper
for InstallProgressView
{
206 cursive
::wrap_impl
!(self.view
: PaddedView
<LinearLayout
>);
209 #[derive(Clone, Debug)]
214 Finished(bool
, String
),
215 Progress(usize, String
),
218 impl FromStr
for UiMessage
{
221 fn from_str(s
: &str) -> Result
<Self, Self::Err
> {
222 let (ty
, rest
) = s
.split_once(": ").ok_or("invalid message: no type")?
;
225 "message" => Ok(UiMessage
::Info(rest
.to_owned())),
226 "error" => Ok(UiMessage
::Error(rest
.to_owned())),
227 "prompt" => Ok(UiMessage
::Prompt(rest
.to_owned())),
229 let (state
, rest
) = rest
.split_once(", ").ok_or("invalid message: no state")?
;
230 Ok(UiMessage
::Finished(state
== "ok", rest
.to_owned()))
233 let (percent
, rest
) = rest
.split_once(' '
).ok_or("invalid progress message")?
;
234 Ok(UiMessage
::Progress(
237 .map(|v
| (v
* 100.).floor() as usize)
238 .map_err(|err
| err
.to_string())?
,
242 unknown
=> Err(format
!("invalid message type {unknown}, rest: {rest}")),