diff --git a/radicle-httpd/src/api/v1/projects.rs b/radicle-httpd/src/api/v1/projects.rs index 3a4e32e2..556c37b5 100644 --- a/radicle-httpd/src/api/v1/projects.rs +++ b/radicle-httpd/src/api/v1/projects.rs @@ -457,7 +457,7 @@ async fn issues_handler( let aliases = &ctx.profile.aliases()?; let issues = issues .into_iter() - .map(|(id, issue, _)| api::json::issue(id, issue, &aliases)) + .map(|(id, issue, _)| api::json::issue(id, issue, aliases)) .skip(page * per_page) .take(per_page) .collect::>(); @@ -568,7 +568,7 @@ async fn issue_handler( let issue = issue::Issues::open(&repo)? .get(&issue_id.into())? .ok_or(Error::NotFound)?; - let aliases = &ctx.profile.aliases()?; + let aliases = ctx.profile.aliases()?; Ok::<_, Error>(Json(api::json::issue(issue_id.into(), issue, &aliases))) } @@ -720,7 +720,7 @@ async fn patches_handler( }) .collect::>(); patches.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp())); - let aliases = &ctx.profile.aliases()?; + let aliases = ctx.profile.aliases()?; let patches = patches .into_iter() .map(|(id, patch, _)| api::json::patch(id, patch, &repo, &aliases)) @@ -742,7 +742,7 @@ async fn patch_handler( let patch = patch::Patches::open(&repo)? .get(&patch_id.into())? .ok_or(Error::NotFound)?; - let aliases = &ctx.profile.aliases()?; + let aliases = ctx.profile.aliases()?; Ok::<_, Error>(Json(api::json::patch( patch_id.into(), diff --git a/radicle-node/src/service/config.rs b/radicle-node/src/service/config.rs index 8298f2e7..57693c67 100644 --- a/radicle-node/src/service/config.rs +++ b/radicle-node/src/service/config.rs @@ -131,3 +131,17 @@ impl Config { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_node_announcement() { + let cfg = Config { + alias: Some(String::from("cloudhead")), + ..Config::default() + }; + assert_eq!("cloudhead", cfg.node(0).alias().unwrap()); + } +} diff --git a/radicle-node/src/service/message.rs b/radicle-node/src/service/message.rs index 555e7bf4..950b7346 100644 --- a/radicle-node/src/service/message.rs +++ b/radicle-node/src/service/message.rs @@ -121,7 +121,12 @@ impl NodeAnnouncement { /// Get the alias as a UTF-8 string. pub fn alias(&self) -> Result<&str, std::str::Utf8Error> { - str::from_utf8(&self.alias) + let length = self + .alias + .iter() + .position(|&b| b == 0) + .unwrap_or(self.alias.len()); + str::from_utf8(&self.alias[..length]) } } diff --git a/radicle/src/node.rs b/radicle/src/node.rs index b8eb4e7a..43970d74 100644 --- a/radicle/src/node.rs +++ b/radicle/src/node.rs @@ -5,7 +5,7 @@ pub mod events; pub mod routing; pub mod tracking; -use std::collections::{BTreeSet, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::io::{BufRead, BufReader}; use std::ops::Deref; use std::os::unix::net::UnixStream; @@ -707,6 +707,24 @@ pub trait AliasStore { fn alias(&self, nid: &NodeId) -> Option; } +impl AliasStore for &T { + fn alias(&self, nid: &NodeId) -> Option { + dbg!((*self).alias(nid)) + } +} + +impl AliasStore for Box { + fn alias(&self, nid: &NodeId) -> Option { + self.deref().alias(nid) + } +} + +impl AliasStore for HashMap { + fn alias(&self, nid: &NodeId) -> Option { + dbg!(self.get(nid).map(ToOwned::to_owned)) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/radicle/src/node/address/store.rs b/radicle/src/node/address/store.rs index 034920ef..acaee4cc 100644 --- a/radicle/src/node/address/store.rs +++ b/radicle/src/node/address/store.rs @@ -77,7 +77,7 @@ impl Store for Book { if let Some(Ok(row)) = stmt.into_iter().next() { let features = row.read::("features"); - let alias = row.read::<&str, _>("alias").to_owned(); + let alias = row.read::, _>("alias").map(ToOwned::to_owned); let timestamp = row.read::("timestamp") as Timestamp; let pow = row.read::("pow") as u32; let mut addrs = Vec::new(); @@ -143,6 +143,11 @@ impl Store for Book { SET features = ?2, alias = ?3, pow = ?4, timestamp = ?5 WHERE timestamp < ?5", )?; + let alias = if alias.is_empty() { + sql::Value::Null + } else { + sql::Value::String(alias.to_owned()) + }; stmt.bind((1, node))?; stmt.bind((2, features))?; @@ -260,7 +265,7 @@ impl AliasStore for Book { /// Calls `Self::get` under the hood. fn alias(&self, nid: &NodeId) -> Option { self.get(nid) - .map(|node| node.map(|n| n.alias)) + .map(|node| node.and_then(|n| n.alias)) .unwrap_or(None) } } @@ -396,6 +401,26 @@ mod test { assert!(!removed); } + #[test] + fn test_alias() { + let alice = arbitrary::gen::(1); + let mut cache = Book::memory().unwrap(); + let features = node::Features::SEED; + let timestamp = LocalTime::now().as_millis(); + + cache + .insert(&alice, features, "alice", 16, timestamp, []) + .unwrap(); + let node = cache.get(&alice).unwrap().unwrap(); + assert_eq!(node.alias.as_deref(), Some("alice")); + + cache + .insert(&alice, features, "", 16, timestamp + 1, []) + .unwrap(); + let node = cache.get(&alice).unwrap().unwrap(); + assert_eq!(node.alias.as_deref(), None); + } + #[test] fn test_insert_and_get() { let alice = arbitrary::gen::(1); @@ -419,7 +444,7 @@ mod test { assert_eq!(node.features, features); assert_eq!(node.pow, 16); assert_eq!(node.timestamp, timestamp); - assert_eq!(node.alias.as_str(), "alice"); + assert_eq!(node.alias.as_deref(), Some("alice")); assert_eq!(node.addrs, vec![ka]); } @@ -478,7 +503,7 @@ mod test { assert!(!updated, "Can't update using a smaller timestamp"); let node = cache.get(&alice).unwrap().unwrap(); - assert_eq!(node.alias, "alice"); + assert_eq!(node.alias.as_deref(), Some("alice")); assert_eq!(node.timestamp, timestamp); assert_eq!(node.pow, 0); @@ -501,7 +526,7 @@ mod test { let node = cache.get(&alice).unwrap().unwrap(); assert_eq!(node.features, node::Features::NONE); - assert_eq!(node.alias, "~alice~"); + assert_eq!(node.alias.as_deref(), Some("~alice~")); assert_eq!(node.timestamp, timestamp + 2); assert_eq!(node.pow, 1); assert_eq!(node.addrs, vec![ka]); diff --git a/radicle/src/node/address/types.rs b/radicle/src/node/address/types.rs index 328e384b..ac1c09c7 100644 --- a/radicle/src/node/address/types.rs +++ b/radicle/src/node/address/types.rs @@ -78,7 +78,7 @@ impl DerefMut for AddressBook { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Node { /// Advertized alias. - pub alias: String, + pub alias: Option, /// Advertized features. pub features: node::Features, /// Advertized addresses diff --git a/radicle/src/node/tracking/store.rs b/radicle/src/node/tracking/store.rs index 0c98a900..6053e270 100644 --- a/radicle/src/node/tracking/store.rs +++ b/radicle/src/node/tracking/store.rs @@ -257,7 +257,7 @@ impl Config { } } -impl AliasStore for &Config { +impl AliasStore for Config { /// Retrieve `alias` of given node. /// Calls `Self::node_policy` under the hood. fn alias(&self, nid: &NodeId) -> Option { diff --git a/radicle/src/profile.rs b/radicle/src/profile.rs index 4dddee9f..eec96aa5 100644 --- a/radicle/src/profile.rs +++ b/radicle/src/profile.rs @@ -191,13 +191,13 @@ pub struct Aliases { addresses: address::Book, } -impl AliasStore for &Aliases { +impl AliasStore for Aliases { /// Retrieve `alias` of given node. - /// First looks in `tracking.db` and then `address.db`, + /// First looks in `tracking.db` and then `address.db`. fn alias(&self, nid: &NodeId) -> Option { - (&&self.tracking as &dyn AliasStore) + self.tracking .alias(nid) - .or_else(|| (&self.addresses as &dyn AliasStore).alias(nid)) + .or_else(|| self.addresses.alias(nid)) } }