use std::convert::Infallible;
use color_eyre::eyre;
pub mod cmake;
pub mod doctor;
#[derive(Debug, Clone, thiserror::Error)]
#[error("Unfixable toolchain: {message}\n Suggestion: {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;
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum ToolchainError<Install: Installation> {
#[error("Unfixable toolchain, consider manual intervention")]
Unfixable(#[from] UnfixableToolchain),
#[error("Toolchain is missing, but can be fixed automatically")]
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;
async fn install(&self) -> Result<(), Self::Error> {
unreachable!()
}
}
impl Toolchain for Infallible {
type Installation = Self;
async fn check(&self) -> Result<(), crate::toolchain::ToolchainError<Self::Installation>> {
unreachable!()
}
}
macro_rules! tuples {
($macro:ident) => {
$macro!();
$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(())
}
}
};
}
tuples!(impl_installations);
macro_rules! impl_toolchains {
($($ty:ident),*) => {
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<$($ty: Toolchain),*> Toolchain for ($($ty,)*) {
type Installation = ($($ty::Installation,)*);
async fn check(&self) -> Result<(), ToolchainError<Self::Installation>> {
let ($($ty,)*) = self;
$(
match $ty.check().await {
Ok(()) => {}
Err(e) => {
return Err(match e {
ToolchainError::Unfixable(u) => ToolchainError::Unfixable(u),
ToolchainError::Fixable(_) => ToolchainError::Unfixable(
UnfixableToolchain::new(
format!("One of the toolchains requires fixing"),
"Run the fix command to install missing components",
)
),
});
}
}
)*
Ok(())
}
}
};
}
tuples!(impl_toolchains);