tor_netdoc/doc/netstatus/rs/build/
each_flavor.rs1use super::*;
14
15ns_use_this_variety! {
16 use [crate::doc::netstatus::rs]::?::{DocDigest, RouterStatus, RouterStatusIntroItem};
17}
18
19#[cfg_attr(docsrs, doc(cfg(feature = "build_docs")))]
22#[derive(Debug, Clone)]
23pub struct RouterStatusBuilder {
24 nickname: Option<String>,
26 identity: Option<RsaIdentity>,
28 addrs: Vec<SocketAddr>,
30 doc_digest: Option<DocDigest>,
32 flags: RelayFlags,
34 version: Option<String>,
36 protos: Option<Protocols>,
38 weight: Option<RelayWeight>,
40}
41
42impl RouterStatusBuilder {
43 pub(crate) fn new() -> Self {
45 RouterStatusBuilder {
46 nickname: None,
47 identity: None,
48 addrs: Vec::new(),
49 doc_digest: None,
50 flags: RelayFlags::empty(),
51 version: None,
52 protos: None,
53 weight: None,
54 }
55 }
56
57 pub fn nickname(&mut self, nickname: String) -> &mut Self {
61 self.nickname = Some(nickname);
62 self
63 }
64
65 pub fn identity(&mut self, identity: RsaIdentity) -> &mut Self {
71 self.identity = Some(identity);
72 self
73 }
74 pub fn add_or_port(&mut self, addr: SocketAddr) -> &mut Self {
78 self.addrs.push(addr);
79 self
80 }
81 pub fn doc_digest(&mut self, doc_digest: DocDigest) -> &mut Self {
85 self.doc_digest = Some(doc_digest);
86 self
87 }
88 pub fn set_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
90 self.flags = flags.into();
91 self
92 }
93 pub fn add_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
96 self.flags |= flags.into();
97 self
98 }
99 #[cfg(feature = "testing")]
101 pub fn clear_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
102 self.flags &= !flags.into();
103 self
104 }
105 pub fn version(&mut self, version: String) -> &mut Self {
109 self.version = Some(version);
110 self
111 }
112 pub fn protos(&mut self, protos: Protocols) -> &mut Self {
117 self.protos = Some(protos);
118 self
119 }
120 pub fn weight(&mut self, weight: RelayWeight) -> &mut Self {
124 self.weight = Some(weight);
125 self
126 }
127 pub(super) fn finish(&self) -> Result<RouterStatus> {
130 let nickname = self.nickname.as_deref().unwrap_or("Unnamed").parse()?;
131 let identity = self
132 .identity
133 .ok_or(Error::CannotBuild("Missing RSA identity"))?;
134 if self.addrs.is_empty() {
135 return Err(Error::CannotBuild("No addresses"));
136 }
137 let doc_digest = *self
138 .doc_digest
139 .as_ref()
140 .ok_or(Error::CannotBuild("Missing document digest"))?;
141 let protos = self
142 .protos
143 .as_ref()
144 .ok_or(Error::CannotBuild("Missing protocols"))?
145 .clone();
146 let weight = self.weight.unwrap_or(RelayWeight::Unmeasured(0));
147 let version = self.version.as_deref().map(str::parse).transpose()?;
148
149 let mut ip = None;
150 let a = self
151 .addrs
152 .iter()
153 .filter_map(|a| match a {
154 SocketAddr::V4(a) if ip.is_none() => {
155 ip = Some(a);
156 None
157 }
158 other => Some(*other),
159 })
160 .collect::<Vec<_>>();
161 let ip = ip.ok_or_else(|| Error::CannotBuild("No IPv4 address"))?;
162
163 ns_choose! { (
164 let r_doc_digest = doc_digest;
165 let m_doc_digest = NotPresent;
166 ) (
167 let r_doc_digest = NotPresent;
168 let m_doc_digest = doc_digest;
169 ) (
170 compile_error!("no builder for votes");
171 ) };
172
173 Ok(RouterStatus {
174 r: RouterStatusIntroItem {
175 nickname,
176 identity: Base64Fingerprint(identity),
177 doc_digest: r_doc_digest,
178 publication: IgnoredPublicationTimeSp,
179 ip: *ip.ip(),
180 or_port: ip.port(),
181 },
182 m: m_doc_digest,
183 a,
184 version,
185 protos,
186 flags: DocRelayFlags {
187 known: self.flags,
188 unknown: Unknown::new_discard(),
189 },
190 weight,
191 })
192 }
193
194 pub fn build_into(&self, builder: &mut ConsensusBuilder) -> Result<()> {
197 builder.add_rs(self.build()?);
198 Ok(())
199 }
200
201 pub fn build(&self) -> Result<RouterStatus> {
204 self.finish()
205 }
206}