use super::discovery::{
BootstrapRequest, Contact, ContactPage, ContactUpdate, Identity, PeerDirectoryEntry, Presence,
PresencePage,
};
use super::errors::Error;
use super::node::Client;
use crate::domain::{ContactListRequest, PresenceListRequest, TopicListRequest};
use crate::SdkBackend;
use std::collections::BTreeMap;
impl<B: SdkBackend> Client<B> {
pub fn identities(&self) -> Result<Vec<Identity>, Error> {
let identities = self.backend.identity_list().map_err(Error::from)?;
Ok(identities.into_iter().map(Identity::from).collect())
}
pub fn announce_now(&self) -> Result<(), Error> {
self.backend.identity_announce_now().map_err(Error::from)?;
Ok(())
}
pub fn contacts(
&self,
cursor: Option<String>,
limit: Option<usize>,
) -> Result<ContactPage, Error> {
let result = self
.backend
.identity_contact_list(ContactListRequest {
cursor,
limit,
extensions: BTreeMap::new(),
})
.map_err(Error::from)?;
Ok(ContactPage::from(result))
}
pub fn presence(
&self,
cursor: Option<String>,
limit: Option<usize>,
) -> Result<PresencePage, Error> {
self.presence_since(cursor, limit, None)
}
pub fn presence_since(
&self,
cursor: Option<String>,
limit: Option<usize>,
min_last_seen_ts_ms: Option<i64>,
) -> Result<PresencePage, Error> {
let result = self
.backend
.identity_presence_list(PresenceListRequest {
cursor,
limit,
min_last_seen_ts_ms,
extensions: BTreeMap::new(),
})
.map_err(Error::from)?;
Ok(PresencePage::from(result))
}
pub fn update_contact(&self, update: ContactUpdate) -> Result<Contact, Error> {
let contact = self.backend.identity_contact_update(update.into()).map_err(Error::from)?;
Ok(Contact::from(contact))
}
pub fn bootstrap_identity(&self, request: BootstrapRequest) -> Result<Contact, Error> {
let contact = self.backend.identity_bootstrap(request.into()).map_err(Error::from)?;
Ok(Contact::from(contact))
}
pub fn peer_directory(&self, limit: Option<usize>) -> Result<Vec<PeerDirectoryEntry>, Error> {
self.peer_directory_since(limit, None)
}
pub fn peer_directory_since(
&self,
limit: Option<usize>,
min_last_seen_ts_ms: Option<i64>,
) -> Result<Vec<PeerDirectoryEntry>, Error> {
let mut entries = BTreeMap::<String, PeerDirectoryEntry>::new();
for contact in self.collect_contacts(limit)? {
entries.insert(
contact.identity.clone(),
PeerDirectoryEntry {
peer_id: contact.identity.clone(),
display_name: contact.display_name.clone(),
name_source: contact.display_name.as_ref().map(|_| "contact".to_owned()),
trust_level: Some(contact.trust_level.clone()),
bootstrap: contact.bootstrap,
online: false,
last_seen_ts_ms: None,
first_seen_ts_ms: None,
seen_count: 0,
metadata: contact.metadata.clone(),
extensions: contact.extensions.clone(),
},
);
}
for presence in self.collect_presence(limit, min_last_seen_ts_ms)? {
let entry =
entries.entry(presence.peer_id.clone()).or_insert_with(|| PeerDirectoryEntry {
peer_id: presence.peer_id.clone(),
display_name: presence.display_name.clone(),
name_source: presence.name_source.clone(),
trust_level: presence.trust_level.clone(),
bootstrap: presence.bootstrap.unwrap_or(false),
online: true,
last_seen_ts_ms: Some(presence.last_seen_ts_ms),
first_seen_ts_ms: Some(presence.first_seen_ts_ms),
seen_count: presence.seen_count,
metadata: BTreeMap::new(),
extensions: presence.extensions.clone(),
});
entry.online = true;
entry.last_seen_ts_ms = Some(presence.last_seen_ts_ms);
entry.first_seen_ts_ms = Some(presence.first_seen_ts_ms);
entry.seen_count = presence.seen_count;
if entry.display_name.is_none() {
entry.display_name = presence.display_name.clone();
}
if entry.name_source.is_none() {
entry.name_source = presence.name_source.clone();
}
if entry.trust_level.is_none() {
entry.trust_level = presence.trust_level.clone();
}
if !entry.bootstrap {
entry.bootstrap = presence.bootstrap.unwrap_or(false);
}
for (key, value) in presence.extensions {
entry.extensions.entry(key).or_insert(value);
}
}
let mut values = entries.into_values().collect::<Vec<_>>();
if let Some(limit) = limit {
values.truncate(limit);
}
Ok(values)
}
pub(crate) fn find_contact(&self, identity: &str) -> Result<Option<Contact>, Error> {
Ok(self.collect_contacts(None)?.into_iter().find(|contact| contact.identity == identity))
}
pub(crate) fn find_topic_by_path(
&self,
topic_path: &str,
page_size: usize,
) -> Result<Option<crate::domain::TopicRecord>, Error> {
let mut cursor = None;
loop {
let page = self
.backend
.topic_list(TopicListRequest {
cursor: cursor.clone(),
limit: Some(page_size),
extensions: BTreeMap::new(),
})
.map_err(Error::from)?;
if let Some(found) = page.topics.into_iter().find(|topic| {
topic.topic_path.as_ref().map(|path| path.0.as_str()) == Some(topic_path)
}) {
return Ok(Some(found));
}
match page.next_cursor {
Some(next_cursor) if cursor.as_deref() != Some(next_cursor.as_str()) => {
cursor = Some(next_cursor);
}
_ => return Ok(None),
}
}
}
pub(crate) fn collect_contacts(&self, limit: Option<usize>) -> Result<Vec<Contact>, Error> {
let mut contacts = Vec::new();
let mut cursor = None;
loop {
let page = self.contacts(cursor.clone(), limit)?;
contacts.extend(page.contacts);
match page.next_cursor {
Some(next_cursor) if cursor.as_deref() != Some(next_cursor.as_str()) => {
cursor = Some(next_cursor);
}
_ => break,
}
}
Ok(contacts)
}
pub(crate) fn collect_presence(
&self,
limit: Option<usize>,
min_last_seen_ts_ms: Option<i64>,
) -> Result<Vec<Presence>, Error> {
let mut peers = Vec::new();
let mut cursor = None;
loop {
let page = self.presence_since(cursor.clone(), limit, min_last_seen_ts_ms)?;
peers.extend(page.peers);
match page.next_cursor {
Some(next_cursor) if cursor.as_deref() != Some(next_cursor.as_str()) => {
cursor = Some(next_cursor);
}
_ => break,
}
}
Ok(peers)
}
}