iced_devtools/
comet.rs

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}