use super::*;
ns_use_this_variety! {
use [crate::doc::netstatus::rs]::?::{DocDigest, RouterStatus, RouterStatusIntroItem};
}
#[cfg_attr(docsrs, doc(cfg(feature = "build_docs")))]
#[derive(Debug, Clone)]
pub struct RouterStatusBuilder {
nickname: Option<String>,
identity: Option<RsaIdentity>,
addrs: Vec<SocketAddr>,
doc_digest: Option<DocDigest>,
flags: RelayFlags,
version: Option<String>,
protos: Option<Protocols>,
weight: Option<RelayWeight>,
}
impl RouterStatusBuilder {
pub(crate) fn new() -> Self {
RouterStatusBuilder {
nickname: None,
identity: None,
addrs: Vec::new(),
doc_digest: None,
flags: RelayFlags::empty(),
version: None,
protos: None,
weight: None,
}
}
pub fn nickname(&mut self, nickname: String) -> &mut Self {
self.nickname = Some(nickname);
self
}
pub fn identity(&mut self, identity: RsaIdentity) -> &mut Self {
self.identity = Some(identity);
self
}
pub fn add_or_port(&mut self, addr: SocketAddr) -> &mut Self {
self.addrs.push(addr);
self
}
pub fn doc_digest(&mut self, doc_digest: DocDigest) -> &mut Self {
self.doc_digest = Some(doc_digest);
self
}
pub fn set_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
self.flags = flags.into();
self
}
pub fn add_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
self.flags |= flags.into();
self
}
#[cfg(feature = "testing")]
pub fn clear_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
self.flags &= !flags.into();
self
}
pub fn version(&mut self, version: String) -> &mut Self {
self.version = Some(version);
self
}
pub fn protos(&mut self, protos: Protocols) -> &mut Self {
self.protos = Some(protos);
self
}
pub fn weight(&mut self, weight: RelayWeight) -> &mut Self {
self.weight = Some(weight);
self
}
pub(super) fn finish(&self) -> Result<RouterStatus> {
let nickname = self.nickname.as_deref().unwrap_or("Unnamed").parse()?;
let identity = self
.identity
.ok_or(Error::CannotBuild("Missing RSA identity"))?;
if self.addrs.is_empty() {
return Err(Error::CannotBuild("No addresses"));
}
let doc_digest = *self
.doc_digest
.as_ref()
.ok_or(Error::CannotBuild("Missing document digest"))?;
let protos = self
.protos
.as_ref()
.ok_or(Error::CannotBuild("Missing protocols"))?
.clone();
let weight = self.weight.unwrap_or(RelayWeight::Unmeasured(0));
let version = self.version.as_deref().map(str::parse).transpose()?;
let mut ip = None;
let a = self
.addrs
.iter()
.filter_map(|a| match a {
SocketAddr::V4(a) if ip.is_none() => {
ip = Some(a);
None
}
other => Some(*other),
})
.collect::<Vec<_>>();
let ip = ip.ok_or_else(|| Error::CannotBuild("No IPv4 address"))?;
ns_choose! { (
let r_doc_digest = doc_digest;
let m_doc_digest = NotPresent;
) (
let r_doc_digest = NotPresent;
let m_doc_digest = doc_digest;
) (
compile_error!("no builder for votes");
) };
Ok(RouterStatus {
r: RouterStatusIntroItem {
nickname,
identity: Base64Fingerprint(identity),
doc_digest: r_doc_digest,
publication: IgnoredPublicationTimeSp,
ip: *ip.ip(),
or_port: ip.port(),
},
m: m_doc_digest,
a,
version,
protos,
flags: DocRelayFlags {
known: self.flags,
unknown: Unknown::new_discard(),
},
weight,
})
}
pub fn build_into(&self, builder: &mut ConsensusBuilder) -> Result<()> {
builder.add_rs(self.build()?);
Ok(())
}
pub fn build(&self) -> Result<RouterStatus> {
self.finish()
}
}