use std::convert::Infallible;
use color_eyre::eyre;
pub mod cmake;
pub mod doctor;
pub mod linux;
pub mod meson;
pub mod rust;
pub mod sccache;
pub mod web;
pub mod windows_arm64_llvm;
pub(crate) mod winget;
#[derive(Debug, Clone, thiserror::Error)]
#[error("Unfixable toolchain: {message}\nSuggestion: {suggestion}")]
pub struct UnfixableToolchain {
message: String,
suggestion: String,
}
impl UnfixableToolchain {
pub fn new(message: impl Into<String>, suggestion: impl Into<String>) -> Self {
Self {
message: message.into(),
suggestion: suggestion.into(),
}
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
#[must_use]
pub fn suggestion(&self) -> &str {
&self.suggestion
}
}
pub trait Installation: Send + Sync {
type Error: Into<eyre::Report> + Send;
fn install(&self) -> impl Future<Output = Result<(), Self::Error>> + Send;
}
impl<I: Installation> Installation for Option<I> {
type Error = eyre::Report;
async fn install(&self) -> Result<(), Self::Error> {
if let Some(install) = self {
install.install().await.map_err(Into::into)?;
}
Ok(())
}
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum ToolchainError<Install: Installation> {
#[error("{0}")]
Unfixable(#[from] UnfixableToolchain),
#[error(
"Toolchain is missing components that can be fixed automatically. Run `water doctor --fix` for details."
)]
Fixable(Install),
}
impl<I: Installation> ToolchainError<I> {
#[must_use]
pub const fn is_fixable(&self) -> bool {
matches!(self, Self::Fixable(_))
}
#[must_use]
pub const fn fixable(install: I) -> Self {
Self::Fixable(install)
}
#[must_use]
pub fn unfixable(message: impl Into<String>, suggestion: impl Into<String>) -> Self {
Self::Unfixable(UnfixableToolchain::new(message, suggestion))
}
}
pub trait Toolchain: Send + Sync {
type Installation: Installation;
fn check(&self) -> impl Future<Output = Result<(), ToolchainError<Self::Installation>>> + Send;
}
impl Installation for Infallible {
type Error = Self;
fn install(&self) -> impl Future<Output = Result<(), Self::Error>> + Send {
std::future::poll_fn(|_| {
unreachable!("an Infallible installation plan cannot be constructed")
})
}
}
macro_rules! tuples {
($macro:ident) => {
$macro!(T0);
$macro!(T0, T1);
$macro!(T0, T1, T2);
$macro!(T0, T1, T2, T3);
$macro!(T0, T1, T2, T3, T4);
$macro!(T0, T1, T2, T3, T4, T5);
$macro!(T0, T1, T2, T3, T4, T5, T6);
$macro!(T0, T1, T2, T3, T4, T5, T6, T7);
$macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
$macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
$macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
$macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
$macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
$macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
$macro!(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14
);
};
}
macro_rules! impl_installations {
($($ty:ident),*) => {
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<$($ty: Installation),*> Installation for ($($ty,)*) {
type Error = eyre::Report;
async fn install(&self) -> Result<(), Self::Error> {
let ($($ty,)*) = self;
$(
$ty.install().await.map_err(|e| e.into())?;
)*
Ok(())
}
}
};
}
impl Installation for () {
type Error = eyre::Report;
fn install(&self) -> impl Future<Output = Result<(), Self::Error>> + Send {
std::future::ready(Ok(()))
}
}
tuples!(impl_installations);
macro_rules! impl_toolchains {
($(($idx:tt, $ty:ident)),*) => {
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<$($ty: Toolchain),*> Toolchain for ($($ty,)*) {
type Installation = ($(Option<$ty::Installation>,)*);
async fn check(&self) -> Result<(), ToolchainError<Self::Installation>> {
#[allow(unused_mut)]
let mut any_fixable = false;
#[allow(unused_mut)]
let mut installs: Self::Installation = ($(None::<$ty::Installation>,)*);
$(
match self.$idx.check().await {
Ok(()) => {}
Err(ToolchainError::Unfixable(u)) => {
return Err(ToolchainError::Unfixable(u));
}
Err(ToolchainError::Fixable(install)) => {
any_fixable = true;
installs.$idx = Some(install);
}
}
)*
if any_fixable {
Err(ToolchainError::Fixable(installs))
} else {
Ok(())
}
}
}
};
}
impl Toolchain for () {
type Installation = ();
fn check(&self) -> impl Future<Output = Result<(), ToolchainError<Self::Installation>>> + Send {
std::future::ready(Ok(()))
}
}
macro_rules! tuples_idx {
($macro:ident) => {
$macro!((0, T0));
$macro!((0, T0), (1, T1));
$macro!((0, T0), (1, T1), (2, T2));
$macro!((0, T0), (1, T1), (2, T2), (3, T3));
$macro!((0, T0), (1, T1), (2, T2), (3, T3), (4, T4));
$macro!((0, T0), (1, T1), (2, T2), (3, T3), (4, T4), (5, T5));
$macro!(
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6)
);
$macro!(
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7)
);
$macro!(
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8)
);
$macro!(
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8),
(9, T9)
);
$macro!(
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8),
(9, T9),
(10, T10)
);
$macro!(
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8),
(9, T9),
(10, T10),
(11, T11)
);
$macro!(
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8),
(9, T9),
(10, T10),
(11, T11),
(12, T12)
);
$macro!(
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8),
(9, T9),
(10, T10),
(11, T11),
(12, T12),
(13, T13)
);
$macro!(
(0, T0),
(1, T1),
(2, T2),
(3, T3),
(4, T4),
(5, T5),
(6, T6),
(7, T7),
(8, T8),
(9, T9),
(10, T10),
(11, T11),
(12, T12),
(13, T13),
(14, T14)
);
};
}
tuples_idx!(impl_toolchains);