1use std::fmt::{self, Debug, Display};
10use std::net::SocketAddr;
11use std::slice;
12use std::str::FromStr;
13
14use itertools::Either;
15use safelog::Redactable;
16use serde::{Deserialize, Serialize};
17use thiserror::Error;
18
19use crate::HasAddrs;
20
21#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
33pub struct TransportId(Inner);
34
35#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
39enum Inner {
40 #[default]
42 BuiltIn,
43
44 #[cfg(feature = "pt-client")]
46 Pluggable(PtTransportName),
47}
48
49#[derive(
58 Debug,
59 Clone,
60 Default,
61 Eq,
62 PartialEq,
63 Hash,
64 serde_with::DeserializeFromStr,
65 serde_with::SerializeDisplay,
66)]
67pub struct PtTransportName(String);
68
69impl FromStr for PtTransportName {
70 type Err = TransportIdError;
71
72 fn from_str(s: &str) -> Result<Self, Self::Err> {
73 s.to_string().try_into()
74 }
75}
76
77impl TryFrom<String> for PtTransportName {
78 type Error = TransportIdError;
79
80 fn try_from(s: String) -> Result<PtTransportName, Self::Error> {
81 if is_well_formed_id(&s) {
82 Ok(PtTransportName(s))
83 } else {
84 Err(TransportIdError::BadId(s))
85 }
86 }
87}
88
89impl Display for PtTransportName {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 Display::fmt(&self.0, f)
92 }
93}
94
95impl AsRef<str> for PtTransportName {
96 fn as_ref(&self) -> &str {
97 &self.0
98 }
99}
100
101const BUILT_IN_IDS: &[&str] = &["-", "", "bridge", "<none>"];
109
110impl FromStr for TransportId {
111 type Err = TransportIdError;
112
113 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 if BUILT_IN_IDS.contains(&s) {
115 return Ok(TransportId(Inner::BuiltIn));
116 };
117
118 #[cfg(feature = "pt-client")]
119 {
120 let name: PtTransportName = s.parse()?;
121 Ok(TransportId(Inner::Pluggable(name)))
122 }
123
124 #[cfg(not(feature = "pt-client"))]
125 Err(TransportIdError::NoSupport)
126 }
127}
128
129impl Display for TransportId {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 match &self.0 {
132 Inner::BuiltIn => write!(f, "{}", BUILT_IN_IDS[0]),
133 #[cfg(feature = "pt-client")]
134 Inner::Pluggable(name) => write!(f, "{}", name),
135 }
136 }
137}
138
139#[cfg(feature = "pt-client")]
140impl From<PtTransportName> for TransportId {
141 fn from(name: PtTransportName) -> Self {
142 TransportId(Inner::Pluggable(name))
143 }
144}
145
146fn is_well_formed_id(s: &str) -> bool {
152 let mut bytes = s.bytes();
155
156 if let Some(first) = bytes.next() {
157 (first.is_ascii_alphabetic() || first == b'_')
158 && bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
159 && !s.eq_ignore_ascii_case("bridge")
160 } else {
161 false
162 }
163}
164
165#[derive(Clone, Debug, thiserror::Error)]
167#[non_exhaustive]
168pub enum TransportIdError {
169 #[error("Not compiled with pluggable transport support")]
172 NoSupport,
173
174 #[error("{0:?} is not a valid pluggable transport ID")]
176 BadId(String),
177}
178
179impl TransportId {
180 pub fn new_builtin() -> Self {
184 TransportId(Inner::BuiltIn)
185 }
186
187 #[cfg(feature = "pt-client")]
191 pub fn new_pluggable(pt: PtTransportName) -> Self {
192 pt.into()
193 }
194
195 pub fn is_builtin(&self) -> bool {
197 self.0 == Inner::BuiltIn
198 }
199
200 #[cfg(feature = "pt-client")]
205 pub fn as_pluggable(&self) -> Option<&PtTransportName> {
206 match &self.0 {
207 Inner::BuiltIn => None,
208 #[cfg(feature = "pt-client")]
209 Inner::Pluggable(pt) => Some(pt),
210 }
211 }
212
213 #[cfg(feature = "pt-client")]
218 pub fn into_pluggable(self) -> Option<PtTransportName> {
219 match self.0 {
220 Inner::BuiltIn => None,
221 #[cfg(feature = "pt-client")]
222 Inner::Pluggable(pt) => Some(pt),
223 }
224 }
225}
226
227const NONE_ADDR: &str = "-";
229
230#[derive(
241 Clone, Debug, PartialEq, Eq, Hash, serde_with::DeserializeFromStr, serde_with::SerializeDisplay,
242)]
243#[non_exhaustive]
244pub enum PtTargetAddr {
245 IpPort(SocketAddr),
249 HostPort(String, u16),
251 None,
253}
254
255#[derive(
266 Clone,
267 Debug,
268 PartialEq,
269 Eq,
270 Hash,
271 serde_with::DeserializeFromStr,
272 serde_with::SerializeDisplay,
273 derive_more::Display,
274)]
275pub struct BridgeAddr(BridgeAddrInner<SocketAddr, String>);
276
277#[derive(Clone, Debug, PartialEq, Eq, Hash)]
283enum BridgeAddrInner<SA, HN> {
284 IpPort(SA),
286 HostPort(HN, u16),
288}
289
290impl BridgeAddr {
298 pub fn new_addr_from_sockaddr(sa: SocketAddr) -> Self {
300 BridgeAddr(BridgeAddrInner::IpPort(sa))
301 }
302
303 pub fn as_socketaddr(&self) -> Option<&SocketAddr> {
305 match &self.0 {
306 BridgeAddrInner::IpPort(sa) => Some(sa),
307 BridgeAddrInner::HostPort(..) => None,
308 }
309 }
310
311 pub fn new_named_host_port(hostname: impl Into<String>, port: u16) -> Self {
313 BridgeAddr(BridgeAddrInner::HostPort(hostname.into(), port))
314 }
315
316 pub fn as_host_port(&self) -> Option<(&str, u16)> {
318 match &self.0 {
319 BridgeAddrInner::IpPort(..) => None,
320 BridgeAddrInner::HostPort(hn, port) => Some((hn, *port)),
321 }
322 }
323}
324
325impl From<PtTargetAddr> for Option<BridgeAddr> {
326 fn from(pt: PtTargetAddr) -> Option<BridgeAddr> {
327 match pt {
328 PtTargetAddr::IpPort(sa) => Some(BridgeAddrInner::IpPort(sa)),
329 PtTargetAddr::HostPort(hn, p) => Some(BridgeAddrInner::HostPort(hn, p)),
330 PtTargetAddr::None => None,
331 }
332 .map(BridgeAddr)
333 }
334}
335impl From<Option<BridgeAddr>> for PtTargetAddr {
336 fn from(pt: Option<BridgeAddr>) -> PtTargetAddr {
337 match pt.map(|ba| ba.0) {
338 Some(BridgeAddrInner::IpPort(sa)) => PtTargetAddr::IpPort(sa),
339 Some(BridgeAddrInner::HostPort(hn, p)) => PtTargetAddr::HostPort(hn, p),
340 None => PtTargetAddr::None,
341 }
342 }
343}
344
345#[derive(Clone, Debug, thiserror::Error)]
347#[non_exhaustive]
348pub enum BridgeAddrError {
349 #[error("Not compiled with pluggable transport support.")]
351 NoSupport,
352 #[error("Cannot parse {0:?} as an address.")]
354 BadAddress(String),
355}
356
357impl FromStr for BridgeAddr {
358 type Err = BridgeAddrError;
359
360 fn from_str(s: &str) -> Result<Self, Self::Err> {
361 Ok(BridgeAddr(if let Ok(addr) = s.parse() {
362 BridgeAddrInner::IpPort(addr)
363 } else if let Some((name, port)) = s.rsplit_once(':') {
364 let port = port
365 .parse()
366 .map_err(|_| BridgeAddrError::BadAddress(s.to_string()))?;
367
368 BridgeAddrInner::HostPort(name.to_string(), port)
369 } else {
370 return Err(BridgeAddrError::BadAddress(s.to_string()));
371 }))
372 }
373}
374
375impl FromStr for PtTargetAddr {
376 type Err = BridgeAddrError;
377
378 fn from_str(s: &str) -> Result<Self, Self::Err> {
379 Ok(if s == NONE_ADDR {
380 PtTargetAddr::None
381 } else {
382 Some(BridgeAddr::from_str(s)?).into()
383 })
384 }
385}
386
387impl PtTargetAddr {
388 fn as_bridge_ref(&self) -> Option<BridgeAddrInner<&SocketAddr, &str>> {
395 match self {
396 PtTargetAddr::IpPort(addr) => Some(BridgeAddrInner::IpPort(addr)),
397 PtTargetAddr::HostPort(host, port) => Some(BridgeAddrInner::HostPort(host, *port)),
398 PtTargetAddr::None => None,
399 }
400 }
401}
402
403impl<SA: Display, HN: Display> Display for BridgeAddrInner<SA, HN> {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 match self {
406 BridgeAddrInner::IpPort(addr) => write!(f, "{}", addr),
407 BridgeAddrInner::HostPort(host, port) => write!(f, "{}:{}", host, port),
408 }
409 }
410}
411
412impl Display for PtTargetAddr {
415 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416 match self.as_bridge_ref() {
417 Some(b) => write!(f, "{}", b),
418 None => write!(f, "{}", NONE_ADDR),
419 }
420 }
421}
422
423impl<SA: Debug + Redactable, HN: Debug + Display + AsRef<str>> Redactable
424 for BridgeAddrInner<SA, HN>
425{
426 fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427 match self {
428 BridgeAddrInner::IpPort(a) => a.display_redacted(f),
429 BridgeAddrInner::HostPort(host, port) => write!(f, "{}…:{}", &host.as_ref()[..2], port),
430 }
431 }
432}
433
434impl Redactable for BridgeAddr {
435 fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
436 self.0.display_redacted(f)
437 }
438}
439
440impl Redactable for PtTargetAddr {
441 fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442 match self.as_bridge_ref() {
443 Some(b) => b.display_redacted(f),
444 None => write!(f, "{}", NONE_ADDR),
445 }
446 }
447}
448
449#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
459#[serde(into = "Vec<(String, String)>", try_from = "Vec<(String, String)>")]
460pub struct PtTargetSettings {
461 settings: Vec<(String, String)>,
463}
464
465impl PtTargetSettings {
466 fn check_setting(k: &str, v: &str) -> Result<(), PtTargetInvalidSetting> {
468 if k.find(|c: char| c == '=' || c.is_whitespace()).is_some() {
473 return Err(PtTargetInvalidSetting::Key(k.to_string()));
474 }
475 if v.find(|c: char| c.is_whitespace()).is_some() {
476 return Err(PtTargetInvalidSetting::Value(v.to_string()));
477 }
478 Ok(())
479 }
480
481 fn push_setting(
483 &mut self,
484 k: impl Into<String>,
485 v: impl Into<String>,
486 ) -> Result<(), PtTargetInvalidSetting> {
487 let k = k.into();
488 let v = v.into();
489 Self::check_setting(&k, &v)?;
490 self.settings.push((k, v));
491 Ok(())
492 }
493
494 pub fn into_inner(self) -> Vec<(String, String)> {
496 self.settings
497 }
498}
499
500impl TryFrom<Vec<(String, String)>> for PtTargetSettings {
501 type Error = PtTargetInvalidSetting;
502
503 fn try_from(settings: Vec<(String, String)>) -> Result<Self, Self::Error> {
504 for (k, v) in settings.iter() {
505 Self::check_setting(k, v)?;
506 }
507 Ok(Self { settings })
508 }
509}
510
511impl From<PtTargetSettings> for Vec<(String, String)> {
512 fn from(settings: PtTargetSettings) -> Self {
513 settings.settings
514 }
515}
516
517#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
520pub struct PtTarget {
521 transport: PtTransportName,
523 addr: PtTargetAddr,
525 #[serde(default)]
527 settings: PtTargetSettings,
528}
529
530#[derive(Error, Clone, Debug, Eq, PartialEq)]
532#[non_exhaustive]
533pub enum PtTargetInvalidSetting {
534 #[error("key {0:?} has invalid or unsupported syntax")]
539 Key(String),
540
541 #[error("value {0:?} has invalid or unsupported syntax")]
546 Value(String),
547}
548
549impl PtTarget {
550 pub fn new(transport: PtTransportName, addr: PtTargetAddr) -> Self {
552 PtTarget {
553 transport,
554 addr,
555 settings: Default::default(),
556 }
557 }
558
559 pub fn push_setting(
561 &mut self,
562 k: impl Into<String>,
563 v: impl Into<String>,
564 ) -> Result<(), PtTargetInvalidSetting> {
565 self.settings.push_setting(k, v)
566 }
567
568 pub fn transport(&self) -> &PtTransportName {
570 &self.transport
571 }
572
573 pub fn addr(&self) -> &PtTargetAddr {
575 &self.addr
576 }
577
578 pub fn settings(&self) -> impl Iterator<Item = (&str, &str)> {
580 self.settings.settings.iter().map(|(k, v)| (&**k, &**v))
581 }
582
583 pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
593 match self {
594 PtTarget {
595 addr: PtTargetAddr::IpPort(addr),
596 ..
597 } => Some(std::slice::from_ref(addr)),
598
599 _ => None,
600 }
601 }
602
603 pub fn into_parts(self) -> (PtTransportName, PtTargetAddr, PtTargetSettings) {
605 (self.transport, self.addr, self.settings)
606 }
607}
608
609impl Display for PtTarget {
610 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
611 write!(
612 f,
613 "{} @ {} with {:?}",
614 self.transport, self.addr, self.settings
615 )
616 }
617}
618
619#[derive(Clone, Debug, Eq, PartialEq, Hash)]
625#[non_exhaustive]
626pub enum ChannelMethod {
627 Direct(Vec<std::net::SocketAddr>),
629
630 #[cfg(feature = "pt-client")]
632 Pluggable(PtTarget),
633}
634
635impl ChannelMethod {
636 pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
646 match self {
647 ChannelMethod::Direct(addr) => Some(addr.as_ref()),
648
649 #[cfg(feature = "pt-client")]
650 ChannelMethod::Pluggable(t) => t.socket_addrs(),
651 }
652 }
653
654 pub fn target_addr(&self) -> Option<PtTargetAddr> {
658 match self {
659 ChannelMethod::Direct(addr) if !addr.is_empty() => Some(PtTargetAddr::IpPort(addr[0])),
660
661 #[cfg(feature = "pt-client")]
662 ChannelMethod::Pluggable(PtTarget { addr, .. }) => Some(addr.clone()),
663
664 _ => None,
665 }
666 }
667
668 pub fn unique_direct_addr(&self) -> Option<SocketAddr> {
678 match self {
679 Self::Direct(addrs) => match addrs.as_slice() {
680 [addr] => Some(*addr),
681 _ => None,
683 },
684 #[cfg(feature = "pt-client")]
685 Self::Pluggable(_) => None,
686 }
687 }
688
689 pub fn is_direct(&self) -> bool {
691 matches!(self, ChannelMethod::Direct(_))
692 }
693
694 pub fn transport_id(&self) -> TransportId {
696 match self {
697 ChannelMethod::Direct(_) => TransportId::default(),
698 #[cfg(feature = "pt-client")]
699 ChannelMethod::Pluggable(target) => target.transport().clone().into(),
700 }
701 }
702
703 pub fn retain_addrs<P>(&mut self, pred: P) -> Result<(), RetainAddrsError>
711 where
712 P: Fn(&std::net::SocketAddr) -> bool,
713 {
714 #[cfg(feature = "pt-client")]
715 use PtTargetAddr as Pt;
716
717 match self {
718 ChannelMethod::Direct(d) if d.is_empty() => {}
719 ChannelMethod::Direct(d) => {
720 d.retain(pred);
721 if d.is_empty() {
722 return Err(RetainAddrsError::NoAddrsLeft);
723 }
724 }
725 #[cfg(feature = "pt-client")]
726 ChannelMethod::Pluggable(PtTarget { addr, .. }) => match addr {
727 Pt::IpPort(a) => {
728 if !pred(a) {
729 *addr = Pt::None;
730 return Err(RetainAddrsError::NoAddrsLeft);
731 }
732 }
733 Pt::HostPort(_, _) => {}
734 Pt::None => {}
735 },
736 }
737 Ok(())
738 }
739
740 pub fn contained_by(&self, other: &ChannelMethod) -> bool {
743 use ChannelMethod as CM;
744 match (self, other) {
745 (CM::Direct(our_addrs), CM::Direct(their_addrs)) => {
746 our_addrs.iter().all(|a| their_addrs.contains(a))
747 }
748 #[cfg(feature = "pt-client")]
749 (CM::Pluggable(our_target), CM::Pluggable(their_target)) => our_target == their_target,
750 #[cfg(feature = "pt-client")]
751 (_, _) => false,
752 }
753 }
754}
755
756#[derive(Clone, Debug, thiserror::Error)]
758pub enum RetainAddrsError {
759 #[error("All addresses were removed.")]
761 NoAddrsLeft,
762}
763
764impl HasAddrs for PtTargetAddr {
765 fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
766 match self {
767 PtTargetAddr::IpPort(sockaddr) => slice::from_ref(sockaddr),
768 PtTargetAddr::HostPort(..) | PtTargetAddr::None => &[],
769 }
770 .iter()
771 .copied()
772 }
773}
774
775impl HasAddrs for ChannelMethod {
776 fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
777 let r = match self {
778 ChannelMethod::Direct(addrs) => Either::Left(addrs.iter().copied()),
779 #[cfg(feature = "pt-client")]
780 ChannelMethod::Pluggable(pt) => Either::Right(pt.addr.addrs()),
781 };
782
783 #[cfg(not(feature = "pt-client"))]
787 let _: &Either<_, std::iter::Empty<_>> = &r;
788
789 r
790 }
791}
792
793#[cfg(test)]
794mod test {
795 #![allow(clippy::bool_assert_comparison)]
797 #![allow(clippy::clone_on_copy)]
798 #![allow(clippy::dbg_macro)]
799 #![allow(clippy::mixed_attributes_style)]
800 #![allow(clippy::print_stderr)]
801 #![allow(clippy::print_stdout)]
802 #![allow(clippy::single_char_pattern)]
803 #![allow(clippy::unwrap_used)]
804 #![allow(clippy::unchecked_time_subtraction)]
805 #![allow(clippy::useless_vec)]
806 #![allow(clippy::needless_pass_by_value)]
807 use super::*;
809 use itertools::Itertools;
810
811 #[test]
812 fn builtin() {
813 assert!(TransportId::default().is_builtin());
814 assert_eq!(
815 TransportId::default(),
816 "<none>".parse().expect("Couldn't parse default ID")
817 );
818 }
819
820 #[test]
821 #[cfg(not(feature = "pt-client"))]
822 fn nosupport() {
823 assert!(matches!(
825 TransportId::from_str("obfs4"),
826 Err(TransportIdError::NoSupport)
827 ));
828 }
829
830 #[test]
831 #[cfg(feature = "pt-client")]
832 fn wellformed() {
833 for id in &["snowflake", "obfs4", "_ohai", "Z", "future_WORK2"] {
834 assert!(is_well_formed_id(id));
835 }
836
837 for id in &[" ", "Mölm", "12345", ""] {
838 assert!(!is_well_formed_id(id));
839 }
840 }
841
842 #[test]
843 #[cfg(feature = "pt-client")]
844 fn parsing() {
845 let obfs = TransportId::from_str("obfs4").unwrap();
846 let dflt = TransportId::default();
847 let dflt2 = TransportId::from_str("<none>").unwrap();
848 let dflt3 = TransportId::from_str("-").unwrap();
849 let dflt4 = TransportId::from_str("").unwrap();
850 let dflt5 = TransportId::from_str("bridge").unwrap();
851 let snow = TransportId::from_str("snowflake").unwrap();
852 let obfs_again = TransportId::from_str("obfs4").unwrap();
853
854 assert_eq!(obfs, obfs_again);
855 assert_eq!(dflt, dflt2);
856 assert_eq!(dflt, dflt3);
857 assert_eq!(dflt, dflt4);
858 assert_eq!(dflt, dflt5);
859 assert_ne!(snow, obfs);
860 assert_ne!(snow, dflt);
861
862 assert_eq!(dflt.to_string(), "-");
863
864 assert!(matches!(
865 TransportId::from_str("12345"),
866 Err(TransportIdError::BadId(_))
867 ));
868 assert!(matches!(
869 TransportId::from_str("Bridge"),
870 Err(TransportIdError::BadId(_))
871 ));
872 }
873
874 #[test]
875 fn addr() {
876 let chk_bridge_addr = |a: &PtTargetAddr, addr: &str| {
877 let ba: BridgeAddr = addr.parse().unwrap();
878 assert_eq!(&ba.to_string(), addr);
879
880 assert_eq!(&PtTargetAddr::from(Some(ba.clone())), a);
881 let reba: Option<BridgeAddr> = a.clone().into();
882 assert_eq!(reba.as_ref(), Some(&ba));
883 };
884
885 for addr in &["1.2.3.4:555", "[::1]:9999"] {
886 let a: PtTargetAddr = addr.parse().unwrap();
887 assert_eq!(&a.to_string(), addr);
888
889 let sa: SocketAddr = addr.parse().unwrap();
890 assert_eq!(a.addrs().collect_vec(), &[sa]);
891
892 chk_bridge_addr(&a, addr);
893 }
894
895 for addr in &["www.example.com:9100", "-"] {
896 let a: PtTargetAddr = addr.parse().unwrap();
897 assert_eq!(&a.to_string(), addr);
898 assert_eq!(a.addrs().collect_vec(), &[]);
899
900 if a == PtTargetAddr::None {
901 let e = BridgeAddr::from_str(addr).unwrap_err();
902 assert!(matches!(e, BridgeAddrError::BadAddress(_)));
903 } else {
904 chk_bridge_addr(&a, addr);
905 }
906 }
907
908 for addr in &["foobar", "<<<>>>"] {
909 let e = PtTargetAddr::from_str(addr).unwrap_err();
910 assert!(matches!(e, BridgeAddrError::BadAddress(_)));
911
912 let e = BridgeAddr::from_str(addr).unwrap_err();
913 assert!(matches!(e, BridgeAddrError::BadAddress(_)));
914 }
915 }
916
917 #[test]
918 fn transport_id() {
919 let id1: TransportId = "<none>".parse().unwrap();
920 assert!(id1.is_builtin());
921 assert_eq!(id1.to_string(), "-".to_string());
922
923 #[cfg(feature = "pt-client")]
924 {
925 let id2: TransportId = "obfs4".parse().unwrap();
926 assert_ne!(id2, id1);
927 assert!(!id2.is_builtin());
928 assert_eq!(id2.to_string(), "obfs4");
929
930 assert!(matches!(
931 TransportId::from_str("==="),
932 Err(TransportIdError::BadId(_))
933 ));
934 }
935
936 #[cfg(not(feature = "pt-client"))]
937 {
938 assert!(matches!(
939 TransportId::from_str("obfs4"),
940 Err(TransportIdError::NoSupport)
941 ));
942 }
943 }
944
945 #[test]
946 fn settings() {
947 let s = PtTargetSettings::try_from(vec![]).unwrap();
948 assert_eq!(Vec::<_>::from(s), vec![]);
949
950 let v = vec![("abc".into(), "def".into()), ("ghi".into(), "jkl".into())];
951 let s = PtTargetSettings::try_from(v.clone()).unwrap();
952 assert_eq!(Vec::<_>::from(s), v);
953
954 let v = vec![("a=b".into(), "def".into())];
955 let s = PtTargetSettings::try_from(v);
956 assert!(matches!(s, Err(PtTargetInvalidSetting::Key(_))));
957
958 let v = vec![("abc".into(), "d ef".into())];
959 let s = PtTargetSettings::try_from(v);
960 assert!(matches!(s, Err(PtTargetInvalidSetting::Value(_))));
961 }
962
963 #[test]
964 fn chanmethod_direct() {
965 let a1 = "127.0.0.1:8080".parse().unwrap();
966 let a2 = "127.0.0.2:8181".parse().unwrap();
967 let a3 = "127.0.0.3:8282".parse().unwrap();
968
969 let m = ChannelMethod::Direct(vec![a1, a2]);
970 assert_eq!(m.socket_addrs(), Some(&[a1, a2][..]));
971 assert_eq!((m.target_addr()), Some(PtTargetAddr::IpPort(a1)));
972 assert!(m.is_direct());
973 assert_eq!(m.transport_id(), TransportId::default());
974
975 let m2 = ChannelMethod::Direct(vec![a1, a2, a3]);
976 assert!(m.contained_by(&m));
977 assert!(m.contained_by(&m2));
978 assert!(!m2.contained_by(&m));
979
980 let mut m3 = m2.clone();
981 m3.retain_addrs(|a| a.port() != 8282).unwrap();
982 assert_eq!(m3, m);
983 assert_ne!(m3, m2);
984 }
985
986 #[test]
987 #[cfg(feature = "pt-client")]
988 fn chanmethod_pt() {
989 use itertools::Itertools;
990
991 let transport = "giraffe".parse().unwrap();
992 let addr1 = PtTargetAddr::HostPort("pt.example.com".into(), 1234);
993 let target1 = PtTarget::new("giraffe".parse().unwrap(), addr1.clone());
994 let m1 = ChannelMethod::Pluggable(target1);
995
996 let addr2 = PtTargetAddr::IpPort("127.0.0.1:567".parse().unwrap());
997 let target2 = PtTarget::new("giraffe".parse().unwrap(), addr2.clone());
998 let m2 = ChannelMethod::Pluggable(target2);
999
1000 let addr3 = PtTargetAddr::None;
1001 let target3 = PtTarget::new("giraffe".parse().unwrap(), addr3.clone());
1002 let m3 = ChannelMethod::Pluggable(target3);
1003
1004 assert_eq!(m1.socket_addrs(), None);
1005 assert_eq!(
1006 m2.socket_addrs(),
1007 Some(&["127.0.0.1:567".parse().unwrap()][..])
1008 );
1009 assert_eq!(m3.socket_addrs(), None);
1010
1011 assert_eq!(m1.target_addr(), Some(addr1));
1012 assert_eq!(m2.target_addr(), Some(addr2));
1013 assert_eq!(m3.target_addr(), Some(addr3));
1014
1015 assert!(!m1.is_direct());
1016 assert!(!m2.is_direct());
1017 assert!(!m3.is_direct());
1018
1019 assert_eq!(m1.transport_id(), transport);
1020 assert_eq!(m2.transport_id(), transport);
1021 assert_eq!(m3.transport_id(), transport);
1022
1023 for v in [&m1, &m2, &m3].iter().combinations(2) {
1024 let first = v[0];
1025 let second = v[1];
1026 assert_eq!(first.contained_by(second), first == second);
1027 }
1028
1029 let mut m1new = m1.clone();
1030 let mut m2new = m2.clone();
1031 let mut m3new = m3.clone();
1032 m1new.retain_addrs(|a| a.port() == 567).unwrap();
1034 m2new.retain_addrs(|a| a.port() == 567).unwrap();
1035 m3new.retain_addrs(|a| a.port() == 567).unwrap();
1036 assert_eq!(m1new, m1);
1037 assert_eq!(m2new, m2);
1038 assert_eq!(m3new, m3);
1039
1040 assert!(matches!(
1042 m2new.retain_addrs(|a| a.port() == 999),
1043 Err(RetainAddrsError::NoAddrsLeft)
1044 ));
1045 }
1046}