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