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