Skip to main content

necessist_core/framework/
union.rs

1use super::{Interface, ToImplementation};
2use crate::LightContext;
3use anyhow::Result;
4use std::marker::PhantomData;
5
6#[cfg(feature = "clap")]
7use clap::{ValueEnum, builder::PossibleValue};
8
9// smoelius: The `IntoEnumIterator` trait and `Iter` enum are currently unused.
10#[allow(dead_code)]
11pub trait IntoEnumIterator: Sized {
12    type Iterator: Iterator<Item = Self>;
13
14    fn iter() -> Self::Iterator;
15}
16
17impl<T: strum::IntoEnumIterator> IntoEnumIterator for T {
18    type Iterator = <Self as strum::IntoEnumIterator>::Iterator;
19
20    fn iter() -> Self::Iterator {
21        <Self as strum::IntoEnumIterator>::iter()
22    }
23}
24
25#[derive(Debug, Clone, Copy, Eq, PartialEq)]
26pub enum Union<L, R> {
27    Left(L),
28    Right(R),
29}
30
31#[allow(dead_code)]
32pub enum Iter<L, R, I, J>
33where
34    L: IntoEnumIterator<Iterator = I>,
35    R: IntoEnumIterator<Iterator = J>,
36{
37    Left(I, PhantomData<L>),
38    Right(J, PhantomData<R>),
39}
40
41impl<L, R, I, J> Iter<L, R, I, J>
42where
43    L: IntoEnumIterator<Iterator = I>,
44    R: IntoEnumIterator<Iterator = J>,
45{
46    fn new() -> Self {
47        Self::Left(L::iter(), PhantomData)
48    }
49}
50
51impl<L, R, I, J> Iterator for Iter<L, R, I, J>
52where
53    L: IntoEnumIterator<Iterator = I>,
54    R: IntoEnumIterator<Iterator = J>,
55    I: Iterator<Item = L>,
56    J: Iterator<Item = R>,
57{
58    type Item = Union<L, R>;
59
60    fn next(&mut self) -> Option<Self::Item> {
61        if let Self::Left(left, _) = self {
62            if let Some(framework) = left.next() {
63                return Some(Union::Left(framework));
64            }
65            *self = Self::Right(R::iter(), PhantomData);
66        }
67        if let Self::Right(right, _) = self {
68            right.next().map(Union::Right)
69        } else {
70            unreachable!()
71        }
72    }
73}
74
75impl<L, R, I, J> IntoEnumIterator for Union<L, R>
76where
77    L: IntoEnumIterator<Iterator = I>,
78    R: IntoEnumIterator<Iterator = J>,
79    I: Iterator<Item = L>,
80    J: Iterator<Item = R>,
81{
82    type Iterator = Iter<L, R, I, J>;
83
84    fn iter() -> Self::Iterator {
85        Iter::new()
86    }
87}
88
89impl<L, R> ToImplementation for Union<L, R>
90where
91    L: ToImplementation,
92    R: ToImplementation,
93{
94    fn to_implementation(&self, context: &LightContext) -> Result<Option<Box<dyn Interface>>> {
95        match self {
96            Self::Left(left) => left.to_implementation(context),
97            Self::Right(right) => right.to_implementation(context),
98        }
99    }
100}
101
102#[cfg(feature = "clap")]
103impl<L, R> ValueEnum for Union<L, R>
104where
105    L: Clone + ValueEnum,
106    R: Clone + ValueEnum,
107{
108    fn value_variants<'a>() -> &'a [Self] {
109        let mut names = L::value_variants()
110            .iter()
111            .filter_map(|left| {
112                left.to_possible_value()
113                    .map(|left| left.get_name().to_owned())
114            })
115            .collect::<Vec<_>>();
116        names.extend(R::value_variants().iter().filter_map(|right| {
117            right
118                .to_possible_value()
119                .map(|right| right.get_name().to_owned())
120        }));
121        names.sort();
122        Box::leak(
123            names
124                .iter()
125                .flat_map(|name| Self::from_str(name, false))
126                .collect::<Vec<_>>()
127                .into_boxed_slice(),
128        )
129    }
130
131    fn to_possible_value(&self) -> Option<PossibleValue> {
132        match self {
133            Self::Left(left) => left.to_possible_value(),
134            Self::Right(right) => right.to_possible_value(),
135        }
136    }
137
138    fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
139        L::from_str(input, ignore_case)
140            .map(Self::Left)
141            .or_else(|left| {
142                R::from_str(input, ignore_case)
143                    .map(Self::Right)
144                    .map_err(|right| format!("{left}, {right}"))
145            })
146    }
147}