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 use crate::OwnedChanTarget;
144
145 use super::*;
146 #[test]
147 fn decode_ok() {
148 let ct = OwnedChanTarget::builder()
149 .addrs(vec![
150 "[::1]:99".parse().unwrap(),
151 "127.0.0.1:11".parse().unwrap(),
152 ])
153 .ed_identity([42; 32].into())
154 .rsa_identity([45; 20].into())
155 .build()
156 .unwrap();
157
158 let ls = vec![
159 LinkSpec::OrPort("::1".parse().unwrap(), 99),
160 LinkSpec::OrPort("127.0.0.1".parse().unwrap(), 11),
161 LinkSpec::Ed25519Id([42; 32].into()),
162 LinkSpec::RsaId([45; 20].into()),
163 ];
164 let ct2 = OwnedChanTargetBuilder::from_linkspecs(Strictness::Standard, &ls)
165 .unwrap()
166 .build()
167 .unwrap();
168 assert_eq!(format!("{:?}", &ct), format!("{:?}", ct2));
169 }
170
171 #[test]
172 fn decode_errs() {
173 use ChanTargetDecodeError as E;
174 use RelayIdType as ID;
175
176 let ipv4 = LinkSpec::OrPort("127.0.0.1".parse().unwrap(), 11);
177 let ipv6 = LinkSpec::OrPort("::1".parse().unwrap(), 99);
178 let ed = LinkSpec::Ed25519Id([42; 32].into());
179 let rsa = LinkSpec::RsaId([45; 20].into());
180 let err_from = |lst: &[&LinkSpec]| {
181 OwnedChanTargetBuilder::from_linkspecs(
182 Strictness::Standard,
183 &lst.iter().map(|ls| (*ls).clone()).collect::<Vec<_>>()[..],
184 )
185 .err()
186 };
187
188 assert!(err_from(&[&ipv4, &ipv6, &ed, &rsa]).is_none());
189 assert!(err_from(&[&ipv4, &ed, &rsa]).is_none());
190 assert!(matches!(
191 err_from(&[&ipv4, &ed, &ed, &rsa]),
192 Some(E::DuplicatedId(ID::Ed25519))
193 ));
194 assert!(matches!(
195 err_from(&[&ipv4, &ed, &rsa, &rsa]),
196 Some(E::DuplicatedId(ID::Rsa))
197 ));
198 assert!(matches!(
199 err_from(&[&ipv4, &rsa]),
200 Some(E::MissingId(ID::Ed25519))
201 ));
202 assert!(matches!(
203 err_from(&[&ipv4, &ed]),
204 Some(E::MissingId(ID::Rsa))
205 ));
206 assert!(matches!(
207 err_from(&[&ipv6, &ed, &rsa]),
208 Some(E::MissingAddr)
209 ));
210 }
211}