1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![allow(clippy::cognitive_complexity)] #![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_time_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] #![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] #![allow(mismatched_lifetime_syntaxes)] #![allow(clippy::collapsible_if)] #![deny(clippy::unused_async)]
47#![deny(clippy::string_slice)] #![allow(non_upper_case_globals)]
51#![allow(clippy::upper_case_acronyms)]
52
53use caret::caret_int;
54
55use derive_deftly::Deftly;
56use thiserror::Error;
57use tor_basic_utils::intern::{GloballyInternable as _, Intern};
58
59pub mod named;
60
61#[doc(hidden)]
63pub mod macro_export {
64 pub use paste;
65}
66
67caret_int! {
68 #[derive(Hash,Ord,PartialOrd)]
76 pub struct ProtoKind(u8) {
77 Link = 0,
79 LinkAuth = 1,
81 Relay = 2,
84 DirCache = 3,
86 HSDir = 4,
88 HSIntro = 5,
90 HSRend = 6,
92 Desc = 7,
94 Microdesc = 8,
96 Cons = 9,
98 Padding = 10,
100 FlowCtrl = 11,
102 Conflux = 12,
104 }
105}
106
107const N_RECOGNIZED: usize = 13;
109
110const MAX_VER: usize = 63;
112
113#[derive(Eq, PartialEq, Copy, Clone, Debug)]
115pub struct NamedSubver {
116 kind: ProtoKind,
120 version: u8,
124}
125
126impl NamedSubver {
127 const fn new(kind: ProtoKind, version: u8) -> Self {
133 assert!((kind.0 as usize) < N_RECOGNIZED);
134 assert!((version as usize) <= MAX_VER);
135 Self { kind, version }
136 }
137}
138
139#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
152pub struct NumberedSubver {
153 kind: ProtoKind,
155 version: u8,
157}
158
159impl NumberedSubver {
160 pub fn new(kind: impl Into<ProtoKind>, version: u8) -> Self {
162 Self {
163 kind: kind.into(),
164 version,
165 }
166 }
167 pub fn into_parts(self) -> (ProtoKind, u8) {
169 (self.kind, self.version)
170 }
171}
172impl From<NamedSubver> for NumberedSubver {
173 fn from(value: NamedSubver) -> Self {
174 Self {
175 kind: value.kind,
176 version: value.version,
177 }
178 }
179}
180
181impl From<NamedSubver> for Protocols {
182 fn from(value: NamedSubver) -> Self {
183 Self::from_iter([value])
184 }
185}
186
187#[cfg(feature = "tor-bytes")]
188impl tor_bytes::Readable for NumberedSubver {
189 fn take_from(b: &mut tor_bytes::Reader<'_>) -> tor_bytes::Result<Self> {
190 let kind = b.take_u8()?;
191 let version = b.take_u8()?;
192 Ok(Self::new(kind, version))
193 }
194}
195
196#[cfg(feature = "tor-bytes")]
197impl tor_bytes::Writeable for NumberedSubver {
198 fn write_onto<B: tor_bytes::Writer + ?Sized>(&self, b: &mut B) -> tor_bytes::EncodeResult<()> {
199 b.write_u8(self.kind.into());
200 b.write_u8(self.version);
201 Ok(())
202 }
203}
204
205#[derive(Eq, PartialEq, Clone, Debug, Hash, Ord, PartialOrd)]
207enum Protocol {
208 Proto(ProtoKind),
212 Unrecognized(String),
214}
215
216impl Protocol {
217 fn is_unrecognized(&self, s: &str) -> bool {
219 match self {
220 Protocol::Unrecognized(s2) => s2 == s,
221 _ => false,
222 }
223 }
224 fn to_str(&self) -> &str {
226 match self {
227 Protocol::Proto(k) => k.to_str().unwrap_or("<bug>"),
228 Protocol::Unrecognized(s) => s,
229 }
230 }
231}
232
233fn is_valid_proto_name(s: &str) -> bool {
235 (1..=100).contains(&s.len()) && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
236}
237
238impl std::str::FromStr for Protocol {
239 type Err = ParseError;
240
241 fn from_str(s: &str) -> Result<Self, Self::Err> {
242 match ProtoKind::from_name(s) {
243 Some(p) => Ok(Protocol::Proto(p)),
244 None => {
245 if is_valid_proto_name(s) {
246 Ok(Protocol::Unrecognized(s.to_string()))
247 } else {
248 Err(ParseError::Malformed)
249 }
250 }
251 }
252 }
253}
254
255#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
259struct SubprotocolEntry {
260 proto: Protocol,
262 supported: u64,
265}
266
267#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269enum ParseStrictness {
270 Strict,
272
273 CTorCompatible,
275}
276
277#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
296#[cfg_attr(
297 feature = "serde",
298 derive(serde_with::DeserializeFromStr, serde_with::SerializeDisplay)
299)]
300pub struct Protocols(
301 Intern<ProtocolsInner>,
306);
307
308#[derive(Default, Clone, Debug, Eq, PartialEq, Hash, Deftly)]
312#[derive_deftly(tor_basic_utils::GloballyInternable)]
313struct ProtocolsInner {
314 recognized: [u64; N_RECOGNIZED],
316 unrecognized: Vec<SubprotocolEntry>,
321}
322
323impl From<ProtocolsInner> for Protocols {
324 fn from(value: ProtocolsInner) -> Self {
325 Protocols(value.into_intern())
326 }
327}
328
329impl Protocols {
330 pub fn new() -> Self {
338 Protocols::default()
339 }
340
341 pub fn from_kind_and_versions(kind: ProtoKind, versions: &str) -> Result<Self, ParseError> {
346 let versions = parse_version_mask(versions, ParseStrictness::Strict)?;
347 let mut protocols = ProtocolsInner::default();
348
349 if let Some(p) = protocols.recognized.get_mut(usize::from(kind.get())) {
350 *p = versions;
351 } else {
352 return Err(ParseError::Malformed);
353 }
354
355 Ok(protocols.into())
356 }
357
358 fn supports_recognized_ver(&self, proto: usize, ver: u8) -> bool {
361 if usize::from(ver) > MAX_VER {
362 return false;
363 }
364 if proto >= self.0.recognized.len() {
365 return false;
366 }
367 (self.0.recognized[proto] & (1 << ver)) != 0
368 }
369 fn supports_unrecognized_ver(&self, proto: &str, ver: u8) -> bool {
375 if usize::from(ver) > MAX_VER {
376 return false;
377 }
378 let ent = self
379 .0
380 .unrecognized
381 .iter()
382 .find(|ent| ent.proto.is_unrecognized(proto));
383 match ent {
384 Some(e) => (e.supported & (1 << ver)) != 0,
385 None => false,
386 }
387 }
388
389 pub fn is_empty(&self) -> bool {
391 self.0.recognized.iter().all(|v| *v == 0)
392 && self.0.unrecognized.iter().all(|p| p.supported == 0)
393 }
394
395 pub fn supports_known_subver(&self, proto: ProtoKind, ver: u8) -> bool {
408 self.supports_recognized_ver(proto.get() as usize, ver)
409 }
410 pub fn supports_subver(&self, proto: &str, ver: u8) -> bool {
423 match ProtoKind::from_name(proto) {
424 Some(p) => self.supports_recognized_ver(p.get() as usize, ver),
425 None => self.supports_unrecognized_ver(proto, ver),
426 }
427 }
428
429 pub fn supports_named_subver(&self, protover: NamedSubver) -> bool {
438 self.supports_known_subver(protover.kind, protover.version)
439 }
440
441 pub fn supports_numbered_subver(&self, protover: NumberedSubver) -> bool {
450 self.supports_known_subver(protover.kind, protover.version)
451 }
452
453 pub fn difference(&self, other: &Protocols) -> Protocols {
464 let mut r = ProtocolsInner::default();
465
466 for i in 0..N_RECOGNIZED {
467 r.recognized[i] = self.0.recognized[i] & !other.0.recognized[i];
468 }
469 for ent in self.0.unrecognized.iter() {
471 let mut ent = ent.clone();
472 if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
473 ent.supported &= !other_ent.supported;
474 }
475 if ent.supported != 0 {
476 r.unrecognized.push(ent);
477 }
478 }
479 Protocols::from(r)
480 }
481
482 pub fn union(&self, other: &Protocols) -> Protocols {
493 let mut r = (**self.0).clone();
494 for i in 0..N_RECOGNIZED {
495 r.recognized[i] |= other.0.recognized[i];
496 }
497 for ent in other.0.unrecognized.iter() {
498 if let Some(my_ent) = r.unrecognized.iter_mut().find(|e| e.proto == ent.proto) {
499 my_ent.supported |= ent.supported;
500 } else {
501 r.unrecognized.push(ent.clone());
502 }
503 }
504 r.unrecognized.sort();
505 Protocols::from(r)
506 }
507
508 pub fn intersection(&self, other: &Protocols) -> Protocols {
519 let mut r = ProtocolsInner::default();
520 for i in 0..N_RECOGNIZED {
521 r.recognized[i] = self.0.recognized[i] & other.0.recognized[i];
522 }
523 for ent in self.0.unrecognized.iter() {
524 if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
525 let supported = ent.supported & other_ent.supported;
526 if supported != 0 {
527 r.unrecognized.push(SubprotocolEntry {
528 proto: ent.proto.clone(),
529 supported,
530 });
531 }
532 }
533 }
534 r.unrecognized.sort();
535 Protocols::from(r)
536 }
537
538 pub fn from_str_c_compatible(s: &str) -> Result<Self, ParseError> {
548 Self::from_str_impl(s, ParseStrictness::CTorCompatible)
549 }
550}
551
552impl ProtocolsInner {
553 fn add(
565 &mut self,
566 foundmask: &mut u64,
567 ent: SubprotocolEntry,
568 strictness: ParseStrictness,
569 ) -> Result<(), ParseError> {
570 match ent.proto {
571 Protocol::Proto(k) => {
572 let idx = k.get() as usize;
573 assert!(idx < N_RECOGNIZED); let bit = 1 << u64::from(k.get());
575 if (*foundmask & bit) != 0 {
576 match strictness {
577 ParseStrictness::Strict => return Err(ParseError::Duplicate),
578 ParseStrictness::CTorCompatible => return Ok(()),
579 }
580 }
581 *foundmask |= bit;
582 self.recognized[idx] = ent.supported;
583 }
584 Protocol::Unrecognized(_) => {
585 if ent.supported != 0 {
586 self.unrecognized.push(ent);
587 }
588 }
589 }
590 Ok(())
591 }
592}
593
594#[derive(Error, Debug, PartialEq, Eq, Clone)]
596#[non_exhaustive]
597pub enum ParseError {
598 #[error("Protocol version out of range")]
600 OutOfRange,
601 #[error("Duplicate protocol entry")]
603 Duplicate,
604 #[error("Malformed protocol entry")]
606 Malformed,
607}
608
609fn bitrange(lo: u64, hi: u64) -> u64 {
622 assert!(lo <= hi && lo <= 63 && hi <= 63);
623 let mut mask = !0;
624 mask <<= 63 - hi;
625 mask >>= 63 - hi + lo;
626 mask <<= lo;
627 mask
628}
629
630fn is_good_number(n: &str, strictness: ParseStrictness) -> bool {
634 if strictness == ParseStrictness::Strict && n.starts_with('0') {
635 return false;
636 }
637 n.chars().all(|ch| ch.is_ascii_digit())
638}
639
640fn parse_version_mask(versions: &str, strictness: ParseStrictness) -> Result<u64, ParseError> {
642 if versions.is_empty() {
643 return Ok(0);
647 }
648 let mut supported = 0_u64;
650 for ent in versions.split(',') {
651 let (lo_s, hi_s) = ent.split_once('-').unwrap_or((ent, ent));
655
656 if !is_good_number(lo_s, strictness) {
657 return Err(ParseError::Malformed);
658 }
659 if !is_good_number(hi_s, strictness) {
660 return Err(ParseError::Malformed);
661 }
662 let lo: u64 = lo_s.parse().map_err(|_| ParseError::Malformed)?;
663 let hi: u64 = hi_s.parse().map_err(|_| ParseError::Malformed)?;
664 if lo > (MAX_VER as u64) || hi > (MAX_VER as u64) {
666 return Err(ParseError::OutOfRange);
667 }
668 if lo > hi {
669 return Err(ParseError::Malformed);
670 }
671 let mask = bitrange(lo, hi);
672 if strictness == ParseStrictness::Strict && (supported & mask) != 0 {
674 return Err(ParseError::Duplicate);
675 }
676 let mask = mask & !1;
678 supported |= mask;
680 }
681
682 Ok(supported)
683}
684
685impl SubprotocolEntry {
689 fn from_str(s: &str, strictness: ParseStrictness) -> Result<Self, ParseError> {
691 let (name, versions) = s.split_once('=').ok_or(ParseError::Malformed)?;
693
694 let proto: Protocol = name.parse()?;
696 Ok(SubprotocolEntry {
697 proto,
698 supported: parse_version_mask(versions, strictness)?,
699 })
700 }
701}
702
703impl std::str::FromStr for Protocols {
716 type Err = ParseError;
717
718 fn from_str(s: &str) -> Result<Self, ParseError> {
719 Protocols::from_str_impl(s, ParseStrictness::Strict)
720 }
721}
722
723impl Protocols {
724 fn from_str_impl(s: &str, strictness: ParseStrictness) -> Result<Self, ParseError> {
726 let mut result = ProtocolsInner::default();
727 let mut foundmask = 0_u64;
728 for ent in s.split(' ') {
729 if ent.is_empty() {
730 continue;
731 }
732
733 let s = SubprotocolEntry::from_str(ent, strictness)?;
734 result.add(&mut foundmask, s, strictness)?;
735 }
736 result.unrecognized.sort_by(|a, b| a.proto.cmp(&b.proto));
739 match strictness {
740 ParseStrictness::Strict => {
741 if result
742 .unrecognized
743 .windows(2)
744 .any(|w| w[0].proto == w[1].proto)
745 {
746 return Err(ParseError::Duplicate);
747 }
748 }
749 ParseStrictness::CTorCompatible => {
750 result.unrecognized.dedup_by(|a, b| a.proto.eq(&b.proto));
751 }
752 }
753
754 Ok(result.into())
755 }
756}
757
758fn dumpmask(mut mask: u64) -> String {
772 fn append(v: &mut Vec<String>, lo: u32, hi: u32) {
774 if lo == hi {
775 v.push(lo.to_string());
776 } else {
777 v.push(format!("{}-{}", lo, hi));
778 }
779 }
780 let mut result = Vec::new();
783 let mut shift = 0;
790 while mask != 0 {
791 let zeros = mask.trailing_zeros();
792 mask >>= zeros;
793 shift += zeros;
794 let ones = mask.trailing_ones();
795 append(&mut result, shift, shift + ones - 1);
796 shift += ones;
797 if ones == 64 {
798 break;
801 }
802 mask >>= ones;
803 }
804 result.join(",")
805}
806
807impl std::fmt::Display for Protocols {
817 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
818 let mut entries = Vec::new();
819 for (idx, mask) in self.0.recognized.iter().enumerate() {
820 if *mask != 0 {
821 let pk: ProtoKind = (idx as u8).into();
822 entries.push(format!("{}={}", pk, dumpmask(*mask)));
823 }
824 }
825 for ent in &self.0.unrecognized {
826 if ent.supported != 0 {
827 entries.push(format!(
828 "{}={}",
829 ent.proto.to_str(),
830 dumpmask(ent.supported)
831 ));
832 }
833 }
834 entries.sort();
836 write!(f, "{}", entries.join(" "))
837 }
838}
839
840impl FromIterator<NamedSubver> for Protocols {
841 fn from_iter<T: IntoIterator<Item = NamedSubver>>(iter: T) -> Self {
842 let mut r = ProtocolsInner::default();
843 for named_subver in iter {
844 let proto_idx = usize::from(named_subver.kind.get());
845 let proto_ver = named_subver.version;
846
847 assert!(proto_idx < N_RECOGNIZED);
849 assert!(usize::from(proto_ver) <= MAX_VER);
850 r.recognized[proto_idx] |= 1_u64 << proto_ver;
851 }
852 Protocols::from(r)
853 }
854}
855
856pub mod doc_supported {}
876
877pub mod doc_changing {}
911
912#[cfg(test)]
913mod test {
914 #![allow(clippy::bool_assert_comparison)]
916 #![allow(clippy::clone_on_copy)]
917 #![allow(clippy::dbg_macro)]
918 #![allow(clippy::mixed_attributes_style)]
919 #![allow(clippy::print_stderr)]
920 #![allow(clippy::print_stdout)]
921 #![allow(clippy::single_char_pattern)]
922 #![allow(clippy::unwrap_used)]
923 #![allow(clippy::unchecked_time_subtraction)]
924 #![allow(clippy::useless_vec)]
925 #![allow(clippy::needless_pass_by_value)]
926 #![allow(clippy::string_slice)] use std::str::FromStr;
929
930 use super::*;
931
932 #[test]
933 fn test_bitrange() {
934 assert_eq!(0b1, bitrange(0, 0));
935 assert_eq!(0b10, bitrange(1, 1));
936 assert_eq!(0b11, bitrange(0, 1));
937 assert_eq!(0b1111110000000, bitrange(7, 12));
938 assert_eq!(!0, bitrange(0, 63));
939 }
940
941 #[test]
942 fn test_dumpmask() {
943 assert_eq!("", dumpmask(0));
944 assert_eq!("0-5", dumpmask(0b111111));
945 assert_eq!("4-5", dumpmask(0b110000));
946 assert_eq!("1,4-5", dumpmask(0b110010));
947 assert_eq!("0-63", dumpmask(!0));
948 }
949
950 #[test]
951 fn test_canonical() -> Result<(), ParseError> {
952 fn t(orig: &str, canonical: &str) -> Result<(), ParseError> {
953 let protos: Protocols = orig.parse()?;
954 let enc = format!("{}", protos);
955 assert_eq!(enc, canonical);
956 Ok(())
957 }
958
959 t("", "")?;
960 t(" ", "")?;
961 t("Link=5,6,7,9 Relay=4-7,2", "Link=5-7,9 Relay=2,4-7")?;
962 t("FlowCtrl= Padding=8,7 Desc=1-5,6-8", "Desc=1-8 Padding=7-8")?;
963 t("Zelda=7 Gannon=3,6 Link=4", "Gannon=3,6 Link=4 Zelda=7")?;
964
965 Ok(())
966 }
967
968 #[test]
969 fn test_invalid() {
970 fn t(s: &str) -> ParseError {
971 let protos: Result<Protocols, ParseError> = s.parse();
972 assert!(protos.is_err());
973 protos.err().unwrap()
974 }
975
976 assert_eq!(t("Link=1-100"), ParseError::OutOfRange);
977 assert_eq!(t("Zelda=100"), ParseError::OutOfRange);
978 assert_eq!(t("Link=100-200"), ParseError::OutOfRange);
979
980 assert_eq!(t("Link=1,1"), ParseError::Duplicate);
981 assert_eq!(t("Link=1 Link=1"), ParseError::Duplicate);
982 assert_eq!(t("Link=1 Link=3"), ParseError::Duplicate);
983 assert_eq!(t("Zelda=1 Zelda=3"), ParseError::Duplicate);
984
985 assert_eq!(t("Link=Zelda"), ParseError::Malformed);
986 assert_eq!(t("Link=6-2"), ParseError::Malformed);
987 assert_eq!(t("Link=6-"), ParseError::Malformed);
988 assert_eq!(t("Link=6-,2"), ParseError::Malformed);
989 assert_eq!(t("Link=1,,2"), ParseError::Malformed);
990 assert_eq!(t("Link=6-frog"), ParseError::Malformed);
991 assert_eq!(t("Link=gannon-9"), ParseError::Malformed);
992 assert_eq!(t("Link Zelda"), ParseError::Malformed);
993
994 assert_eq!(t("Link=01"), ParseError::Malformed);
995 assert_eq!(t("Link=waffle"), ParseError::Malformed);
996 assert_eq!(t("Link=1_1"), ParseError::Malformed);
997 }
998
999 #[test]
1000 fn test_ctor_compat() {
1001 for (loose, strict) in &[
1002 ("Link=0-10 Link=25-30", "Link=1-10"),
1004 ("Link=25-30 Link=0-10", "Link=25-30"),
1005 ("Zelda=6-8 Link=1-10 Zelda=6-10", "Link=1-10 Zelda=6-8"),
1006 ("LinkAuth=0-00040", "LinkAuth=1-40"),
1008 ("Foo=0,3,004,5,0010", "Foo=3-5,10"),
1009 ("Foo=1-10,2-20,3-5", "Foo=1-20"),
1011 ("Link=1-10,2-20,3-5", "Link=1-20"),
1012 ] {
1013 let p1 = Protocols::from_str_c_compatible(loose).unwrap();
1014 let p2 = Protocols::from_str(strict).unwrap();
1015 assert_eq!(p1, p2);
1016
1017 assert!(Protocols::from_str(loose).is_err());
1018 }
1019 }
1020
1021 #[test]
1022 fn test_supports() -> Result<(), ParseError> {
1023 let p: Protocols = "Link=4,5-7 Padding=2 Lonk=1-3,5".parse()?;
1024
1025 assert!(p.supports_known_subver(ProtoKind::Padding, 2));
1026 assert!(!p.supports_known_subver(ProtoKind::Padding, 1));
1027 assert!(p.supports_known_subver(ProtoKind::Link, 6));
1028 assert!(!p.supports_known_subver(ProtoKind::Link, 255));
1029 assert!(!p.supports_known_subver(ProtoKind::Cons, 1));
1030 assert!(!p.supports_known_subver(ProtoKind::Cons, 0));
1031 assert!(p.supports_subver("Link", 6));
1032 assert!(!p.supports_subver("link", 6));
1033 assert!(!p.supports_subver("Cons", 0));
1034 assert!(p.supports_subver("Lonk", 3));
1035 assert!(!p.supports_subver("Lonk", 4));
1036 assert!(!p.supports_subver("lonk", 3));
1037 assert!(!p.supports_subver("Lonk", 64));
1038
1039 Ok(())
1040 }
1041
1042 #[test]
1043 fn test_difference() -> Result<(), ParseError> {
1044 let p1: Protocols = "Link=1-10 Desc=5-10 Relay=1,3,5,7,9 Other=7-60 Mine=1-20".parse()?;
1045 let p2: Protocols = "Link=3-4 Desc=1-6 Relay=2-6 Other=8 Theirs=20".parse()?;
1046
1047 assert_eq!(
1048 p1.difference(&p2),
1049 Protocols::from_str("Link=1-2,5-10 Desc=7-10 Relay=1,7,9 Other=7,9-60 Mine=1-20")?
1050 );
1051 assert_eq!(
1052 p2.difference(&p1),
1053 Protocols::from_str("Desc=1-4 Relay=2,4,6 Theirs=20")?,
1054 );
1055
1056 let nil = Protocols::default();
1057 assert_eq!(p1.difference(&nil), p1);
1058 assert_eq!(p2.difference(&nil), p2);
1059 assert_eq!(nil.difference(&p1), nil);
1060 assert_eq!(nil.difference(&p2), nil);
1061
1062 Ok(())
1063 }
1064
1065 #[test]
1066 fn test_union() -> Result<(), ParseError> {
1067 let p1: Protocols = "Link=1-10 Desc=5-10 Relay=1,3,5,7,9 Other=7-60 Mine=1-20".parse()?;
1068 let p2: Protocols = "Link=3-4 Desc=1-6 Relay=2-6 Other=2,8 Theirs=20".parse()?;
1069
1070 assert_eq!(
1071 p1.union(&p2),
1072 Protocols::from_str(
1073 "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
1074 )?
1075 );
1076 assert_eq!(
1077 p2.union(&p1),
1078 Protocols::from_str(
1079 "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
1080 )?
1081 );
1082
1083 let nil = Protocols::default();
1084 assert_eq!(p1.union(&nil), p1);
1085 assert_eq!(p2.union(&nil), p2);
1086 assert_eq!(nil.union(&p1), p1);
1087 assert_eq!(nil.union(&p2), p2);
1088
1089 Ok(())
1090 }
1091
1092 #[test]
1093 fn test_intersection() -> Result<(), ParseError> {
1094 let p1: Protocols = "Link=1-10 Desc=5-10 Relay=1,3,5,7,9 Other=7-60 Mine=1-20".parse()?;
1095 let p2: Protocols = "Link=3-4 Desc=1-6 Relay=2-6 Other=2,8 Theirs=20".parse()?;
1096
1097 assert_eq!(
1098 p1.intersection(&p2),
1099 Protocols::from_str("Link=3-4 Desc=5-6 Relay=3,5 Other=8")?
1100 );
1101 assert_eq!(
1102 p2.intersection(&p1),
1103 Protocols::from_str("Link=3-4 Desc=5-6 Relay=3,5 Other=8")?
1104 );
1105
1106 let nil = Protocols::default();
1107 assert_eq!(p1.intersection(&nil), nil);
1108 assert_eq!(p2.intersection(&nil), nil);
1109 assert_eq!(nil.intersection(&p1), nil);
1110 assert_eq!(nil.intersection(&p2), nil);
1111
1112 Ok(())
1113 }
1114
1115 #[test]
1116 fn from_iter() {
1117 use named as n;
1118 let empty: [NamedSubver; 0] = [];
1119 let prs: Protocols = empty.iter().copied().collect();
1120 assert_eq!(prs, Protocols::default());
1121 let prs: Protocols = empty.into_iter().collect();
1122 assert_eq!(prs, Protocols::default());
1123
1124 let prs = [
1125 n::LINK_V3,
1126 n::HSDIR_V3,
1127 n::LINK_V4,
1128 n::LINK_V5,
1129 n::CONFLUX_BASE,
1130 ]
1131 .into_iter()
1132 .collect::<Protocols>();
1133 assert_eq!(prs, "Link=3-5 HSDir=2 Conflux=1".parse().unwrap());
1134 }
1135
1136 #[test]
1137 fn order_numbered_subvers() {
1138 assert!(NumberedSubver::new(5, 7) < NumberedSubver::new(7, 5));
1140 assert!(NumberedSubver::new(7, 5) < NumberedSubver::new(7, 6));
1141 assert!(NumberedSubver::new(7, 6) < NumberedSubver::new(8, 6));
1142 }
1143}