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.addrs.iter().filter_map(|a| match a {
151 SocketAddr::V4(a) if ip.is_none() => {
152 ip = Some(a);
153 None
154 }
155 other => {
156 Some(*other)
157 }
158 }).collect::<Vec<_>>();
159 let ip = ip.ok_or_else(|| Error::CannotBuild("No IPv4 address"))?;
160
161 ns_choose! { (
162 let r_doc_digest = doc_digest;
163 let m_doc_digest = NotPresent;
164 ) (
165 let r_doc_digest = NotPresent;
166 let m_doc_digest = doc_digest;
167 ) (
168 compile_error!("no builder for votes");
169 ) };
170
171 Ok(RouterStatus {
172 r: RouterStatusIntroItem {
173 nickname,
174 identity: Base64Fingerprint(identity),
175 doc_digest: r_doc_digest,
176 publication: IgnoredPublicationTimeSp,
177 ip: *ip.ip(),
178 or_port: ip.port(),
179 },
180 m: m_doc_digest,
181 a,
182 version,
183 protos: doc::PROTOVERS_CACHE.intern(protos),
184 flags: DocRelayFlags {
185 known: self.flags,
186 unknown: Unknown::new_discard(),
187 },
188 weight,
189 })
190 }
191
192 pub fn build_into(
195 &self,
196 builder: &mut ConsensusBuilder,
197 ) -> Result<()> {
198 builder.add_rs(self.build()?);
199 Ok(())
200 }
201
202 pub fn build(&self) -> Result<RouterStatus> {
205 self.finish()
206 }
207}