Skip to main content

necessist_core/framework/
auto.rs

1use super::{Applicable, Interface, ToImplementation, Union};
2use crate::LightContext;
3use anyhow::{Result, ensure};
4use std::fmt::Display;
5use strum::IntoEnumIterator;
6
7#[cfg(feature = "clap")]
8use clap::{ValueEnum, builder::PossibleValue};
9
10#[derive(Debug, Clone, Copy, Eq, PartialEq)]
11#[cfg_attr(feature = "clap", derive(ValueEnum))]
12enum Singleton {
13    Auto,
14}
15
16#[derive(Debug, Clone, Copy, Eq, PartialEq)]
17pub struct Auto<T>(Union<Singleton, T>);
18
19impl<T> Default for Auto<T> {
20    fn default() -> Self {
21        Self(Union::Left(Singleton::Auto))
22    }
23}
24
25impl<T> ToImplementation for Auto<T>
26where
27    T: Applicable + Display + IntoEnumIterator + ToImplementation,
28{
29    fn to_implementation(&self, context: &LightContext) -> Result<Option<Box<dyn Interface>>> {
30        match &self.0 {
31            Union::Left(_) => {
32                let unflattened_frameworks = T::iter()
33                    .map(|framework| {
34                        if framework.applicable(context)? {
35                            Ok(Some(framework))
36                        } else {
37                            Ok(None)
38                        }
39                    })
40                    .collect::<Result<Vec<_>>>()?;
41
42                let applicable_frameworks = unflattened_frameworks
43                    .into_iter()
44                    .flatten()
45                    .collect::<Vec<_>>();
46
47                ensure!(
48                    applicable_frameworks.len() <= 1,
49                    "Found multiple applicable frameworks: {}; please select one with --framework \
50                     <FRAMEWORK>",
51                    applicable_frameworks
52                        .iter()
53                        .map(ToString::to_string)
54                        .collect::<Vec<_>>()
55                        .join(", ")
56                );
57
58                if let Some(framework) = applicable_frameworks.into_iter().next() {
59                    framework.to_implementation(context)
60                } else {
61                    Ok(None)
62                }
63            }
64            Union::Right(framework) => framework.to_implementation(context),
65        }
66    }
67}
68
69#[cfg(feature = "clap")]
70impl<T> ValueEnum for Auto<T>
71where
72    T: Clone + ValueEnum,
73{
74    fn value_variants<'a>() -> &'a [Self] {
75        Box::leak(
76            Union::<Singleton, T>::value_variants()
77                .iter()
78                .cloned()
79                .map(Self)
80                .collect::<Vec<_>>()
81                .into_boxed_slice(),
82        )
83    }
84
85    fn to_possible_value(&self) -> Option<PossibleValue> {
86        self.0.to_possible_value()
87    }
88
89    fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
90        Union::<Singleton, T>::from_str(input, ignore_case).map(Self)
91    }
92}