1use crate::runtime::task::{self, Task};
2
3use std::process;
4
5pub const COMPATIBLE_REVISION: &str = "3f75f3240edc1719df584810337bc7df010327d8";
6
7pub fn launch() -> Task<launch::Result> {
8 task::try_blocking(|mut sender| {
9 let cargo_install = process::Command::new("cargo")
10 .args(["install", "--list"])
11 .output()?;
12
13 let installed_packages = String::from_utf8_lossy(&cargo_install.stdout);
14
15 for line in installed_packages.lines() {
16 if !line.starts_with("iced_comet ") {
17 continue;
18 }
19
20 let Some((_, revision)) = line.rsplit_once("?rev=") else {
21 return Err(launch::Error::Outdated { revision: None });
22 };
23
24 let Some((revision, _)) = revision.rsplit_once("#") else {
25 return Err(launch::Error::Outdated { revision: None });
26 };
27
28 if revision != COMPATIBLE_REVISION {
29 return Err(launch::Error::Outdated {
30 revision: Some(revision.to_owned()),
31 });
32 }
33
34 let _ = process::Command::new("iced_comet")
35 .stdin(process::Stdio::null())
36 .stdout(process::Stdio::null())
37 .stderr(process::Stdio::null())
38 .spawn()?;
39
40 let _ = sender.try_send(());
41 return Ok(());
42 }
43
44 Err(launch::Error::NotFound)
45 })
46}
47
48pub fn install() -> Task<install::Result> {
49 task::try_blocking(|mut sender| {
50 use std::io::{BufRead, BufReader};
51 use std::process::{Command, Stdio};
52
53 let mut install = Command::new("cargo")
54 .args([
55 "install",
56 "--locked",
57 "--git",
58 "https://github.com/iced-rs/comet.git",
59 "--rev",
60 COMPATIBLE_REVISION,
61 ])
62 .stdin(Stdio::null())
63 .stdout(Stdio::null())
64 .stderr(Stdio::piped())
65 .spawn()?;
66
67 let mut stderr = BufReader::new(install.stderr.take().expect("stderr must be piped"));
68
69 let mut log = String::new();
70
71 while let Ok(n) = stderr.read_line(&mut log) {
72 if n == 0 {
73 let status = install.wait()?;
74
75 if status.success() {
76 break;
77 } else {
78 return Err(install::Error::ProcessFailed(status));
79 }
80 }
81
82 let _ = sender.try_send(install::Event::Logged(log.trim_end().to_owned()));
83
84 log.clear();
85 }
86
87 let _ = sender.try_send(install::Event::Finished);
88
89 Ok(())
90 })
91}
92
93pub mod launch {
94 use std::io;
95 use std::sync::Arc;
96
97 pub type Result = std::result::Result<(), Error>;
98
99 #[derive(Debug, Clone)]
100 pub enum Error {
101 NotFound,
102 Outdated { revision: Option<String> },
103 IoFailed(Arc<io::Error>),
104 }
105
106 impl From<io::Error> for Error {
107 fn from(error: io::Error) -> Self {
108 Self::IoFailed(Arc::new(error))
109 }
110 }
111}
112
113pub mod install {
114 use std::io;
115 use std::process;
116 use std::sync::Arc;
117
118 pub type Result = std::result::Result<Event, Error>;
119
120 #[derive(Debug, Clone)]
121 pub enum Event {
122 Logged(String),
123 Finished,
124 }
125
126 #[derive(Debug, Clone)]
127 pub enum Error {
128 ProcessFailed(process::ExitStatus),
129 IoFailed(Arc<io::Error>),
130 }
131
132 impl From<io::Error> for Error {
133 fn from(error: io::Error) -> Self {
134 Self::IoFailed(Arc::new(error))
135 }
136 }
137}