necessist_core/framework/
auto.rs1use 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}