Skip to main content

tor_linkspec/ids/
by_id.rs

1//! Define a type for a set of HasRelayIds objects that can be looked up by any
2//! of their keys.
3
4use tor_basic_utils::{n_key_list, n_key_set};
5use tor_llcrypto::pk::ed25519::Ed25519Identity;
6use tor_llcrypto::pk::rsa::RsaIdentity;
7
8use crate::{HasRelayIds, RelayIdRef};
9
10n_key_list! {
11    /// A list of objects that can be accessed by relay identity.
12    ///
13    /// Multiple objects in the list can have a given relay identity.
14    ///
15    /// # Invariants
16    ///
17    /// Every object in the list **must** have at least one recognized relay identity; if it does
18    /// not, it cannot be inserted.
19    ///
20    /// This list may panic or give incorrect results if the values can change their keys through
21    /// interior mutability.
22    #[derive(Clone, Debug)]
23    pub struct[H:HasRelayIds] ListByRelayIds[H] for H
24    {
25        (Option) rsa: RsaIdentity { rsa_identity() },
26        (Option) ed25519: Ed25519Identity { ed_identity() },
27    }
28}
29
30n_key_set! {
31    /// A set of objects that can be accessed by relay identity.
32    ///
33    /// No more than one object in the set can have any given relay identity.
34    ///
35    /// # Invariants
36    ///
37    /// Every object in the set MUST have at least one recognized relay
38    /// identity; if it does not, it cannot be inserted.
39    ///
40    /// This set may panic or give incorrect results if the values can change their
41    /// keys through interior mutability.
42    ///
43    #[derive(Clone, Debug)]
44    pub struct[H:HasRelayIds] ByRelayIds[H] for H
45    {
46        (Option) rsa: RsaIdentity { rsa_identity() },
47        (Option) ed25519: Ed25519Identity { ed_identity() },
48    }
49}
50
51impl<H: HasRelayIds> ByRelayIds<H> {
52    /// Return the value in this set (if any) that has the key `key`.
53    pub fn by_id<'a, T>(&self, key: T) -> Option<&H>
54    where
55        T: Into<RelayIdRef<'a>>,
56    {
57        match key.into() {
58            RelayIdRef::Ed25519(ed) => self.by_ed25519(ed),
59            RelayIdRef::Rsa(rsa) => self.by_rsa(rsa),
60        }
61    }
62
63    /// Return the value in this set (if any) that has the key `key`.
64    pub fn remove_by_id<'a, T>(&mut self, key: T) -> Option<H>
65    where
66        T: Into<RelayIdRef<'a>>,
67    {
68        match key.into() {
69            RelayIdRef::Ed25519(ed) => self.remove_by_ed25519(ed),
70            RelayIdRef::Rsa(rsa) => self.remove_by_rsa(rsa),
71        }
72    }
73
74    /// Modify the value in this set (if any) that has the key `key`.
75    ///
76    /// Return values are as for [`modify_by_ed25519`](Self::modify_by_ed25519)
77    pub fn modify_by_id<'a, T, F>(&mut self, key: T, func: F) -> Vec<H>
78    where
79        T: Into<RelayIdRef<'a>>,
80        F: FnOnce(&mut H),
81    {
82        match key.into() {
83            RelayIdRef::Ed25519(ed) => self.modify_by_ed25519(ed, func),
84            RelayIdRef::Rsa(rsa) => self.modify_by_rsa(rsa, func),
85        }
86    }
87
88    /// Return the value in this set (if any) that has _all_ the relay IDs
89    /// that `key` does.
90    ///
91    /// Return `None` if `key` has no relay IDs.
92    pub fn by_all_ids<T>(&self, key: &T) -> Option<&H>
93    where
94        T: HasRelayIds,
95    {
96        let any_id = key.identities().next()?;
97        self.by_id(any_id)
98            .filter(|val| val.has_all_relay_ids_from(key))
99    }
100
101    /// Modify the value in this set (if any) that has _all_ the relay IDs
102    /// that `key` does.
103    ///
104    /// Return values are as for [`modify_by_ed25519`](Self::modify_by_ed25519)
105    pub fn modify_by_all_ids<T, F>(&mut self, key: &T, func: F) -> Vec<H>
106    where
107        T: HasRelayIds,
108        F: FnOnce(&mut H),
109    {
110        let any_id = match key.identities().next() {
111            Some(id) => id,
112            None => return Vec::new(),
113        };
114        self.modify_by_id(any_id, |val| {
115            if val.has_all_relay_ids_from(key) {
116                func(val);
117            }
118        })
119    }
120
121    /// Remove the single value in this set (if any) that has _exactly the same_
122    /// relay IDs that `key` does
123    pub fn remove_exact<T>(&mut self, key: &T) -> Option<H>
124    where
125        T: HasRelayIds,
126    {
127        let any_id = key.identities().next()?;
128        if self
129            .by_id(any_id)
130            .filter(|ent| ent.same_relay_ids(key))
131            .is_some()
132        {
133            self.remove_by_id(any_id)
134        } else {
135            None
136        }
137    }
138
139    /// Remove the single value in this set (if any) that has all the same
140    /// relay IDs that `key` does. If `key` does not have any relay IDs, no
141    /// value is returned.
142    pub fn remove_by_all_ids<T>(&mut self, key: &T) -> Option<H>
143    where
144        T: HasRelayIds,
145    {
146        let any_id = key.identities().next()?;
147        if self
148            .by_id(any_id)
149            .filter(|ent| ent.has_all_relay_ids_from(key))
150            .is_some()
151        {
152            self.remove_by_id(any_id)
153        } else {
154            None
155        }
156    }
157
158    /// Return a reference to every element in this set that shares _any_ ID
159    /// with `key`.
160    ///
161    /// No element is returned more than once.
162    pub fn all_overlapping<T>(&self, key: &T) -> Vec<&H>
163    where
164        T: HasRelayIds,
165    {
166        use by_address::ByAddress;
167        use std::collections::HashSet;
168
169        let mut items: HashSet<ByAddress<&H>> = HashSet::new();
170
171        for ident in key.identities() {
172            if let Some(found) = self.by_id(ident) {
173                items.insert(ByAddress(found));
174            }
175        }
176
177        items.into_iter().map(|by_addr| by_addr.0).collect()
178    }
179}
180
181impl<H: HasRelayIds> ListByRelayIds<H> {
182    /// Return an iterator of the values in this list that have the key `key`.
183    pub fn by_id<'a, T>(&self, key: T) -> ListByRelayIdsIter<H>
184    where
185        T: Into<RelayIdRef<'a>>,
186    {
187        match key.into() {
188            RelayIdRef::Ed25519(ed) => self.by_ed25519(ed),
189            RelayIdRef::Rsa(rsa) => self.by_rsa(rsa),
190        }
191    }
192
193    /// Return the values in this list that have *all* the relay IDs that `key` does.
194    ///
195    /// Returns an empty iterator if `key` has no relay IDs.
196    pub fn by_all_ids<'a>(&'a self, key: &'a impl HasRelayIds) -> impl Iterator<Item = &'a H> + 'a {
197        key.identities()
198            .next()
199            .map_or_else(Default::default, |id| self.by_id(id))
200            .filter(|val| val.has_all_relay_ids_from(key))
201    }
202
203    /// Return a reference to every element in this set that shares *any* ID with `key`.
204    ///
205    /// No element is returned more than once. Equality is compared using
206    /// [`ByAddress`](by_address::ByAddress).
207    pub fn all_overlapping<T>(&self, key: &T) -> Vec<&H>
208    where
209        T: HasRelayIds,
210    {
211        use by_address::ByAddress;
212        use std::collections::HashSet;
213
214        let mut items: HashSet<ByAddress<&H>> = HashSet::new();
215
216        for ident in key.identities() {
217            for found in self.by_id(ident) {
218                items.insert(ByAddress(found));
219            }
220        }
221
222        items.into_iter().map(|by_addr| by_addr.0).collect()
223    }
224
225    /// Return a reference to every element in this list whose relay IDs are a subset of the relay
226    /// IDs that `key` has.
227    ///
228    /// No element is returned more than once. Equality is compared using
229    /// [`ByAddress`](by_address::ByAddress).
230    pub fn all_subset<T>(&self, key: &T) -> Vec<&H>
231    where
232        T: HasRelayIds,
233    {
234        use by_address::ByAddress;
235        use std::collections::HashSet;
236
237        let mut items: HashSet<ByAddress<&H>> = HashSet::new();
238
239        for ident in key.identities() {
240            for found in self.by_id(ident) {
241                // if 'key's relay ids are a superset of 'found's relay ids
242                if key.has_all_relay_ids_from(found) {
243                    items.insert(ByAddress(found));
244                }
245            }
246        }
247
248        items.into_iter().map(|by_addr| by_addr.0).collect()
249    }
250
251    /// Return the values in this list that have the key `key` and where `filter` returns `true`.
252    pub fn remove_by_id<'a, T>(&mut self, key: T, filter: impl FnMut(&H) -> bool) -> Vec<H>
253    where
254        T: Into<RelayIdRef<'a>>,
255    {
256        match key.into() {
257            RelayIdRef::Ed25519(ed) => self.remove_by_ed25519(ed, filter),
258            RelayIdRef::Rsa(rsa) => self.remove_by_rsa(rsa, filter),
259        }
260    }
261
262    /// Remove and return the values in this list that have *exactly the same* relay IDs that `key`
263    /// does.
264    pub fn remove_exact<T>(&mut self, key: &T) -> Vec<H>
265    where
266        T: HasRelayIds,
267    {
268        let Some(id) = key.identities().next() else {
269            return Vec::new();
270        };
271
272        self.remove_by_id(id, |val| val.same_relay_ids(key))
273    }
274
275    /// Remove and return the values in this list that have all the same relay IDs that `key` does.
276    ///
277    /// If `key` has no relay IDs, then no values are removed.
278    pub fn remove_by_all_ids<T>(&mut self, key: &T) -> Vec<H>
279    where
280        T: HasRelayIds,
281    {
282        let Some(id) = key.identities().next() else {
283            return Vec::new();
284        };
285
286        self.remove_by_id(id, |val| val.has_all_relay_ids_from(key))
287    }
288}
289
290pub use tor_basic_utils::n_key_list::Error as ListByRelayIdsError;
291pub use tor_basic_utils::n_key_set::Error as ByRelayIdsError;
292
293#[cfg(test)]
294mod test {
295    // @@ begin test lint list maintained by maint/add_warning @@
296    #![allow(clippy::bool_assert_comparison)]
297    #![allow(clippy::clone_on_copy)]
298    #![allow(clippy::dbg_macro)]
299    #![allow(clippy::mixed_attributes_style)]
300    #![allow(clippy::print_stderr)]
301    #![allow(clippy::print_stdout)]
302    #![allow(clippy::single_char_pattern)]
303    #![allow(clippy::unwrap_used)]
304    #![allow(clippy::unchecked_time_subtraction)]
305    #![allow(clippy::useless_vec)]
306    #![allow(clippy::needless_pass_by_value)]
307    #![allow(clippy::string_slice)] // See arti#2571
308    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
309
310    use super::*;
311    use crate::{RelayIds, RelayIdsBuilder};
312
313    fn sort<T: std::cmp::Ord>(i: impl Iterator<Item = T>) -> Vec<T> {
314        let mut v: Vec<_> = i.collect();
315        v.sort();
316        v
317    }
318
319    #[test]
320    fn lookup() {
321        let rsa1: RsaIdentity = (*b"12345678901234567890").into();
322        let rsa2: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
323        let rsa3: RsaIdentity = (*b"abcefghijklmnopQRSTU").into();
324        let ed1: Ed25519Identity = (*b"12345678901234567890123456789012").into();
325        let ed2: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyzABCDEFG").into();
326        let ed3: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyz1234567").into();
327
328        let keys1 = RelayIdsBuilder::default()
329            .rsa_identity(rsa1)
330            .ed_identity(ed1)
331            .build()
332            .unwrap();
333
334        let keys2 = RelayIdsBuilder::default()
335            .rsa_identity(rsa2)
336            .ed_identity(ed2)
337            .build()
338            .unwrap();
339
340        // `ByRelayIds` and `ListByRelayIds` work similarly in the case where we only add a single
341        // value per key, so we can test them both here with the same test cases.
342
343        let mut set = ByRelayIds::new();
344        set.insert(keys1.clone());
345        set.insert(keys2.clone());
346
347        let mut list = ListByRelayIds::new();
348        list.insert(keys1.clone());
349        list.insert(keys2.clone());
350
351        // Try by_id
352        assert_eq!(set.by_id(&rsa1), Some(&keys1));
353        assert_eq!(set.by_id(&ed1), Some(&keys1));
354        assert_eq!(set.by_id(&rsa2), Some(&keys2));
355        assert_eq!(set.by_id(&ed2), Some(&keys2));
356        assert_eq!(set.by_id(&rsa3), None);
357        assert_eq!(set.by_id(&ed3), None);
358        assert_eq!(sort(list.by_id(&rsa1)), [&keys1]);
359        assert_eq!(sort(list.by_id(&ed1)), [&keys1]);
360        assert_eq!(sort(list.by_id(&rsa2)), [&keys2]);
361        assert_eq!(sort(list.by_id(&ed2)), [&keys2]);
362        assert_eq!(list.by_id(&rsa3).len(), 0);
363        assert_eq!(list.by_id(&ed3).len(), 0);
364
365        // Try exact lookup
366        assert_eq!(set.by_all_ids(&keys1), Some(&keys1));
367        assert_eq!(set.by_all_ids(&keys2), Some(&keys2));
368        assert_eq!(set.by_all_ids(&RelayIds::empty()), None);
369        assert_eq!(sort(list.by_all_ids(&keys1)), [&keys1]);
370        assert_eq!(sort(list.by_all_ids(&keys2)), [&keys2]);
371        assert!(sort(list.by_all_ids(&RelayIds::empty())).is_empty());
372        {
373            let search = RelayIdsBuilder::default()
374                .rsa_identity(rsa1)
375                .build()
376                .unwrap();
377            assert_eq!(set.by_all_ids(&search), Some(&keys1));
378            assert_eq!(sort(list.by_all_ids(&search)), [&keys1]);
379        }
380        {
381            let search = RelayIdsBuilder::default()
382                .rsa_identity(rsa1)
383                .ed_identity(ed2)
384                .build()
385                .unwrap();
386            assert_eq!(set.by_all_ids(&search), None);
387            assert!(sort(list.by_all_ids(&search)).is_empty());
388        }
389
390        // Try looking for overlap
391        assert_eq!(set.all_overlapping(&keys1), vec![&keys1]);
392        assert_eq!(set.all_overlapping(&keys2), vec![&keys2]);
393        assert_eq!(list.all_overlapping(&keys1), vec![&keys1]);
394        assert_eq!(list.all_overlapping(&keys2), vec![&keys2]);
395        {
396            let search = RelayIdsBuilder::default()
397                .rsa_identity(rsa1)
398                .ed_identity(ed2)
399                .build()
400                .unwrap();
401            let answer = set.all_overlapping(&search);
402            assert_eq!(answer.len(), 2);
403            assert!(answer.contains(&&keys1));
404            assert!(answer.contains(&&keys2));
405            let answer = list.all_overlapping(&search);
406            assert_eq!(answer.len(), 2);
407            assert!(answer.contains(&&keys1));
408            assert!(answer.contains(&&keys2));
409        }
410        {
411            let search = RelayIdsBuilder::default()
412                .rsa_identity(rsa2)
413                .build()
414                .unwrap();
415            assert_eq!(set.all_overlapping(&search), vec![&keys2]);
416            assert_eq!(list.all_overlapping(&search), vec![&keys2]);
417        }
418        {
419            let search = RelayIdsBuilder::default()
420                .rsa_identity(rsa3)
421                .build()
422                .unwrap();
423            assert!(set.all_overlapping(&search).is_empty());
424            assert!(list.all_overlapping(&search).is_empty());
425        }
426    }
427
428    #[test]
429    fn remove_exact() {
430        let rsa1: RsaIdentity = (*b"12345678901234567890").into();
431        let rsa2: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
432        let ed1: Ed25519Identity = (*b"12345678901234567890123456789012").into();
433        let ed2: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyzABCDEFG").into();
434
435        let keys1 = RelayIdsBuilder::default()
436            .rsa_identity(rsa1)
437            .ed_identity(ed1)
438            .build()
439            .unwrap();
440
441        let keys2 = RelayIdsBuilder::default()
442            .rsa_identity(rsa2)
443            .ed_identity(ed2)
444            .build()
445            .unwrap();
446
447        // `ByRelayIds` and `ListByRelayIds` work similarly in the case where we only add a single
448        // value per key, so we can test them both here with the same test cases.
449
450        let mut set = ByRelayIds::new();
451        set.insert(keys1.clone());
452        set.insert(keys2.clone());
453        assert_eq!(set.len(), 2);
454
455        let mut list = ListByRelayIds::new();
456        list.insert(keys1.clone());
457        list.insert(keys2.clone());
458        assert_eq!(list.len(), 2);
459
460        assert_eq!(set.remove_exact(&keys1), Some(keys1.clone()));
461        assert_eq!(set.len(), 1);
462        assert_eq!(list.remove_exact(&keys1), vec![keys1.clone()]);
463        assert_eq!(list.len(), 1);
464
465        {
466            let search = RelayIdsBuilder::default().ed_identity(ed2).build().unwrap();
467
468            // We're calling remove_exact, but we did not list _all_ the keys in keys2.
469            assert_eq!(set.remove_exact(&search), None);
470            assert_eq!(set.len(), 1);
471            assert_eq!(list.remove_exact(&search), vec![]);
472            assert_eq!(list.len(), 1);
473
474            // If we were to use `remove_by_all_ids` with a search that didn't
475            // match, it wouldn't work.
476            let no_match = RelayIdsBuilder::default()
477                .ed_identity(ed2)
478                .rsa_identity(rsa1)
479                .build()
480                .unwrap();
481            assert_eq!(set.remove_by_all_ids(&no_match), None);
482            assert_eq!(set.len(), 1);
483            assert_eq!(list.remove_by_all_ids(&no_match), vec![]);
484            assert_eq!(list.len(), 1);
485
486            // If we use `remove_by_all_ids` with the original search, though,
487            // it will remove the element.
488            assert_eq!(set.remove_by_all_ids(&search), Some(keys2.clone()));
489            assert!(set.is_empty());
490            assert_eq!(list.remove_by_all_ids(&search), vec![keys2.clone()]);
491            assert!(list.is_empty());
492        }
493    }
494
495    #[test]
496    fn all_subset() {
497        let rsa1: RsaIdentity = (*b"12345678901234567890").into();
498        let rsa2: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
499        let ed1: Ed25519Identity = (*b"12345678901234567890123456789012").into();
500
501        // one rsa id and one ed id
502        let keys1 = RelayIdsBuilder::default()
503            .rsa_identity(rsa1)
504            .ed_identity(ed1)
505            .build()
506            .unwrap();
507
508        // one rsa id
509        let keys2 = RelayIdsBuilder::default()
510            .rsa_identity(rsa2)
511            .build()
512            .unwrap();
513
514        let mut list = ListByRelayIds::new();
515        list.insert(keys1.clone());
516        list.insert(keys2.clone());
517
518        assert_eq!(list.all_subset(&keys1), vec![&keys1]);
519        assert_eq!(list.all_subset(&keys2), vec![&keys2]);
520
521        {
522            let search = RelayIdsBuilder::default()
523                .rsa_identity(rsa1)
524                .build()
525                .unwrap();
526            assert!(list.all_subset(&search).is_empty());
527        }
528
529        {
530            let search = RelayIdsBuilder::default().ed_identity(ed1).build().unwrap();
531            assert!(list.all_subset(&search).is_empty());
532        }
533
534        {
535            let search = RelayIdsBuilder::default()
536                .rsa_identity(rsa2)
537                .build()
538                .unwrap();
539            assert_eq!(list.all_subset(&search), vec![&keys2]);
540        }
541
542        {
543            let search = RelayIdsBuilder::default()
544                .ed_identity(ed1)
545                .rsa_identity(rsa2)
546                .build()
547                .unwrap();
548            assert_eq!(list.all_subset(&search), vec![&keys2]);
549        }
550    }
551
552    #[test]
553    fn list_by_relay_ids() {
554        #[derive(Clone, Debug)]
555        struct ErsatzChannel<T> {
556            val: T,
557            ids: RelayIds,
558        }
559
560        impl<T> ErsatzChannel<T> {
561            fn new(val: T, ids: RelayIds) -> Self {
562                Self { val, ids }
563            }
564        }
565
566        impl<T> HasRelayIds for ErsatzChannel<T> {
567            fn identity(&self, key_type: crate::RelayIdType) -> Option<RelayIdRef<'_>> {
568                self.ids.identity(key_type)
569            }
570        }
571
572        // helper to build a `RelayIds` to make tests shorter
573        fn ids(
574            rsa: impl Into<Option<RsaIdentity>>,
575            ed: impl Into<Option<Ed25519Identity>>,
576        ) -> RelayIds {
577            let mut ids = RelayIdsBuilder::default();
578            if let Some(rsa) = rsa.into() {
579                ids.rsa_identity(rsa);
580            }
581            if let Some(ed) = ed.into() {
582                ids.ed_identity(ed);
583            }
584            ids.build().unwrap()
585        }
586
587        // ids for relay A
588        let rsa_a: RsaIdentity = (*b"12345678901234567890").into();
589        let ed_a: Ed25519Identity = (*b"12345678901234567890123456789012").into();
590
591        // ids for relay B
592        let ed_b: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyzABCDEFG").into();
593        let rsa_b: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
594
595        // channel to A with all ids
596        let channel_a_all = ErsatzChannel::new("channel-a-all", ids(rsa_a, ed_a));
597
598        // channel to A with only the rsa id
599        let channel_a_rsa_only_1 = ErsatzChannel::new("channel-a-rsa-only-1", ids(rsa_a, None));
600
601        // channel to A with only the rsa id; this could for example represent a channel with the
602        // same relay id as above but at a different ip address
603        let channel_a_rsa_only_2 = ErsatzChannel::new("channel-a-rsa-only-2", ids(rsa_a, None));
604
605        // channel to A with only the ed id
606        let channel_a_ed_only = ErsatzChannel::new("channel-a-ed-only", ids(None, ed_a));
607
608        // channel to B with all ids
609        let channel_b_all = ErsatzChannel::new("channel-b-all", ids(rsa_b, ed_b));
610
611        // an "invalid" channel with A's rsa id and B's ed id; this could for example represent an
612        // in-progress pending channel that hasn't been verified yet
613        let channel_invalid = ErsatzChannel::new("channel-invalid", ids(rsa_a, ed_b));
614
615        let mut list = ListByRelayIds::new();
616        list.insert(channel_a_all.clone());
617        list.insert(channel_a_rsa_only_1.clone());
618        list.insert(channel_a_rsa_only_2.clone());
619        list.insert(channel_a_ed_only.clone());
620        list.insert(channel_b_all.clone());
621        list.insert(channel_invalid.clone());
622
623        // look up by A's rsa id
624        assert_eq!(
625            sort(list.by_id(&rsa_a).map(|x| x.val)),
626            [
627                "channel-a-all",
628                "channel-a-rsa-only-1",
629                "channel-a-rsa-only-2",
630                "channel-invalid",
631            ],
632        );
633
634        // look up by A's ed id
635        assert_eq!(
636            sort(list.by_id(&ed_a).map(|x| x.val)),
637            ["channel-a-all", "channel-a-ed-only"],
638        );
639
640        // look up by B's rsa id
641        assert_eq!(sort(list.by_id(&rsa_b).map(|x| x.val)), ["channel-b-all"]);
642
643        // look up by B's ed id
644        assert_eq!(
645            sort(list.by_id(&ed_b).map(|x| x.val)),
646            ["channel-b-all", "channel-invalid"],
647        );
648
649        // look up by both A's rsa id and ed id
650        assert_eq!(
651            sort(list.by_all_ids(&ids(rsa_a, ed_a)).map(|x| x.val)),
652            ["channel-a-all"],
653        );
654
655        // look up by both B's rsa id and ed id
656        assert_eq!(
657            sort(list.by_all_ids(&ids(rsa_b, ed_b)).map(|x| x.val)),
658            ["channel-b-all"],
659        );
660
661        // look up by either A's rsa id or ed id
662        assert_eq!(
663            sort(
664                list.all_overlapping(&ids(rsa_a, ed_a))
665                    .into_iter()
666                    .map(|x| x.val)
667            ),
668            [
669                "channel-a-all",
670                "channel-a-ed-only",
671                "channel-a-rsa-only-1",
672                "channel-a-rsa-only-2",
673                "channel-invalid",
674            ],
675        );
676
677        // look up where channel's ids are a subset of A's ids
678        assert_eq!(
679            sort(
680                list.all_subset(&ids(rsa_a, ed_a))
681                    .into_iter()
682                    .map(|x| x.val)
683            ),
684            [
685                "channel-a-all",
686                "channel-a-ed-only",
687                "channel-a-rsa-only-1",
688                "channel-a-rsa-only-2",
689            ],
690        );
691
692        // some sanity checks
693        assert_eq!(list.by_all_ids(&ids(None, None)).count(), 0);
694        assert!(list.all_overlapping(&ids(None, None)).is_empty());
695        assert!(list.all_subset(&ids(None, None)).is_empty());
696        assert_eq!(
697            sort(
698                list.all_overlapping(&ids(rsa_a, None))
699                    .into_iter()
700                    .map(|x| x.val)
701            ),
702            sort(list.by_id(&rsa_a).map(|x| x.val)),
703        );
704        assert_eq!(
705            sort(
706                list.all_overlapping(&ids(None, ed_b))
707                    .into_iter()
708                    .map(|x| x.val)
709            ),
710            sort(list.by_id(&ed_b).map(|x| x.val)),
711        );
712        assert_eq!(
713            sort(list.by_id(&rsa_a).map(|x| x.val)),
714            sort(list.by_rsa(&rsa_a).map(|x| x.val)),
715        );
716        assert_eq!(
717            sort(list.by_id(&ed_a).map(|x| x.val)),
718            sort(list.by_ed25519(&ed_a).map(|x| x.val)),
719        );
720
721        // remove channels with exactly A's rsa id and ed id
722        {
723            let mut list = list.clone();
724            assert_eq!(
725                sort(
726                    list.remove_exact(&ids(rsa_a, ed_a))
727                        .into_iter()
728                        .map(|x| x.val)
729                ),
730                ["channel-a-all"],
731            );
732            assert_eq!(list.by_all_ids(&ids(rsa_a, ed_a)).count(), 0);
733        }
734
735        // remove channels with exactly A's rsa id and no ed id
736        {
737            let mut list = list.clone();
738            assert_eq!(
739                sort(
740                    list.remove_exact(&ids(rsa_a, None))
741                        .into_iter()
742                        .map(|x| x.val)
743                ),
744                ["channel-a-rsa-only-1", "channel-a-rsa-only-2"],
745            );
746            assert_eq!(
747                sort(list.by_all_ids(&ids(rsa_a, None)).map(|x| x.val)),
748                ["channel-a-all", "channel-invalid"],
749            );
750        }
751
752        // remove channels with at least A's rsa id
753        {
754            let mut list = list.clone();
755            assert_eq!(
756                sort(
757                    list.remove_by_all_ids(&ids(rsa_a, None))
758                        .into_iter()
759                        .map(|x| x.val)
760                ),
761                [
762                    "channel-a-all",
763                    "channel-a-rsa-only-1",
764                    "channel-a-rsa-only-2",
765                    "channel-invalid",
766                ],
767            );
768            assert_eq!(list.by_all_ids(&ids(rsa_a, None)).count(), 0);
769        }
770    }
771}