use super::{Applicable, Interface, ToImplementation, Union};
use crate::LightContext;
use anyhow::{Result, ensure};
use std::fmt::Display;
use strum::IntoEnumIterator;
#[cfg(feature = "clap")]
use clap::{ValueEnum, builder::PossibleValue};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "clap", derive(ValueEnum))]
enum Singleton {
Auto,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Auto<T>(Union<Singleton, T>);
impl<T> Default for Auto<T> {
fn default() -> Self {
Self(Union::Left(Singleton::Auto))
}
}
impl<T> ToImplementation for Auto<T>
where
T: Applicable + Display + IntoEnumIterator + ToImplementation,
{
fn to_implementation(&self, context: &LightContext) -> Result<Option<Box<dyn Interface>>> {
match &self.0 {
Union::Left(_) => {
let unflattened_frameworks = T::iter()
.map(|framework| {
if framework.applicable(context)? {
Ok(Some(framework))
} else {
Ok(None)
}
})
.collect::<Result<Vec<_>>>()?;
let applicable_frameworks = unflattened_frameworks
.into_iter()
.flatten()
.collect::<Vec<_>>();
ensure!(
applicable_frameworks.len() <= 1,
"Found multiple applicable frameworks: {}; please select one with --framework \
<FRAMEWORK>",
applicable_frameworks
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ")
);
if let Some(framework) = applicable_frameworks.into_iter().next() {
framework.to_implementation(context)
} else {
Ok(None)
}
}
Union::Right(framework) => framework.to_implementation(context),
}
}
}
#[cfg(feature = "clap")]
impl<T> ValueEnum for Auto<T>
where
T: Clone + ValueEnum,
{
fn value_variants<'a>() -> &'a [Self] {
Box::leak(
Union::<Singleton, T>::value_variants()
.iter()
.cloned()
.map(Self)
.collect::<Vec<_>>()
.into_boxed_slice(),
)
}
fn to_possible_value(&self) -> Option<PossibleValue> {
self.0.to_possible_value()
}
fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
Union::<Singleton, T>::from_str(input, ignore_case).map(Self)
}
}