iced_devtools/
comet.rs

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