1use std::net::SocketAddr;
9
10use crate::{EncodedLinkSpec, LinkSpec, OwnedChanTargetBuilder, RelayIdType};
11use itertools::Itertools as _;
12
13#[derive(Debug, Clone, Copy)]
19#[non_exhaustive]
20pub enum Strictness {
21 Standard,
28}
29
30impl OwnedChanTargetBuilder {
31 #[allow(unstable_name_collisions)]
38 pub fn from_linkspecs(
39 strictness: Strictness,
40 linkspecs: &[LinkSpec],
41 ) -> Result<Self, ChanTargetDecodeError> {
42 let _ = strictness;
44
45 let ed_id = linkspecs
47 .iter()
48 .filter_map(|ls| match ls {
49 LinkSpec::Ed25519Id(ed) => Some(ed),
50 _ => None,
51 })
52 .exactly_one()
53 .map_err(|mut e| match e.next() {
54 Some(_) => ChanTargetDecodeError::DuplicatedId(RelayIdType::Ed25519),
55 None => ChanTargetDecodeError::MissingId(RelayIdType::Ed25519),
56 })?;
57
58 let rsa_id = linkspecs
60 .iter()
61 .filter_map(|ls| match ls {
62 LinkSpec::RsaId(rsa) => Some(rsa),
63 _ => None,
64 })
65 .exactly_one()
66 .map_err(|mut e| match e.next() {
67 Some(_) => ChanTargetDecodeError::DuplicatedId(RelayIdType::Rsa),
68 None => ChanTargetDecodeError::MissingId(RelayIdType::Rsa),
69 })?;
70
71 let addrs: Vec<SocketAddr> = linkspecs
72 .iter()
73 .filter_map(|ls| match ls {
74 LinkSpec::OrPort(addr, port) => Some(SocketAddr::new(*addr, *port)),
75 _ => None,
76 })
77 .collect();
78 if !addrs.iter().any(|addr| addr.is_ipv4()) {
80 return Err(ChanTargetDecodeError::MissingAddr);
81 }
82 let mut builder = OwnedChanTargetBuilder::default();
83
84 builder
85 .ed_identity(*ed_id)
86 .rsa_identity(*rsa_id)
87 .addrs(addrs);
88 Ok(builder)
89 }
90
91 pub fn from_encoded_linkspecs(
94 strictness: Strictness,
95 linkspecs: &[EncodedLinkSpec],
96 ) -> Result<Self, ChanTargetDecodeError> {
97 let linkspecs_decoded = linkspecs
100 .iter()
101 .map(|ls| ls.parse())
102 .collect::<Result<Vec<_>, _>>()
103 .map_err(ChanTargetDecodeError::MisformedLinkSpec)?;
104 Self::from_linkspecs(strictness, &linkspecs_decoded)
105 }
106}
107
108#[derive(Clone, Debug, thiserror::Error)]
111#[non_exhaustive]
112pub enum ChanTargetDecodeError {
113 #[error("Missing a required {0} identity key")]
115 MissingId(RelayIdType),
116 #[error("Duplicated a {0} identity key")]
118 DuplicatedId(RelayIdType),
119 #[error("Missing a required address type")]
121 MissingAddr,
122 #[error("Mis-formatted link specifier")]
124 MisformedLinkSpec(#[source] tor_bytes::Error),
125}
126
127#[cfg(test)]
128mod test {
129 #![allow(clippy::bool_assert_comparison)]
131 #![allow(clippy::clone_on_copy)]
132 #![allow(clippy::dbg_macro)]
133 #![allow(clippy::mixed_attributes_style)]
134 #![allow(clippy::print_stderr)]
135 #![allow(clippy::print_stdout)]
136 #![allow(clippy::single_char_pattern)]
137 #![allow(clippy::unwrap_used)]
138 #![allow(clippy::unchecked_time_subtraction)]
139 #![allow(clippy::useless_vec)]
140 #![allow(clippy::needless_pass_by_value)]
141 #![allow(clippy::string_slice)] use crate::OwnedChanTarget;
145
146 use super::*;
147 #[test]
148 fn decode_ok() {
149 let ct = OwnedChanTarget::builder()
150 .addrs(vec![
151 "[::1]:99".parse().unwrap(),
152 "127.0.0.1:11".parse().unwrap(),
153 ])
154 .ed_identity([42; 32].into())
155 .rsa_identity([45; 20].into())
156 .build()
157 .unwrap();
158
159 let ls = vec![
160 LinkSpec::OrPort("::1".parse().unwrap(), 99),
161 LinkSpec::OrPort("127.0.0.1".parse().unwrap(), 11),
162 LinkSpec::Ed25519Id([42; 32].into()),
163 LinkSpec::RsaId([45; 20].into()),
164 ];
165 let ct2 = OwnedChanTargetBuilder::from_linkspecs(Strictness::Standard, &ls)
166 .unwrap()
167 .build()
168 .unwrap();
169 assert_eq!(format!("{:?}", &ct), format!("{:?}", ct2));
170 }
171
172 #[test]
173 fn decode_errs() {
174 use ChanTargetDecodeError as E;
175 use RelayIdType as ID;
176
177 let ipv4 = LinkSpec::OrPort("127.0.0.1".parse().unwrap(), 11);
178 let ipv6 = LinkSpec::OrPort("::1".parse().unwrap(), 99);
179 let ed = LinkSpec::Ed25519Id([42; 32].into());
180 let rsa = LinkSpec::RsaId([45; 20].into());
181 let err_from = |lst: &[&LinkSpec]| {
182 OwnedChanTargetBuilder::from_linkspecs(
183 Strictness::Standard,
184 &lst.iter().map(|ls| (*ls).clone()).collect::<Vec<_>>()[..],
185 )
186 .err()
187 };
188
189 assert!(err_from(&[&ipv4, &ipv6, &ed, &rsa]).is_none());
190 assert!(err_from(&[&ipv4, &ed, &rsa]).is_none());
191 assert!(matches!(
192 err_from(&[&ipv4, &ed, &ed, &rsa]),
193 Some(E::DuplicatedId(ID::Ed25519))
194 ));
195 assert!(matches!(
196 err_from(&[&ipv4, &ed, &rsa, &rsa]),
197 Some(E::DuplicatedId(ID::Rsa))
198 ));
199 assert!(matches!(
200 err_from(&[&ipv4, &rsa]),
201 Some(E::MissingId(ID::Ed25519))
202 ));
203 assert!(matches!(
204 err_from(&[&ipv4, &ed]),
205 Some(E::MissingId(ID::Rsa))
206 ));
207 assert!(matches!(
208 err_from(&[&ipv6, &ed, &rsa]),
209 Some(E::MissingAddr)
210 ));
211 }
212}