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
609#[derive(Clone, Debug, Eq, PartialEq, Hash)]
615#[non_exhaustive]
616pub enum ChannelMethod {
617 Direct(Vec<std::net::SocketAddr>),
619
620 #[cfg(feature = "pt-client")]
622 Pluggable(PtTarget),
623}
624
625impl ChannelMethod {
626 pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
636 match self {
637 ChannelMethod::Direct(addr) => Some(addr.as_ref()),
638
639 #[cfg(feature = "pt-client")]
640 ChannelMethod::Pluggable(t) => t.socket_addrs(),
641 }
642 }
643
644 pub fn target_addr(&self) -> Option<PtTargetAddr> {
648 match self {
649 ChannelMethod::Direct(addr) if !addr.is_empty() => Some(PtTargetAddr::IpPort(addr[0])),
650
651 #[cfg(feature = "pt-client")]
652 ChannelMethod::Pluggable(PtTarget { addr, .. }) => Some(addr.clone()),
653
654 _ => None,
655 }
656 }
657
658 pub fn unique_direct_addr(&self) -> Option<SocketAddr> {
668 match self {
669 Self::Direct(addrs) => match addrs.as_slice() {
670 [addr] => Some(*addr),
671 _ => None,
673 },
674 #[cfg(feature = "pt-client")]
675 _ => None,
676 }
677 }
678
679 pub fn is_direct(&self) -> bool {
681 matches!(self, ChannelMethod::Direct(_))
682 }
683
684 pub fn transport_id(&self) -> TransportId {
686 match self {
687 ChannelMethod::Direct(_) => TransportId::default(),
688 #[cfg(feature = "pt-client")]
689 ChannelMethod::Pluggable(target) => target.transport().clone().into(),
690 }
691 }
692
693 pub fn retain_addrs<P>(&mut self, pred: P) -> Result<(), RetainAddrsError>
701 where
702 P: Fn(&std::net::SocketAddr) -> bool,
703 {
704 #[cfg(feature = "pt-client")]
705 use PtTargetAddr as Pt;
706
707 match self {
708 ChannelMethod::Direct(d) if d.is_empty() => {}
709 ChannelMethod::Direct(d) => {
710 d.retain(pred);
711 if d.is_empty() {
712 return Err(RetainAddrsError::NoAddrsLeft);
713 }
714 }
715 #[cfg(feature = "pt-client")]
716 ChannelMethod::Pluggable(PtTarget { addr, .. }) => match addr {
717 Pt::IpPort(a) => {
718 if !pred(a) {
719 *addr = Pt::None;
720 return Err(RetainAddrsError::NoAddrsLeft);
721 }
722 }
723 Pt::HostPort(_, _) => {}
724 Pt::None => {}
725 },
726 }
727 Ok(())
728 }
729
730 pub fn contained_by(&self, other: &ChannelMethod) -> bool {
733 use ChannelMethod as CM;
734 match (self, other) {
735 (CM::Direct(our_addrs), CM::Direct(their_addrs)) => {
736 our_addrs.iter().all(|a| their_addrs.contains(a))
737 }
738 #[cfg(feature = "pt-client")]
739 (CM::Pluggable(our_target), CM::Pluggable(their_target)) => our_target == their_target,
740 #[cfg(feature = "pt-client")]
741 (_, _) => false,
742 }
743 }
744}
745
746#[derive(Clone, Debug, thiserror::Error)]
748pub enum RetainAddrsError {
749 #[error("All addresses were removed.")]
751 NoAddrsLeft,
752}
753
754impl HasAddrs for PtTargetAddr {
755 fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
756 match self {
757 PtTargetAddr::IpPort(sockaddr) => slice::from_ref(sockaddr),
758 PtTargetAddr::HostPort(..) | PtTargetAddr::None => &[],
759 }
760 .iter()
761 .copied()
762 }
763}
764
765impl HasAddrs for ChannelMethod {
766 fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
767 let r = match self {
768 ChannelMethod::Direct(addrs) => Either::Left(addrs.iter().copied()),
769 #[cfg(feature = "pt-client")]
770 ChannelMethod::Pluggable(pt) => Either::Right(pt.addr.addrs()),
771 };
772
773 #[cfg(not(feature = "pt-client"))]
777 let _: &Either<_, std::iter::Empty<_>> = &r;
778
779 r
780 }
781}
782
783#[cfg(test)]
784mod test {
785 #![allow(clippy::bool_assert_comparison)]
787 #![allow(clippy::clone_on_copy)]
788 #![allow(clippy::dbg_macro)]
789 #![allow(clippy::mixed_attributes_style)]
790 #![allow(clippy::print_stderr)]
791 #![allow(clippy::print_stdout)]
792 #![allow(clippy::single_char_pattern)]
793 #![allow(clippy::unwrap_used)]
794 #![allow(clippy::unchecked_time_subtraction)]
795 #![allow(clippy::useless_vec)]
796 #![allow(clippy::needless_pass_by_value)]
797 use super::*;
799 use itertools::Itertools;
800
801 #[test]
802 fn builtin() {
803 assert!(TransportId::default().is_builtin());
804 assert_eq!(
805 TransportId::default(),
806 "<none>".parse().expect("Couldn't parse default ID")
807 );
808 }
809
810 #[test]
811 #[cfg(not(feature = "pt-client"))]
812 fn nosupport() {
813 assert!(matches!(
815 TransportId::from_str("obfs4"),
816 Err(TransportIdError::NoSupport)
817 ));
818 }
819
820 #[test]
821 #[cfg(feature = "pt-client")]
822 fn wellformed() {
823 for id in &["snowflake", "obfs4", "_ohai", "Z", "future_WORK2"] {
824 assert!(is_well_formed_id(id));
825 }
826
827 for id in &[" ", "Mölm", "12345", ""] {
828 assert!(!is_well_formed_id(id));
829 }
830 }
831
832 #[test]
833 #[cfg(feature = "pt-client")]
834 fn parsing() {
835 let obfs = TransportId::from_str("obfs4").unwrap();
836 let dflt = TransportId::default();
837 let dflt2 = TransportId::from_str("<none>").unwrap();
838 let dflt3 = TransportId::from_str("-").unwrap();
839 let dflt4 = TransportId::from_str("").unwrap();
840 let dflt5 = TransportId::from_str("bridge").unwrap();
841 let snow = TransportId::from_str("snowflake").unwrap();
842 let obfs_again = TransportId::from_str("obfs4").unwrap();
843
844 assert_eq!(obfs, obfs_again);
845 assert_eq!(dflt, dflt2);
846 assert_eq!(dflt, dflt3);
847 assert_eq!(dflt, dflt4);
848 assert_eq!(dflt, dflt5);
849 assert_ne!(snow, obfs);
850 assert_ne!(snow, dflt);
851
852 assert_eq!(dflt.to_string(), "-");
853
854 assert!(matches!(
855 TransportId::from_str("12345"),
856 Err(TransportIdError::BadId(_))
857 ));
858 assert!(matches!(
859 TransportId::from_str("Bridge"),
860 Err(TransportIdError::BadId(_))
861 ));
862 }
863
864 #[test]
865 fn addr() {
866 let chk_bridge_addr = |a: &PtTargetAddr, addr: &str| {
867 let ba: BridgeAddr = addr.parse().unwrap();
868 assert_eq!(&ba.to_string(), addr);
869
870 assert_eq!(&PtTargetAddr::from(Some(ba.clone())), a);
871 let reba: Option<BridgeAddr> = a.clone().into();
872 assert_eq!(reba.as_ref(), Some(&ba));
873 };
874
875 for addr in &["1.2.3.4:555", "[::1]:9999"] {
876 let a: PtTargetAddr = addr.parse().unwrap();
877 assert_eq!(&a.to_string(), addr);
878
879 let sa: SocketAddr = addr.parse().unwrap();
880 assert_eq!(a.addrs().collect_vec(), &[sa]);
881
882 chk_bridge_addr(&a, addr);
883 }
884
885 for addr in &["www.example.com:9100", "-"] {
886 let a: PtTargetAddr = addr.parse().unwrap();
887 assert_eq!(&a.to_string(), addr);
888 assert_eq!(a.addrs().collect_vec(), &[]);
889
890 if a == PtTargetAddr::None {
891 let e = BridgeAddr::from_str(addr).unwrap_err();
892 assert!(matches!(e, BridgeAddrError::BadAddress(_)));
893 } else {
894 chk_bridge_addr(&a, addr);
895 }
896 }
897
898 for addr in &["foobar", "<<<>>>"] {
899 let e = PtTargetAddr::from_str(addr).unwrap_err();
900 assert!(matches!(e, BridgeAddrError::BadAddress(_)));
901
902 let e = BridgeAddr::from_str(addr).unwrap_err();
903 assert!(matches!(e, BridgeAddrError::BadAddress(_)));
904 }
905 }
906
907 #[test]
908 fn transport_id() {
909 let id1: TransportId = "<none>".parse().unwrap();
910 assert!(id1.is_builtin());
911 assert_eq!(id1.to_string(), "-".to_string());
912
913 #[cfg(feature = "pt-client")]
914 {
915 let id2: TransportId = "obfs4".parse().unwrap();
916 assert_ne!(id2, id1);
917 assert!(!id2.is_builtin());
918 assert_eq!(id2.to_string(), "obfs4");
919
920 assert!(matches!(
921 TransportId::from_str("==="),
922 Err(TransportIdError::BadId(_))
923 ));
924 }
925
926 #[cfg(not(feature = "pt-client"))]
927 {
928 assert!(matches!(
929 TransportId::from_str("obfs4"),
930 Err(TransportIdError::NoSupport)
931 ));
932 }
933 }
934
935 #[test]
936 fn settings() {
937 let s = PtTargetSettings::try_from(vec![]).unwrap();
938 assert_eq!(Vec::<_>::from(s), vec![]);
939
940 let v = vec![("abc".into(), "def".into()), ("ghi".into(), "jkl".into())];
941 let s = PtTargetSettings::try_from(v.clone()).unwrap();
942 assert_eq!(Vec::<_>::from(s), v);
943
944 let v = vec![("a=b".into(), "def".into())];
945 let s = PtTargetSettings::try_from(v);
946 assert!(matches!(s, Err(PtTargetInvalidSetting::Key(_))));
947
948 let v = vec![("abc".into(), "d ef".into())];
949 let s = PtTargetSettings::try_from(v);
950 assert!(matches!(s, Err(PtTargetInvalidSetting::Value(_))));
951 }
952
953 #[test]
954 fn chanmethod_direct() {
955 let a1 = "127.0.0.1:8080".parse().unwrap();
956 let a2 = "127.0.0.2:8181".parse().unwrap();
957 let a3 = "127.0.0.3:8282".parse().unwrap();
958
959 let m = ChannelMethod::Direct(vec![a1, a2]);
960 assert_eq!(m.socket_addrs(), Some(&[a1, a2][..]));
961 assert_eq!((m.target_addr()), Some(PtTargetAddr::IpPort(a1)));
962 assert!(m.is_direct());
963 assert_eq!(m.transport_id(), TransportId::default());
964
965 let m2 = ChannelMethod::Direct(vec![a1, a2, a3]);
966 assert!(m.contained_by(&m));
967 assert!(m.contained_by(&m2));
968 assert!(!m2.contained_by(&m));
969
970 let mut m3 = m2.clone();
971 m3.retain_addrs(|a| a.port() != 8282).unwrap();
972 assert_eq!(m3, m);
973 assert_ne!(m3, m2);
974 }
975
976 #[test]
977 #[cfg(feature = "pt-client")]
978 fn chanmethod_pt() {
979 use itertools::Itertools;
980
981 let transport = "giraffe".parse().unwrap();
982 let addr1 = PtTargetAddr::HostPort("pt.example.com".into(), 1234);
983 let target1 = PtTarget::new("giraffe".parse().unwrap(), addr1.clone());
984 let m1 = ChannelMethod::Pluggable(target1);
985
986 let addr2 = PtTargetAddr::IpPort("127.0.0.1:567".parse().unwrap());
987 let target2 = PtTarget::new("giraffe".parse().unwrap(), addr2.clone());
988 let m2 = ChannelMethod::Pluggable(target2);
989
990 let addr3 = PtTargetAddr::None;
991 let target3 = PtTarget::new("giraffe".parse().unwrap(), addr3.clone());
992 let m3 = ChannelMethod::Pluggable(target3);
993
994 assert_eq!(m1.socket_addrs(), None);
995 assert_eq!(
996 m2.socket_addrs(),
997 Some(&["127.0.0.1:567".parse().unwrap()][..])
998 );
999 assert_eq!(m3.socket_addrs(), None);
1000
1001 assert_eq!(m1.target_addr(), Some(addr1));
1002 assert_eq!(m2.target_addr(), Some(addr2));
1003 assert_eq!(m3.target_addr(), Some(addr3));
1004
1005 assert!(!m1.is_direct());
1006 assert!(!m2.is_direct());
1007 assert!(!m3.is_direct());
1008
1009 assert_eq!(m1.transport_id(), transport);
1010 assert_eq!(m2.transport_id(), transport);
1011 assert_eq!(m3.transport_id(), transport);
1012
1013 for v in [&m1, &m2, &m3].iter().combinations(2) {
1014 let first = v[0];
1015 let second = v[1];
1016 assert_eq!(first.contained_by(second), first == second);
1017 }
1018
1019 let mut m1new = m1.clone();
1020 let mut m2new = m2.clone();
1021 let mut m3new = m3.clone();
1022 m1new.retain_addrs(|a| a.port() == 567).unwrap();
1024 m2new.retain_addrs(|a| a.port() == 567).unwrap();
1025 m3new.retain_addrs(|a| a.port() == 567).unwrap();
1026 assert_eq!(m1new, m1);
1027 assert_eq!(m2new, m2);
1028 assert_eq!(m3new, m3);
1029
1030 assert!(matches!(
1032 m2new.retain_addrs(|a| a.port() == 999),
1033 Err(RetainAddrsError::NoAddrsLeft)
1034 ));
1035 }
1036}