1use std::convert::Infallible;
4
5pub mod cargo_helpers;
6pub mod cmake;
7pub mod doctor;
8pub mod host;
9pub mod linux;
10pub mod meson;
11pub mod rust;
12pub mod sccache;
13#[cfg(test)]
14pub(crate) mod testing;
15pub mod web;
16pub mod windows_arm64_llvm;
17pub(crate) mod winget;
18
19pub use host::Host;
20#[derive(Debug, Clone, thiserror::Error)]
22#[error("Unfixable toolchain: {message}\nSuggestion: {suggestion}")]
23pub struct UnfixableToolchain {
24 message: String,
26 suggestion: String,
28}
29
30impl UnfixableToolchain {
31 pub fn new(message: impl Into<String>, suggestion: impl Into<String>) -> Self {
33 Self {
34 message: message.into(),
35 suggestion: suggestion.into(),
36 }
37 }
38
39 #[must_use]
41 pub fn message(&self) -> &str {
42 &self.message
43 }
44
45 #[must_use]
47 pub fn suggestion(&self) -> &str {
48 &self.suggestion
49 }
50}
51
52pub trait Installation: Send + Sync {
54 type Error: Into<eyre::Report> + Send;
56 fn install(&self, host: &Host) -> impl Future<Output = Result<(), Self::Error>> + Send;
58}
59
60impl<I: Installation> Installation for Option<I> {
64 type Error = eyre::Report;
65
66 async fn install(&self, host: &Host) -> Result<(), Self::Error> {
67 if let Some(install) = self {
68 install.install(host).await.map_err(Into::into)?;
69 }
70 Ok(())
71 }
72}
73
74#[derive(Debug, Clone, thiserror::Error)]
76pub enum ToolchainError<Install: Installation> {
77 #[error("{0}")]
79 Unfixable(#[from] UnfixableToolchain),
80 #[error(
82 "Toolchain is missing components that can be fixed automatically. Run `water doctor --fix` for details."
83 )]
84 Fixable(Install),
85}
86
87impl<I: Installation> ToolchainError<I> {
88 #[must_use]
90 pub const fn is_fixable(&self) -> bool {
91 matches!(self, Self::Fixable(_))
92 }
93
94 #[must_use]
96 pub const fn fixable(install: I) -> Self {
97 Self::Fixable(install)
98 }
99
100 #[must_use]
102 pub fn unfixable(message: impl Into<String>, suggestion: impl Into<String>) -> Self {
103 Self::Unfixable(UnfixableToolchain::new(message, suggestion))
104 }
105}
106
107pub trait Toolchain: Send + Sync {
115 type Installation: Installation;
117
118 fn check(
123 &self,
124 host: &Host,
125 ) -> impl Future<Output = Result<(), ToolchainError<Self::Installation>>> + Send;
126}
127
128impl Installation for Infallible {
129 type Error = Self;
130
131 fn install(&self, _host: &Host) -> impl Future<Output = Result<(), Self::Error>> + Send {
132 std::future::poll_fn(|_| {
133 unreachable!("an Infallible installation plan cannot be constructed")
134 })
135 }
136}
137
138macro_rules! tuples {
139 ($macro:ident) => {
140 $macro!(T0);
141 $macro!(T0, T1);
142 $macro!(T0, T1, T2);
143 $macro!(T0, T1, T2, T3);
144 $macro!(T0, T1, T2, T3, T4);
145 $macro!(T0, T1, T2, T3, T4, T5);
146 $macro!(T0, T1, T2, T3, T4, T5, T6);
147 $macro!(T0, T1, T2, T3, T4, T5, T6, T7);
148 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
149 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
150 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
151 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
152 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
153 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
154 $macro!(
155 T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14
156 );
157 };
158}
159
160macro_rules! impl_installations {
161 ($($ty:ident),*) => {
162 #[allow(unused_variables)]
163 #[allow(non_snake_case)]
164 impl<$($ty: Installation),*> Installation for ($($ty,)*) {
165 type Error = eyre::Report;
166 async fn install(&self, host: &Host) -> Result<(), Self::Error> {
167 let ($($ty,)*) = self;
168 $(
169 $ty.install(host).await.map_err(|e| e.into())?;
170 )*
171 Ok(())
172 }
173 }
174 };
175}
176
177impl Installation for () {
178 type Error = eyre::Report;
179
180 fn install(&self, _host: &Host) -> impl Future<Output = Result<(), Self::Error>> + Send {
181 std::future::ready(Ok(()))
182 }
183}
184
185tuples!(impl_installations);
186
187macro_rules! impl_toolchains {
188 ($(($idx:tt, $ty:ident)),*) => {
189 #[allow(unused_variables)]
190 #[allow(non_snake_case)]
191 impl<$($ty: Toolchain),*> Toolchain for ($($ty,)*) {
192 type Installation = ($(Option<$ty::Installation>,)*);
195
196 async fn check(&self, host: &Host) -> Result<(), ToolchainError<Self::Installation>> {
197 #[allow(unused_mut)]
198 let mut any_fixable = false;
199 #[allow(unused_mut)]
200 let mut installs: Self::Installation = ($(None::<$ty::Installation>,)*);
201
202 $(
203 match self.$idx.check(host).await {
204 Ok(()) => {}
205 Err(ToolchainError::Unfixable(u)) => {
206 return Err(ToolchainError::Unfixable(u));
207 }
208 Err(ToolchainError::Fixable(install)) => {
209 any_fixable = true;
210 installs.$idx = Some(install);
211 }
212 }
213 )*
214
215 if any_fixable {
216 Err(ToolchainError::Fixable(installs))
217 } else {
218 Ok(())
219 }
220 }
221 }
222 };
223}
224
225impl Toolchain for () {
226 type Installation = ();
227
228 fn check(
229 &self,
230 _host: &Host,
231 ) -> impl Future<Output = Result<(), ToolchainError<Self::Installation>>> + Send {
232 std::future::ready(Ok(()))
233 }
234}
235
236macro_rules! tuples_idx {
237 ($macro:ident) => {
238 $macro!((0, T0));
239 $macro!((0, T0), (1, T1));
240 $macro!((0, T0), (1, T1), (2, T2));
241 $macro!((0, T0), (1, T1), (2, T2), (3, T3));
242 $macro!((0, T0), (1, T1), (2, T2), (3, T3), (4, T4));
243 $macro!((0, T0), (1, T1), (2, T2), (3, T3), (4, T4), (5, T5));
244 $macro!(
245 (0, T0),
246 (1, T1),
247 (2, T2),
248 (3, T3),
249 (4, T4),
250 (5, T5),
251 (6, T6)
252 );
253 $macro!(
254 (0, T0),
255 (1, T1),
256 (2, T2),
257 (3, T3),
258 (4, T4),
259 (5, T5),
260 (6, T6),
261 (7, T7)
262 );
263 $macro!(
264 (0, T0),
265 (1, T1),
266 (2, T2),
267 (3, T3),
268 (4, T4),
269 (5, T5),
270 (6, T6),
271 (7, T7),
272 (8, T8)
273 );
274 $macro!(
275 (0, T0),
276 (1, T1),
277 (2, T2),
278 (3, T3),
279 (4, T4),
280 (5, T5),
281 (6, T6),
282 (7, T7),
283 (8, T8),
284 (9, T9)
285 );
286 $macro!(
287 (0, T0),
288 (1, T1),
289 (2, T2),
290 (3, T3),
291 (4, T4),
292 (5, T5),
293 (6, T6),
294 (7, T7),
295 (8, T8),
296 (9, T9),
297 (10, T10)
298 );
299 $macro!(
300 (0, T0),
301 (1, T1),
302 (2, T2),
303 (3, T3),
304 (4, T4),
305 (5, T5),
306 (6, T6),
307 (7, T7),
308 (8, T8),
309 (9, T9),
310 (10, T10),
311 (11, T11)
312 );
313 $macro!(
314 (0, T0),
315 (1, T1),
316 (2, T2),
317 (3, T3),
318 (4, T4),
319 (5, T5),
320 (6, T6),
321 (7, T7),
322 (8, T8),
323 (9, T9),
324 (10, T10),
325 (11, T11),
326 (12, T12)
327 );
328 $macro!(
329 (0, T0),
330 (1, T1),
331 (2, T2),
332 (3, T3),
333 (4, T4),
334 (5, T5),
335 (6, T6),
336 (7, T7),
337 (8, T8),
338 (9, T9),
339 (10, T10),
340 (11, T11),
341 (12, T12),
342 (13, T13)
343 );
344 $macro!(
345 (0, T0),
346 (1, T1),
347 (2, T2),
348 (3, T3),
349 (4, T4),
350 (5, T5),
351 (6, T6),
352 (7, T7),
353 (8, T8),
354 (9, T9),
355 (10, T10),
356 (11, T11),
357 (12, T12),
358 (13, T13),
359 (14, T14)
360 );
361 };
362}
363
364tuples_idx!(impl_toolchains);