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: RelayFlags) -> &mut Self {
90 self.flags = flags;
91 self
92 }
93 pub fn add_flags(&mut self, flags: RelayFlags) -> &mut Self {
96 self.flags |= flags;
97 self
98 }
99 #[cfg(feature = "testing")]
101 pub fn clear_flags(&mut self, flags: RelayFlags) -> &mut Self {
102 self.flags &= !flags;
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: self.flags,
185 weight,
186 })
187 }
188
189 pub fn build_into(
192 &self,
193 builder: &mut ConsensusBuilder,
194 ) -> Result<()> {
195 builder.add_rs(self.build()?);
196 Ok(())
197 }
198
199 pub fn build(&self) -> Result<RouterStatus> {
202 self.finish()
203 }
204}