necessist_core/framework/
union.rs1use 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#[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}