1use std::convert::Infallible;
4
5use color_eyre::eyre;
6
7pub mod cmake;
8pub mod doctor;
9#[derive(Debug, Clone, thiserror::Error)]
11#[error("Unfixable toolchain: {message}\n Suggestion: {suggestion}")]
12pub struct UnfixableToolchain {
13 message: String,
15 suggestion: String,
17}
18
19impl UnfixableToolchain {
20 pub fn new(message: impl Into<String>, suggestion: impl Into<String>) -> Self {
22 Self {
23 message: message.into(),
24 suggestion: suggestion.into(),
25 }
26 }
27
28 #[must_use]
30 pub fn message(&self) -> &str {
31 &self.message
32 }
33
34 #[must_use]
36 pub fn suggestion(&self) -> &str {
37 &self.suggestion
38 }
39}
40
41pub trait Installation: Send + Sync {
43 type Error: Into<eyre::Report> + Send;
45 fn install(&self) -> impl Future<Output = Result<(), Self::Error>> + Send;
47}
48
49#[derive(Debug, Clone, thiserror::Error)]
51pub enum ToolchainError<Install: Installation> {
52 #[error("Unfixable toolchain, consider manual intervention")]
54 Unfixable(#[from] UnfixableToolchain),
55 #[error("Toolchain is missing, but can be fixed automatically")]
57 Fixable(Install),
58}
59
60impl<I: Installation> ToolchainError<I> {
61 #[must_use]
63 pub const fn is_fixable(&self) -> bool {
64 matches!(self, Self::Fixable(_))
65 }
66
67 #[must_use]
69 pub const fn fixable(install: I) -> Self {
70 Self::Fixable(install)
71 }
72
73 #[must_use]
75 pub fn unfixable(message: impl Into<String>, suggestion: impl Into<String>) -> Self {
76 Self::Unfixable(UnfixableToolchain::new(message, suggestion))
77 }
78}
79
80pub trait Toolchain: Send + Sync {
88 type Installation: Installation;
90
91 fn check(&self) -> impl Future<Output = Result<(), ToolchainError<Self::Installation>>> + Send;
96}
97
98impl Installation for Infallible {
99 type Error = Self;
100
101 async fn install(&self) -> Result<(), Self::Error> {
102 unreachable!()
103 }
104}
105
106impl Toolchain for Infallible {
107 type Installation = Self;
108
109 async fn check(&self) -> Result<(), crate::toolchain::ToolchainError<Self::Installation>> {
110 unreachable!()
111 }
112}
113
114macro_rules! tuples {
115 ($macro:ident) => {
116 $macro!();
117 $macro!(T0);
118 $macro!(T0, T1);
119 $macro!(T0, T1, T2);
120 $macro!(T0, T1, T2, T3);
121 $macro!(T0, T1, T2, T3, T4);
122 $macro!(T0, T1, T2, T3, T4, T5);
123 $macro!(T0, T1, T2, T3, T4, T5, T6);
124 $macro!(T0, T1, T2, T3, T4, T5, T6, T7);
125 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
126 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
127 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
128 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
129 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
130 $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
131 $macro!(
132 T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14
133 );
134 };
135}
136
137macro_rules! impl_installations {
138 ($($ty:ident),*) => {
139 #[allow(unused_variables)]
140 #[allow(non_snake_case)]
141 impl<$($ty: Installation),*> Installation for ($($ty,)*) {
142 type Error = eyre::Report;
143 async fn install(&self) -> Result<(), Self::Error> {
144 let ($($ty,)*) = self;
145 $(
146 $ty.install().await.map_err(|e| e.into())?;
147 )*
148 Ok(())
149 }
150 }
151 };
152}
153
154tuples!(impl_installations);
155
156macro_rules! impl_toolchains {
157 ($($ty:ident),*) => {
158 #[allow(unused_variables)]
159 #[allow(non_snake_case)]
160 impl<$($ty: Toolchain),*> Toolchain for ($($ty,)*) {
161 type Installation = ($($ty::Installation,)*);
162
163 async fn check(&self) -> Result<(), ToolchainError<Self::Installation>> {
164 let ($($ty,)*) = self;
165 $(
166 match $ty.check().await {
167 Ok(()) => {}
168 Err(e) => {
169 return Err(match e {
170 ToolchainError::Unfixable(u) => ToolchainError::Unfixable(u),
171 ToolchainError::Fixable(_) => ToolchainError::Unfixable(
172 UnfixableToolchain::new(
173 format!("One of the toolchains requires fixing"),
174 "Run the fix command to install missing components",
175 )
176 ),
177 });
178 }
179 }
180 )*
181 Ok(())
182 }
183 }
184 };
185}
186
187tuples!(impl_toolchains);