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