diff --git a/crates/radicle-cli/src/commands/follow.rs b/crates/radicle-cli/src/commands/follow.rs index de5e083a..7b4f0ff3 100644 --- a/crates/radicle-cli/src/commands/follow.rs +++ b/crates/radicle-cli/src/commands/follow.rs @@ -147,41 +147,45 @@ pub fn following(profile: &Profile, alias: Option) -> anyhow::Result<()> term::format::default(String::from("Policy")), ]); t.divider(); - - match alias { - None => push_policies(&mut t, &aliases, store.follow_policies()?), - Some(alias) => push_policies( - &mut t, - &aliases, - store - .follow_policies()? - .filter(|p| p.alias.as_ref().is_some_and(|alias_| *alias_ == alias)), - ), - }; + push_policies(&mut t, &aliases, store.follow_policies()?, &alias); t.print(); - Ok(()) } fn push_policies( t: &mut Table<3, Paint>, aliases: &impl AliasStore, - policies: impl Iterator, + policies: impl Iterator>, + filter: &Option, ) { - for policy::FollowPolicy { - nid: id, - alias, - policy, - } in policies - { - t.push([ - term::format::highlight(Did::from(id).to_string()), - match alias { - None => term::format::secondary(fallback_alias(&id, aliases)), - Some(alias) => term::format::secondary(alias.to_string()), - }, - term::format::secondary(policy.to_string()), - ]); + for policy in policies { + match policy { + Ok(policy::FollowPolicy { + nid: id, + alias, + policy, + }) => { + if match (filter, &alias) { + (None, _) => false, + (Some(filter), Some(alias)) => *filter != *alias, + (Some(_), None) => true, + } { + continue; + } + + t.push([ + term::format::highlight(Did::from(id).to_string()), + match alias { + None => term::format::secondary(fallback_alias(&id, aliases)), + Some(alias) => term::format::secondary(alias.to_string()), + }, + term::format::secondary(policy.to_string()), + ]); + } + Err(err) => { + term::error(format!("Failed to read a follow policy: {err}")); + } + } } } diff --git a/crates/radicle-cli/src/commands/seed.rs b/crates/radicle-cli/src/commands/seed.rs index bfd4c02f..d0cc083f 100644 --- a/crates/radicle-cli/src/commands/seed.rs +++ b/crates/radicle-cli/src/commands/seed.rs @@ -203,21 +203,28 @@ pub fn seeding(profile: &Profile) -> anyhow::Result<()> { ]); t.divider(); - for policy::SeedPolicy { rid, policy } in store.seed_policies()? { - let id = rid.to_string(); - let name = storage - .repository(rid) - .and_then(|repo| repo.project().map(|proj| proj.name().to_string())) - .unwrap_or_default(); - let scope = policy.scope().unwrap_or_default().to_string(); - let policy = term::format::policy(&Policy::from(policy)); + for policy in store.seed_policies()? { + match policy { + Ok(policy::SeedPolicy { rid, policy }) => { + let id = rid.to_string(); + let name = storage + .repository(rid) + .and_then(|repo| repo.project().map(|proj| proj.name().to_string())) + .unwrap_or_default(); + let scope = policy.scope().unwrap_or_default().to_string(); + let policy = term::format::policy(&Policy::from(policy)); - t.push([ - term::format::tertiary(id), - name.into(), - policy, - term::format::dim(scope), - ]) + t.push([ + term::format::tertiary(id), + name.into(), + policy, + term::format::dim(scope), + ]) + } + Err(err) => { + term::error(format!("Failed to read a seeding policy: {err}")); + } + } } if t.is_empty() { diff --git a/crates/radicle-cli/src/commands/stats.rs b/crates/radicle-cli/src/commands/stats.rs index ae12648a..a98a2356 100644 --- a/crates/radicle-cli/src/commands/stats.rs +++ b/crates/radicle-cli/src/commands/stats.rs @@ -126,7 +126,7 @@ pub fn run(_options: Options, ctx: impl term::Context) -> anyhow::Result<()> { .into_iter() .next() .unwrap()?; - let count = row.read::(0) as usize; + let count = row.try_read::(0)? as usize; stats.repos.unique = count; } @@ -141,7 +141,7 @@ pub fn run(_options: Options, ctx: impl term::Context) -> anyhow::Result<()> { // SAFETY: `COUNT` always returns a row. let row = stmt.iter().next().unwrap()?; - stats.nodes.online_daily = row.read::(0) as usize; + stats.nodes.online_daily = row.try_read::(0)? as usize; let since = now - LocalDuration::from_mins(60 * 24 * 7); // 1 week. stmt.reset()?; @@ -149,7 +149,7 @@ pub fn run(_options: Options, ctx: impl term::Context) -> anyhow::Result<()> { stmt.bind((2, now.as_millis() as i64))?; let row = stmt.iter().next().unwrap()?; - stats.nodes.online_weekly = row.read::(0) as usize; + stats.nodes.online_weekly = row.try_read::(0)? as usize; } { @@ -168,7 +168,7 @@ pub fn run(_options: Options, ctx: impl term::Context) -> anyhow::Result<()> { .next() // SAFETY: `COUNT` always returns a row. .unwrap()?; - let count = row.read::(0) as usize; + let count = row.try_read::(0)? as usize; stats.nodes.public_daily = count; } @@ -187,7 +187,7 @@ pub fn run(_options: Options, ctx: impl term::Context) -> anyhow::Result<()> { .next() // SAFETY: `COUNT` always returns a row. .unwrap()?; - let count = row.read::(0) as usize; + let count = row.try_read::(0)? as usize; stats.nodes.seeding_weekly = count; } diff --git a/crates/radicle-fetch/src/policy.rs b/crates/radicle-fetch/src/policy.rs index 508b5d65..afc07aca 100644 --- a/crates/radicle-fetch/src/policy.rs +++ b/crates/radicle-fetch/src/policy.rs @@ -30,9 +30,22 @@ impl Allowed { let nodes = config .follow_policies() .map_err(|err| error::Policy::FailedNodes { rid, err })?; - let followed: HashSet<_> = nodes - .filter_map(|node| (node.policy == Policy::Allow).then_some(node.nid)) - .collect(); + + let mut followed = HashSet::new(); + + for node in nodes { + let node = match node { + Ok(policy) => policy, + Err(err) => { + log::error!(target: "fetch", "Failed to read follow policy for {rid}: {err}"); + continue; + } + }; + + if node.policy == Policy::Allow { + followed.insert(node.nid); + } + } Ok(Allowed::Followed { remotes: followed }) } @@ -62,10 +75,23 @@ impl BlockList { } pub fn from_config(config: &Config) -> Result { - Ok(config - .follow_policies()? - .filter_map(|entry| (entry.policy == Policy::Block).then_some(entry.nid)) - .collect()) + let mut blocked = HashSet::new(); + + for entry in config.follow_policies()? { + let entry = match entry { + Ok(entry) => entry, + Err(err) => { + log::error!(target: "fetch", "Failed to read follow policy: {err}"); + continue; + } + }; + + if entry.policy == Policy::Block { + blocked.insert(entry.nid); + } + } + + Ok(BlockList(blocked)) } } diff --git a/crates/radicle-protocol/src/service.rs b/crates/radicle-protocol/src/service.rs index 10d013e2..3c373dd9 100644 --- a/crates/radicle-protocol/src/service.rs +++ b/crates/radicle-protocol/src/service.rs @@ -570,13 +570,7 @@ where // Nb. This is potentially slow if we have lots of repos. We should probably // only re-compute the filter when we've unseeded a certain amount of repos // and the filter is really out of date. - // - // TODO: Share this code with initialization code. - self.filter = Filter::new( - self.policies - .seed_policies()? - .filter_map(|t| (t.policy.is_allow()).then_some(t.rid)), - ); + self.filter = Filter::allowed_by(self.policies.seed_policies()?); // Update and announce new inventory. if let Err(e) = self.remove_inventory(id) { error!(target: "service", "Error updating inventory after unseed: {e}"); @@ -750,11 +744,7 @@ where .remove_inventories(private.iter(), &nid)?; // Setup subscription filter for seeded repos. - self.filter = Filter::new( - self.policies - .seed_policies()? - .filter_map(|t| (t.policy.is_allow()).then_some(t.rid)), - ); + self.filter = Filter::allowed_by(self.policies.seed_policies()?); // Connect to configured peers. let addrs = self.config.connect.clone(); for (id, addr) in addrs.into_iter().map(|ca| ca.into()) { @@ -2510,10 +2500,16 @@ where /// Fetch all repositories that are seeded but missing from storage. fn fetch_missing_repositories(&mut self) -> Result<(), Error> { - // TODO(finto): could filter the policies based on the continue checks - // below, but `storage.contains` is fallible let policies = self.policies.seed_policies()?.collect::>(); for policy in policies { + let policy = match policy { + Ok(policy) => policy, + Err(err) => { + log::error!(target: "protocol::filter", "Failed to read seed policy: {err}"); + continue; + } + }; + let rid = policy.rid; if !policy.is_allow() { diff --git a/crates/radicle-protocol/src/service/filter.rs b/crates/radicle-protocol/src/service/filter.rs index c57db7fc..3b934dfa 100644 --- a/crates/radicle-protocol/src/service/filter.rs +++ b/crates/radicle-protocol/src/service/filter.rs @@ -59,6 +59,30 @@ impl Filter { Self(bloom) } + pub fn allowed_by( + policies: impl Iterator< + Item = Result, + >, + ) -> Self { + let mut ids = Vec::new(); + + for seed in policies { + let seed = match seed { + Ok(seed) => seed, + Err(err) => { + log::error!(target: "protocol::filter", "Failed to read seed policy: {err}"); + continue; + } + }; + + if seed.policy.is_allow() { + ids.push(seed.rid); + } + } + + Self::new(ids) + } + /// Empty filter with nothing set. pub fn empty() -> Self { Self(BloomFilter::from(vec![0x0; FILTER_SIZE_S])) diff --git a/crates/radicle-protocol/src/service/gossip/store.rs b/crates/radicle-protocol/src/service/gossip/store.rs index 3d2564a8..c0732727 100644 --- a/crates/radicle-protocol/src/service/gossip/store.rs +++ b/crates/radicle-protocol/src/service/gossip/store.rs @@ -135,7 +135,7 @@ impl Store for Database { if let Some(row) = stmt.into_iter().next() { let row = row?; - let id = row.read::("rowid"); + let id = row.try_read::("rowid")?; Ok(Some(id as AnnouncementId)) } else { @@ -342,9 +342,9 @@ mod parse { use super::*; pub fn announcement(row: sql::Row) -> Result<(AnnouncementId, Announcement), Error> { - let id = row.read::("rowid") as AnnouncementId; - let node = row.read::("node"); - let gt = row.read::("type"); + let id = row.try_read::("rowid")? as AnnouncementId; + let node = row.try_read::("node")?; + let gt = row.try_read::("type")?; let message = match gt { GossipType::Refs => { let ann = row.try_read::("message")?; @@ -359,8 +359,8 @@ mod parse { AnnouncementMessage::Node(ann) } }; - let signature = row.read::("signature"); - let timestamp = row.read::("timestamp"); + let signature = row.try_read::("signature")?; + let timestamp = row.try_read::("timestamp")?; debug_assert_eq!(timestamp, message.timestamp()); diff --git a/crates/radicle/src/cob/cache/migrations/2.rs b/crates/radicle/src/cob/cache/migrations/2.rs index e6393cbe..3be7f088 100644 --- a/crates/radicle/src/cob/cache/migrations/2.rs +++ b/crates/radicle/src/cob/cache/migrations/2.rs @@ -27,8 +27,8 @@ pub fn run( for row in rows { let row = row?; - let id = row.read::<&str, _>("id"); - let mut patch = json::from_str::(row.read::<&str, _>("patch")) + let id = row.try_read::<&str, _>("id")?; + let mut patch = json::from_str::(row.try_read::<&str, _>("patch")?) .map_err(Error::MalformedJson)?; let patch = patch.as_object_mut().ok_or(Error::MalformedJsonSchema)?; let revisions = patch["revisions"] @@ -117,7 +117,7 @@ mod tests { }) .unwrap(); - let patch = row.read::<&str, _>("patch"); + let patch = row.try_read::<&str, _>("patch").unwrap(); let actual: serde_json::Value = serde_json::from_str(patch).unwrap(); let expected: serde_json::Value = serde_json::from_str(PATCH_V2).unwrap(); diff --git a/crates/radicle/src/cob/issue/cache.rs b/crates/radicle/src/cob/issue/cache.rs index b986f246..65521399 100644 --- a/crates/radicle/src/cob/issue/cache.rs +++ b/crates/radicle/src/cob/issue/cache.rs @@ -440,8 +440,8 @@ pub struct IssuesIter<'a> { impl IssuesIter<'_> { fn parse_row(row: sql::Row) -> Result<(IssueId, Issue), Error> { - let id = IssueId::from_str(row.read::<&str, _>("id"))?; - let issue = serde_json::from_str::(row.read::<&str, _>("issue"))?; + let id = IssueId::from_str(row.try_read::<&str, _>("id")?)?; + let issue = serde_json::from_str::(row.try_read::<&str, _>("issue")?)?; Ok((id, issue)) } } @@ -537,7 +537,7 @@ mod query { match stmt.into_iter().next().transpose()? { None => Ok(None), Some(row) => { - let issue = row.read::<&str, _>("issue"); + let issue = row.try_read::<&str, _>("issue")?; let issue = serde_json::from_str(issue)?; Ok(Some(issue)) } @@ -597,8 +597,8 @@ mod query { stmt.into_iter() .try_fold(IssueCounts::default(), |mut counts, row| { let row = row?; - let count = row.read::("count") as usize; - let status = serde_json::from_str::(row.read::<&str, _>("state"))?; + let count = row.try_read::("count")? as usize; + let status = serde_json::from_str::(row.try_read::<&str, _>("state")?)?; match status { State::Closed { .. } => counts.closed += count, State::Open => counts.open += count, diff --git a/crates/radicle/src/cob/patch/cache.rs b/crates/radicle/src/cob/patch/cache.rs index 4e8ff004..b0b51389 100644 --- a/crates/radicle/src/cob/patch/cache.rs +++ b/crates/radicle/src/cob/patch/cache.rs @@ -427,8 +427,8 @@ pub struct PatchesIter<'a> { impl PatchesIter<'_> { fn parse_row(row: sql::Row) -> Result<(PatchId, Patch), Error> { - let id = PatchId::from_str(row.read::<&str, _>("id"))?; - let patch = serde_json::from_str::(row.read::<&str, _>("patch")) + let id = PatchId::from_str(row.try_read::<&str, _>("id")?)?; + let patch = serde_json::from_str::(row.try_read::<&str, _>("patch")?) .map_err(|e| Error::Object(id, e))?; Ok((id, patch)) } @@ -592,7 +592,7 @@ mod query { match stmt.into_iter().next().transpose()? { None => Ok(None), Some(row) => { - let patch = row.read::<&str, _>("patch"); + let patch = row.try_read::<&str, _>("patch")?; let patch = serde_json::from_str(patch).map_err(|e| Error::Object(*id, e))?; Ok(Some(patch)) } @@ -618,10 +618,11 @@ mod query { match stmt.into_iter().next().transpose()? { None => Ok(None), Some(row) => { - let id = PatchId::from_str(row.read::<&str, _>("id"))?; - let patch = serde_json::from_str::(row.read::<&str, _>("patch")) + let id = PatchId::from_str(row.try_read::<&str, _>("id")?)?; + let patch = serde_json::from_str::(row.try_read::<&str, _>("patch")?) .map_err(|e| Error::Object(id, e))?; - let revision = serde_json::from_str::(row.read::<&str, _>("revision"))?; + let revision = + serde_json::from_str::(row.try_read::<&str, _>("revision")?)?; Ok(Some(ByRevision { id, patch, @@ -686,8 +687,8 @@ mod query { stmt.into_iter() .try_fold(PatchCounts::default(), |mut counts, row| { let row = row?; - let count = row.read::("count") as usize; - let status = serde_json::from_str::(row.read::<&str, _>("state"))?; + let count = row.try_read::("count")? as usize; + let status = serde_json::from_str::(row.try_read::<&str, _>("state")?)?; match status { State::Draft => counts.draft += count, State::Open { .. } => counts.open += count, diff --git a/crates/radicle/src/node/address/store.rs b/crates/radicle/src/node/address/store.rs index e2b5aa2b..380c3b77 100644 --- a/crates/radicle/src/node/address/store.rs +++ b/crates/radicle/src/node/address/store.rs @@ -115,15 +115,15 @@ impl Store for Database { stmt.bind((1, node))?; if let Some(Ok(row)) = stmt.into_iter().next() { - let version = row.read::("version").try_into()?; - let features = row.read::("features"); - let alias = Alias::from_str(row.read::<&str, _>("alias"))?; - let timestamp = row.read::("timestamp"); - let pow = row.read::("pow") as u32; - let agent = row.read::("agent"); - let penalty = row.read::("penalty").min(u8::MAX as i64); + let version = row.try_read::("version")?.try_into()?; + let features = row.try_read::("features")?; + let alias = Alias::from_str(row.try_read::<&str, _>("alias")?)?; + let timestamp = row.try_read::("timestamp")?; + let pow = row.try_read::("pow")? as u32; + let agent = row.try_read::("agent")?; + let penalty = row.try_read::("penalty")?.min(u8::MAX as i64); let penalty = Penalty(penalty as u8); - let banned = row.read::("banned").is_positive(); + let banned = row.try_read::("banned")?.is_positive(); let addrs = self.addresses_of(node)?; Ok(Some(Node { @@ -154,8 +154,8 @@ impl Store for Database { if let Some(row) = stmt.into_iter().next() { let row = row?; - let addr_banned = row.read::(0).is_positive(); - let node_banned = row.read::(1).is_positive(); + let addr_banned = row.try_read::(0)?.is_positive(); + let node_banned = row.try_read::(1)?.is_positive(); Ok(node_banned || addr_banned) } else { @@ -183,16 +183,16 @@ impl Store for Database { for row in stmt.into_iter() { let row = row?; - let _typ = row.read::("type"); - let addr = row.read::("value"); - let source = row.read::("source"); + let _typ = row.try_read::("type")?; + let addr = row.try_read::("value")?; + let source = row.try_read::("source")?; let last_attempt = row .read::, _>("last_attempt") .map(|t| LocalTime::from_millis(t as u128)); let last_success = row .read::, _>("last_success") .map(|t| LocalTime::from_millis(t as u128)); - let banned = row.read::("banned").is_positive(); + let banned = row.try_read::("banned")?.is_positive(); addrs.push(KnownAddress { addr, @@ -212,7 +212,7 @@ impl Store for Database { .into_iter() .next() .ok_or(Error::NoRows)??; - let count = row.read::(0) as usize; + let count = row.try_read::(0)? as usize; Ok(count) } @@ -224,7 +224,7 @@ impl Store for Database { .into_iter() .next() .ok_or(Error::NoRows)??; - let count = row.read::(0) as usize; + let count = row.try_read::(0)? as usize; Ok(count) } @@ -299,17 +299,17 @@ impl Store for Database { let mut entries = Vec::new(); while let Some(Ok(row)) = stmt.next() { - let node = row.read::("node"); - let _typ = row.read::("type"); - let addr = row.read::("value"); - let source = row.read::("source"); - let last_success = row.read::, _>("last_success"); - let last_attempt = row.read::, _>("last_attempt"); + let node = row.try_read::("node")?; + let _typ = row.try_read::("type")?; + let addr = row.try_read::("value")?; + let source = row.try_read::("source")?; + let last_success = row.try_read::, _>("last_success")?; + let last_attempt = row.try_read::, _>("last_attempt")?; let last_success = last_success.map(|t| LocalTime::from_millis(t as u128)); let last_attempt = last_attempt.map(|t| LocalTime::from_millis(t as u128)); - let version = row.read::("version").try_into()?; - let banned = row.read::("banned").is_positive(); - let penalty = row.read::("penalty"); + let version = row.try_read::("version")?.try_into()?; + let banned = row.try_read::("banned")?.is_positive(); + let penalty = row.try_read::("penalty")?; let penalty = Penalty(penalty as u8); // Clamped at `u8::MAX`. entries.push(AddressEntry { diff --git a/crates/radicle/src/node/policy/config.rs b/crates/radicle/src/node/policy/config.rs index 7a34a90e..ec673d51 100644 --- a/crates/radicle/src/node/policy/config.rs +++ b/crates/radicle/src/node/policy/config.rs @@ -106,9 +106,20 @@ impl Config { let nodes = self .follow_policies() .map_err(|err| FailedNodes { rid: *rid, err })?; - let mut followed: HashSet<_> = nodes - .filter_map(|node| (node.policy == Policy::Allow).then_some(node.nid)) - .collect(); + + let mut followed: HashSet<_> = HashSet::new(); + for node in nodes { + let node = match node { + Ok(node) => node, + Err(err) => { + log::warn!(target: "service", "Failed to read follow policy: {err}"); + continue; + } + }; + if node.policy == Policy::Allow { + followed.insert(node.nid); + } + } if let Ok(repo) = storage.repository(*rid) { let delegates = repo diff --git a/crates/radicle/src/node/policy/store.rs b/crates/radicle/src/node/policy/store.rs index ce881a95..bfe0946a 100644 --- a/crates/radicle/src/node/policy/store.rs +++ b/crates/radicle/src/node/policy/store.rs @@ -257,13 +257,13 @@ impl Store { stmt.bind((1, id))?; if let Some(Ok(row)) = stmt.into_iter().next() { - let alias = row.read::<&str, _>("alias"); + let alias = row.try_read::<&str, _>("alias")?; let alias = alias .is_empty() .not() .then_some(alias.to_owned()) .and_then(|s| Alias::from_str(&s).ok()); - let policy = row.read::("policy"); + let policy = row.try_read::("policy")?; return Ok(Some(FollowPolicy { nid: *id, @@ -283,9 +283,9 @@ impl Store { stmt.bind((1, id))?; if let Some(Ok(row)) = stmt.into_iter().next() { - let policy = match row.read::("policy") { + let policy = match row.try_read::("policy")? { Policy::Allow => SeedingPolicy::Allow { - scope: row.read::("scope"), + scope: row.try_read::("scope")?, }, Policy::Block => SeedingPolicy::Block, }; @@ -329,25 +329,38 @@ pub struct FollowPolicies<'a> { } impl Iterator for FollowPolicies<'_> { - type Item = FollowPolicy; + type Item = Result; fn next(&mut self) -> Option { let row = self.inner.next()?; let Ok(row) = row else { return self.next() }; - let id = row.read("id"); - let alias = row.read::<&str, _>("alias").to_owned(); + + let id = match row.try_read("id") { + Ok(id) => id, + Err(err) => return Some(Err(err.into())), + }; + + let alias = match row.try_read::<&str, _>("alias") { + Ok(alias) => alias.to_owned(), + Err(err) => return Some(Err(err.into())), + }; + let alias = alias .is_empty() .not() .then_some(alias.to_owned()) .and_then(|s| Alias::from_str(&s).ok()); - let policy = row.read::("policy"); - Some(FollowPolicy { + let policy = match row.try_read::("policy") { + Ok(policy) => policy, + Err(err) => return Some(Err(err.into())), + }; + + Some(Ok(FollowPolicy { nid: id, alias, policy, - }) + })) } } @@ -356,19 +369,35 @@ pub struct SeedPolicies<'a> { } impl Iterator for SeedPolicies<'_> { - type Item = SeedPolicy; + type Item = Result; fn next(&mut self) -> Option { let row = self.inner.next()?; let Ok(row) = row else { return self.next() }; - let id = row.read("id"); - let policy = match row.read::("policy") { - Policy::Allow => SeedingPolicy::Allow { - scope: row.read::("scope"), - }, - Policy::Block => SeedingPolicy::Block, + + let id = match row.try_read("id") { + Ok(id) => id, + Err(err) => return Some(Err(err.into())), }; - Some(SeedPolicy { rid: id, policy }) + + let policy = match row.try_read::("policy") { + Ok(policy) => policy, + Err(err) => return Some(Err(err.into())), + }; + + match policy { + Policy::Allow => match row.try_read::("scope") { + Ok(scope) => Some(Ok(SeedPolicy { + rid: id, + policy: SeedingPolicy::Allow { scope }, + })), + Err(err) => Some(Err(err.into())), + }, + Policy::Block => Some(Ok(SeedPolicy { + rid: id, + policy: SeedingPolicy::Block, + })), + } } } @@ -457,9 +486,9 @@ mod test { assert!(db.follow(id, None).unwrap()); } let mut entries = db.follow_policies().unwrap(); - assert_matches!(entries.next(), Some(FollowPolicy { nid, .. }) if nid == ids[0]); - assert_matches!(entries.next(), Some(FollowPolicy { nid, .. }) if nid == ids[1]); - assert_matches!(entries.next(), Some(FollowPolicy { nid, .. }) if nid == ids[2]); + assert_matches!(entries.next(), Some(Ok(FollowPolicy { nid, .. })) if nid == ids[0]); + assert_matches!(entries.next(), Some(Ok(FollowPolicy { nid, .. })) if nid == ids[1]); + assert_matches!(entries.next(), Some(Ok(FollowPolicy { nid, .. })) if nid == ids[2]); } #[test] @@ -471,9 +500,9 @@ mod test { assert!(db.seed(id, Scope::All).unwrap()); } let mut entries = db.seed_policies().unwrap(); - assert_matches!(entries.next(), Some(SeedPolicy { rid, .. }) if rid == ids[0]); - assert_matches!(entries.next(), Some(SeedPolicy { rid, .. }) if rid == ids[1]); - assert_matches!(entries.next(), Some(SeedPolicy { rid, .. }) if rid == ids[2]); + assert_matches!(entries.next(), Some(Ok(SeedPolicy { rid, .. })) if rid == ids[0]); + assert_matches!(entries.next(), Some(Ok(SeedPolicy { rid, .. })) if rid == ids[1]); + assert_matches!(entries.next(), Some(Ok(SeedPolicy { rid, .. })) if rid == ids[2]); } #[test] diff --git a/crates/radicle/src/node/refs/store.rs b/crates/radicle/src/node/refs/store.rs index 56c67cf7..deae273f 100644 --- a/crates/radicle/src/node/refs/store.rs +++ b/crates/radicle/src/node/refs/store.rs @@ -154,7 +154,7 @@ impl Store for Database { .into_iter() .next() .ok_or(Error::NoRows)??; - let count = row.read::(0) as usize; + let count = row.try_read::(0)? as usize; Ok(count) } diff --git a/crates/radicle/src/node/routing.rs b/crates/radicle/src/node/routing.rs index 403e7c0f..319d21a7 100644 --- a/crates/radicle/src/node/routing.rs +++ b/crates/radicle/src/node/routing.rs @@ -108,7 +108,7 @@ impl Store for Database { stmt.bind((2, node))?; if let Some(Ok(row)) = stmt.into_iter().next() { - return Ok(Some(row.read::("timestamp"))); + return Ok(Some(row.try_read::("timestamp")?)); } Ok(None) }