1use std::collections::HashMap ;
4use std::hash::Hash ;
5
6use nonempty_collections::{ NEMap, NonEmptyIterator, IntoNonEmptyIterator };
7use wasmtime::component::Val ;
8
9
10
11pub trait Cardinality<Id, T>: Sized {
16 type Rebind<U>;
18
19 fn map<N>( &self, map: impl FnMut( &Id, &T ) -> N ) -> Self::Rebind<N>
21 where
22 Id: Clone;
23
24 fn map_mut<N>( self, map: impl FnMut( T ) -> N ) -> Self::Rebind<N> ;
26
27 fn get( &self, id: &Id ) -> Option<&T>
29 where
30 Id: Hash + Eq ;
31}
32
33#[derive( Debug, Clone )]
35pub struct ExactlyOne<Id, T>(
36 pub Id,
38 pub T
40);
41
42#[derive( Debug, Clone )]
44pub struct AtMostOne<Id, T>(
45 pub Option<( Id, T )>
47);
48
49#[derive( Debug, Clone )]
51pub struct AtLeastOne<Id, T>(
52 pub NEMap<Id, T>
54);
55
56#[derive( Debug, Clone )]
58pub struct Any<Id, T>(
59 pub HashMap<Id, T>
61);
62
63impl<Id, T> Cardinality<Id, T> for ExactlyOne<Id, T> {
64 type Rebind<U> = ExactlyOne<Id, U>;
65
66 fn map<N>( &self, mut map: impl FnMut( &Id, &T ) -> N ) -> Self::Rebind<N>
67 where
68 Id: Clone,
69 {
70 ExactlyOne( self.0.clone(), map( &self.0, &self.1 ))
71 }
72
73 fn map_mut<N>( self, mut map: impl FnMut( T ) -> N ) -> Self::Rebind<N> {
74 ExactlyOne( self.0, map( self.1 ))
75 }
76
77 fn get( &self, id: &Id ) -> Option<&T>
78 where
79 Id: Hash + Eq,
80 {
81 debug_assert!( &self.0 == id, "singleton cardinality id mismatch" );
84 Some( &self.1 )
85 }
86}
87
88impl<Id, T> Cardinality<Id, T> for AtMostOne<Id, T> {
89 type Rebind<U> = AtMostOne<Id, U>;
90
91 fn map<N>( &self, mut map: impl FnMut( &Id, &T ) -> N ) -> Self::Rebind<N>
92 where
93 Id: Clone,
94 {
95 match &self.0 {
96 None => AtMostOne( None ),
97 Some(( id, value )) => AtMostOne( Some(( id.clone(), map( id, value )))),
98 }
99 }
100
101 fn map_mut<N>( self, mut map: impl FnMut( T ) -> N ) -> Self::Rebind<N> {
102 match self.0 {
103 None => AtMostOne( None ),
104 Some(( id, value )) => AtMostOne( Some(( id, map( value )))),
105 }
106 }
107
108 fn get( &self, id: &Id ) -> Option<&T>
109 where
110 Id: Hash + Eq,
111 {
112 match self.0.as_ref() {
113 None => None,
114 Some(( stored_id, value )) => {
115 debug_assert!( stored_id == id, "singleton cardinality id mismatch" );
118 Some( value )
119 }
120 }
121 }
122}
123
124impl<Id: Hash + Eq, T> Cardinality<Id, T> for AtLeastOne<Id, T> {
125 type Rebind<U> = AtLeastOne<Id, U>;
126
127 fn map<N>( &self, mut map: impl FnMut( &Id, &T ) -> N ) -> Self::Rebind<N>
128 where
129 Id: Clone,
130 {
131 AtLeastOne(
132 self.0.nonempty_iter()
133 .map(|( id, value )| ( id.clone(), map( id, value )))
134 .collect()
135 )
136 }
137
138 fn map_mut<N>( self, mut map: impl FnMut( T ) -> N ) -> Self::Rebind<N> {
139 AtLeastOne(
140 self.0.into_nonempty_iter()
141 .map(|( id, value )| ( id, map( value )))
142 .collect()
143 )
144 }
145
146 fn get( &self, id: &Id ) -> Option<&T>
147 where
148 Id: Hash + Eq,
149 {
150 self.0.get( id )
151 }
152}
153
154impl<Id: Hash + Eq, T> Cardinality<Id, T> for Any<Id, T> {
155 type Rebind<U> = Any<Id, U>;
156
157 fn map<N>( &self, mut map: impl FnMut( &Id, &T ) -> N ) -> Self::Rebind<N>
158 where
159 Id: Clone,
160 {
161 Any( self.0.iter().map(|( id, value )| ( id.clone(), map( id, value ))).collect() )
162 }
163
164 fn map_mut<N>( self, mut map: impl FnMut( T ) -> N ) -> Self::Rebind<N> {
165 Any( self.0.into_iter().map(|( id, value )| ( id, map( value ))).collect() )
166 }
167
168 fn get( &self, id: &Id ) -> Option<&T>
169 where
170 Id: Hash + Eq,
171 {
172 self.0.get( id )
173 }
174}
175
176impl<Id: Hash + Eq + Into<Val>> From<ExactlyOne<Id, Val>> for Val {
177 fn from( socket: ExactlyOne<Id, Val> ) -> Self {
178 Val::Tuple( vec![ socket.0.into(), socket.1 ])
179 }
180}
181
182impl<Id: Hash + Eq + Into<Val>> From<AtMostOne<Id, Val>> for Val {
183 fn from( socket: AtMostOne<Id, Val> ) -> Self {
184 match socket.0 {
185 None => Val::Option( None ),
186 Some(( id, val )) => Val::Option( Some( Box::new( Val::Tuple( vec![ id.into(), val ] )))),
187 }
188 }
189}
190
191impl<Id: Hash + Eq + Into<Val>> From<AtLeastOne<Id, Val>> for Val {
192 fn from( socket: AtLeastOne<Id, Val> ) -> Self {
193 Val::List(
194 socket.0.into_iter()
195 .map(|( id, val )| Val::Tuple( vec![ id.into(), val ]))
196 .collect()
197 )
198 }
199}
200
201impl<Id: Hash + Eq + Into<Val>> From<Any<Id, Val>> for Val {
202 fn from( socket: Any<Id, Val> ) -> Self {
203 Val::List(
204 socket.0.into_iter()
205 .map(|( id, val )| Val::Tuple( vec![ id.into(), val ]))
206 .collect()
207 )
208 }
209}
210
211#[cfg(test)]
212mod tests {
213
214 use crate::nem ;
215 use super::* ;
216
217 #[test]
218 fn exactly_one_maps_and_gets() {
219 let value = ExactlyOne( "plugin".to_string(), 10_u32 );
220 let mapped = value.map(| id, v | format!( "{id}:{v}" ));
221 assert_eq!( mapped.0, "plugin" );
222 assert_eq!( mapped.1, "plugin:10" );
223 assert_eq!( mapped.get( &"plugin".to_string() ), Some( &"plugin:10".to_string() ));
224 }
225
226 #[test]
227 fn at_most_one_maps_none_and_some() {
228 let none: AtMostOne<String, u32> = AtMostOne( None );
229 let mapped_none = none.map(| _, v | v + 1 );
230 assert!( mapped_none.0.is_none() );
231
232 let some = AtMostOne( Some(( "plugin".to_string(), 3_u32 )));
233 let mapped_some = some.map(| _, v | v + 1 );
234 assert_eq!( mapped_some.0, Some(( "plugin".to_string(), 4 )));
235 }
236
237 #[test]
238 fn at_least_one_maps_and_gets() {
239 let values = AtLeastOne( nem! { "a".to_string() => 1_u32, "b".to_string() => 2_u32 } );
240 let mapped = values.map(| _, v | v * 2 );
241 assert_eq!( mapped.get( &"a".to_string() ), Some( &2 ));
242 assert_eq!( mapped.get( &"b".to_string() ), Some( &4 ));
243 }
244
245 #[test]
246 fn any_maps_and_gets() {
247 let values = Any( HashMap::from([
248 ( "a".to_string(), 1_u32 ),
249 ( "b".to_string(), 2_u32 ),
250 ]));
251 let mapped = values.map(| _, v | v + 10 );
252 assert_eq!( mapped.get( &"a".to_string() ), Some( &11 ));
253 assert_eq!( mapped.get( &"b".to_string() ), Some( &12 ));
254 }
255
256 #[test]
257 fn exactly_one_into_val() {
258 let val = Val::from( ExactlyOne( "id".to_string(), Val::U32( 7 )));
259 match val {
260 Val::Tuple( items ) => {
261 assert_eq!( items.len(), 2 );
262 assert!( matches!( &items[0], Val::String( s ) if s == "id" ));
263 assert!( matches!( &items[1], Val::U32( 7 )));
264 }
265 other => panic!( "expected tuple, got {other:?}" ),
266 }
267 }
268
269 #[test]
270 fn at_most_one_into_val() {
271 let none = Val::from( AtMostOne::<String, Val>( None ));
272 assert!( matches!( none, Val::Option( None )));
273
274 let some = Val::from( AtMostOne( Some(( "id".to_string(), Val::U32( 1 )))));
275 match some {
276 Val::Option( Some( boxed )) => match *boxed {
277 Val::Tuple( items ) => {
278 assert_eq!( items.len(), 2 );
279 assert!( matches!( &items[0], Val::String( s ) if s == "id" ));
280 assert!( matches!( &items[1], Val::U32( 1 )));
281 }
282 other => panic!( "expected tuple, got {other:?}" ),
283 },
284 other => panic!( "expected option, got {other:?}" ),
285 }
286 }
287
288 #[test]
289 fn at_least_one_into_val() {
290 let val = Val::from( AtLeastOne( nem! { "a".to_string() => Val::U32( 1 ) }));
291 match val {
292 Val::List( items ) => {
293 assert_eq!( items.len(), 1 );
294 assert!( matches!( &items[0],
295 Val::Tuple( tuple )
296 if tuple.len() == 2
297 && matches!( &tuple[0], Val::String( s ) if s == "a" )
298 && matches!( &tuple[1], Val::U32( 1 ))
299 ));
300 }
301 other => panic!( "expected list, got {other:?}" ),
302 }
303 }
304
305 #[test]
306 fn any_into_val() {
307 let val = Val::from( Any( HashMap::from([
308 ( "a".to_string(), Val::U32( 1 )),
309 ( "b".to_string(), Val::U32( 2 )),
310 ])));
311 match val {
312 Val::List( items ) => {
313 assert_eq!( items.len(), 2 );
314 let mut seen = ( false, false );
315 for item in items {
316 match item {
317 Val::Tuple( tuple ) if tuple.len() == 2 => match (&tuple[0], &tuple[1]) {
318 ( Val::String( s ), Val::U32( 1 )) if s == "a" => seen.0 = true,
319 ( Val::String( s ), Val::U32( 2 )) if s == "b" => seen.1 = true,
320 _ => {}
321 },
322 _ => {}
323 }
324 }
325 assert!( seen.0 && seen.1 );
326 }
327 other => panic!( "expected list, got {other:?}" ),
328 }
329 }
330}