Skip to content

Commit

Permalink
radicle: implement caching for issues and patches
Browse files Browse the repository at this point in the history
Implement caching for the `Issue` and `Patch` COB. This is achieved by
having the `Transaction::initial` and `Transaction::commit` methods
take a cache which can update the newly created/updated object. The
`Store::remove` method also takes the cache for removing the object
from the cache, as well as the repository.

This meant that the `*Mut` types for the respective COBs are required
to carry a cache alongside the repository store. Identities will not
be cached, and so this is always set to `NoCache` -- which performs no
caching, and always succeeds.

To perform cache reading for issues and patches, the `cache::Issues`
and `cache::Patches` traits are introduced. They capture the minimal
amount of methods that are needed for the `rad issue` and `rad patch`
CLI commands.

The implementor for each of these traits is there respective `Cache`
types, which combines their backing repository store and the SQLite
database. They both provide convenience methods:
- `create` (and `draft` for patches)
- `get_mut`
- `remove`
which can be used instead of their repository store counterparts.

All uses of the repository stores (where needed) are replaced with
`Cache` types.

Signed-off-by: Fintan Halpenny <[email protected]>
X-Clacks-Overhead: GNU Terry Pratchett
  • Loading branch information
FintanH authored and cloudhead committed Feb 23, 2024
1 parent b26842d commit 985b0af
Show file tree
Hide file tree
Showing 44 changed files with 2,632 additions and 289 deletions.
7 changes: 4 additions & 3 deletions radicle-cli/src/commands/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use std::str::FromStr;
use std::time;

use anyhow::anyhow;
use radicle::issue::cache::Issues as _;
use radicle::patch::cache::Patches as _;
use thiserror::Error;

use radicle::cob;
use radicle::git::raw;
use radicle::identity::doc;
use radicle::identity::doc::{DocError, RepoId};
Expand Down Expand Up @@ -186,8 +187,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
info.push([term::format::bold(proj.name()).into()]);
info.push([term::format::italic(proj.description()).into()]);

let issues = cob::issue::Issues::open(&repo)?.counts()?;
let patches = cob::patch::Patches::open(&repo)?.counts()?;
let issues = profile.issues(&repo)?.counts()?;
let patches = profile.patches(&repo)?.counts()?;

info.push([term::Line::spaced([
term::format::tertiary(issues.open).into(),
Expand Down
12 changes: 6 additions & 6 deletions radicle-cli/src/commands/inbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use anyhow::anyhow;

use localtime::LocalTime;
use radicle::identity::Identity;
use radicle::issue::Issues;
use radicle::issue::cache::Issues as _;
use radicle::node::notifications;
use radicle::node::notifications::*;
use radicle::patch::Patches;
use radicle::patch::cache::Patches as _;
use radicle::prelude::{Profile, RepoId};
use radicle::storage::{ReadRepository, ReadStorage};
use radicle::{cob, Storage};
Expand Down Expand Up @@ -233,8 +233,8 @@ where
let (_, head) = repo.head()?;
let doc = repo.identity_doc()?;
let proj = doc.project()?;
let issues = Issues::open(&repo)?;
let patches = Patches::open(&repo)?;
let issues = profile.issues(&repo)?;
let patches = profile.patches(&repo)?;

let mut notifs = notifs.by_repo(&rid, sort_by.field)?.collect::<Vec<_>>();
if !sort_by.reverse {
Expand Down Expand Up @@ -406,13 +406,13 @@ fn show(

match n.kind {
NotificationKind::Cob { type_name, id } if type_name == *cob::issue::TYPENAME => {
let issues = Issues::open(&repo)?;
let issues = profile.issues(&repo)?;
let issue = issues.get(&id)?.unwrap();

term::issue::show(&issue, &id, term::issue::Format::default(), profile)?;
}
NotificationKind::Cob { type_name, id } if type_name == *cob::patch::TYPENAME => {
let patches = Patches::open(&repo)?;
let patches = profile.patches(&repo)?;
let patch = patches.get(&id)?.unwrap();

term::patch::show(&patch, &id, false, &repo, None, profile)?;
Expand Down
45 changes: 29 additions & 16 deletions radicle-cli/src/commands/issue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ use anyhow::{anyhow, Context as _};

use radicle::cob::common::{Label, Reaction};
use radicle::cob::issue;
use radicle::cob::issue::{CloseReason, Issues, State};
use radicle::cob::issue::{CloseReason, State};
use radicle::cob::thread;
use radicle::crypto::Signer;
use radicle::issue::cache::Issues as _;
use radicle::prelude::Did;
use radicle::profile;
use radicle::storage;
use radicle::storage::{WriteRepository, WriteStorage};
use radicle::storage::{ReadRepository, WriteRepository, WriteStorage};
use radicle::Profile;
use radicle::{cob, Node};

Expand Down Expand Up @@ -425,7 +426,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
| Operation::Label { .. }
);

let mut issues = Issues::open(&repo)?;
let mut issues = profile.issues_mut(&repo)?;

match options.op {
Operation::Edit {
Expand Down Expand Up @@ -546,7 +547,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
issue.label(labels, &signer)?;
}
Operation::List { assigned, state } => {
list(&issues, &assigned, &state, &profile)?;
list(issues, &assigned, &state, &profile)?;
}
Operation::Delete { id } => {
let id = id.resolve(&repo.backend)?;
Expand All @@ -562,13 +563,16 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
Ok(())
}

fn list<R: WriteRepository + cob::Store>(
issues: &Issues<R>,
fn list<C>(
cache: C,
assigned: &Option<Assigned>,
state: &Option<State>,
profile: &profile::Profile,
) -> anyhow::Result<()> {
if issues.is_empty()? {
) -> anyhow::Result<()>
where
C: issue::cache::Issues,
{
if cache.is_empty()? {
term::print(term::format::italic("Nothing to show."));
return Ok(());
}
Expand All @@ -580,7 +584,8 @@ fn list<R: WriteRepository + cob::Store>(
};

let mut all = Vec::new();
for result in issues.all()? {
let issues = cache.list()?;
for result in issues {
let Ok((id, issue)) = result else {
// Skip issues that failed to load.
continue;
Expand Down Expand Up @@ -664,24 +669,28 @@ fn list<R: WriteRepository + cob::Store>(
Ok(())
}

fn open<R: WriteRepository + cob::Store, G: Signer>(
fn open<R, G>(
title: Option<String>,
description: Option<String>,
labels: Vec<Label>,
assignees: Vec<Did>,
options: &Options,
issues: &mut Issues<R>,
cache: &mut issue::Cache<issue::Issues<'_, R>, cob::cache::StoreWriter>,
signer: &G,
profile: &Profile,
) -> anyhow::Result<()> {
) -> anyhow::Result<()>
where
R: ReadRepository + WriteRepository + cob::Store,
G: Signer,
{
let (title, description) = if let (Some(t), Some(d)) = (title.as_ref(), description.as_ref()) {
(t.to_owned(), d.to_owned())
} else if let Some((t, d)) = term::issue::get_title_description(title, description)? {
(t, d)
} else {
anyhow::bail!("aborting issue creation due to empty title or description");
};
let issue = issues.create(
let issue = cache.create(
&title,
description,
labels.as_slice(),
Expand All @@ -696,14 +705,18 @@ fn open<R: WriteRepository + cob::Store, G: Signer>(
Ok(())
}

fn edit<'a, 'g, R: WriteRepository + cob::Store, G: radicle::crypto::Signer>(
issues: &'a mut issue::Issues<'a, R>,
fn edit<'a, 'g, R, G>(
issues: &'g mut issue::Cache<issue::Issues<'a, R>, cob::cache::StoreWriter>,
repo: &storage::git::Repository,
id: Rev,
title: Option<String>,
description: Option<String>,
signer: &G,
) -> anyhow::Result<issue::IssueMut<'a, 'g, R>> {
) -> anyhow::Result<issue::IssueMut<'a, 'g, R, cob::cache::StoreWriter>>
where
R: WriteRepository + ReadRepository + cob::Store,
G: radicle::crypto::Signer,
{
let id = id.resolve(&repo.backend)?;
let mut issue = issues.get_mut(&id)?;
let (root, _) = issue.root();
Expand Down
45 changes: 13 additions & 32 deletions radicle-cli/src/commands/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use anyhow::anyhow;

use radicle::cob::patch::PatchId;
use radicle::cob::{patch, Label};
use radicle::patch::cache::Patches as _;
use radicle::storage::git::transport;
use radicle::{prelude::*, Node};

Expand Down Expand Up @@ -179,27 +180,6 @@ pub struct LabelOptions {
pub delete: BTreeSet<Label>,
}

pub struct Filter(fn(&patch::State) -> bool);

impl Filter {
/// Match everything.
fn all() -> Self {
Self(|_| true)
}
}

impl Default for Filter {
fn default() -> Self {
Self(|state| matches!(state, patch::State::Open { .. }))
}
}

impl std::fmt::Debug for Filter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Filter(..)")
}
}

#[derive(Debug)]
pub enum Operation {
Show {
Expand Down Expand Up @@ -250,7 +230,7 @@ pub enum Operation {
opts: LabelOptions,
},
List {
filter: Filter,
filter: Option<patch::Status>,
},
Edit {
patch_id: Rev,
Expand Down Expand Up @@ -311,7 +291,7 @@ impl Args for Options {
let mut patch_id = None;
let mut revision_id = None;
let mut message = Message::default();
let mut filter = Filter::default();
let mut filter = Some(patch::Status::Open);
let mut diff = false;
let mut debug = false;
let mut undo = false;
Expand Down Expand Up @@ -487,19 +467,19 @@ impl Args for Options {

// List options.
Long("all") => {
filter = Filter::all();
filter = None;
}
Long("draft") => {
filter = Filter(|s| s == &patch::State::Draft);
filter = Some(patch::Status::Draft);
}
Long("archived") => {
filter = Filter(|s| s == &patch::State::Archived);
filter = Some(patch::Status::Archived);
}
Long("merged") => {
filter = Filter(|s| matches!(s, patch::State::Merged { .. }));
filter = Some(patch::Status::Merged);
}
Long("open") => {
filter = Filter(|s| matches!(s, patch::State::Open { .. }));
filter = Some(patch::Status::Open);
}
Long("authored") => {
authored = true;
Expand Down Expand Up @@ -662,12 +642,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
transport::local::register(profile.storage.clone());

match options.op {
Operation::List { filter: Filter(f) } => {
Operation::List { filter } => {
let mut authors: BTreeSet<Did> = options.authors.iter().cloned().collect();
if options.authored {
authors.insert(profile.did());
}
list::run(f, authors, &repository, &profile)?;
list::run(filter.as_ref(), authors, &repository, &profile)?;
}
Operation::Show {
patch_id,
Expand All @@ -694,7 +674,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
.map(|rev| rev.resolve::<radicle::git::Oid>(&repository.backend))
.transpose()?
.map(patch::RevisionId::from);
diff::run(&patch_id, revision_id, &repository)?;
diff::run(&patch_id, revision_id, &repository, &profile)?;
}
Operation::Update {
ref patch_id,
Expand Down Expand Up @@ -742,6 +722,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
revision_id,
&repository,
&workdir,
&profile,
opts,
)?;
}
Expand Down Expand Up @@ -801,7 +782,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
label::run(&patch_id, add, delete, &profile, &repository)?;
}
Operation::Set { patch_id } => {
let patches = radicle::cob::patch::Patches::open(&repository)?;
let patches = profile.patches(&repository)?;
let patch_id = patch_id.resolve(&repository.backend)?;
let patch = patches
.get(&patch_id)?
Expand Down
3 changes: 1 addition & 2 deletions radicle-cli/src/commands/patch/archive.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use super::*;

use radicle::cob::patch;
use radicle::prelude::*;
use radicle::storage::git::Repository;

pub fn run(patch_id: &PatchId, profile: &Profile, repository: &Repository) -> anyhow::Result<()> {
let signer = term::signer(profile)?;
let mut patches = patch::Patches::open(repository)?;
let mut patches = profile.patches_mut(repository)?;
let Ok(mut patch) = patches.get_mut(patch_id) else {
anyhow::bail!("Patch `{patch_id}` not found");
};
Expand Down
2 changes: 1 addition & 1 deletion radicle-cli/src/commands/patch/assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn run(
repository: &Repository,
) -> anyhow::Result<()> {
let signer = term::signer(profile)?;
let mut patches = radicle::cob::patch::Patches::open(repository)?;
let mut patches = profile.patches_mut(repository)?;
let Ok(mut patch) = patches.get_mut(patch_id) else {
anyhow::bail!("Patch `{patch_id}` not found");
};
Expand Down
7 changes: 4 additions & 3 deletions radicle-cli/src/commands/patch/checkout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use git_ref_format::Qualified;
use radicle::cob::patch;
use radicle::cob::patch::RevisionId;
use radicle::git::RefString;
use radicle::patch::cache::Patches as _;
use radicle::patch::PatchId;
use radicle::storage::git::Repository;
use radicle::{git, rad};
use radicle::{git, rad, Profile};

use crate::terminal as term;

Expand All @@ -33,10 +34,10 @@ pub fn run(
revision_id: Option<RevisionId>,
stored: &Repository,
working: &git::raw::Repository,
profile: &Profile,
opts: Options,
) -> anyhow::Result<()> {
let patches = patch::Patches::open(stored)?;

let patches = profile.patches(stored)?;
let patch = patches
.get(patch_id)?
.ok_or_else(|| anyhow!("Patch `{patch_id}` not found"))?;
Expand Down
10 changes: 8 additions & 2 deletions radicle-cli/src/commands/patch/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use super::*;
use radicle::cob;
use radicle::cob::patch;
use radicle::cob::thread::CommentId;
use radicle::patch::ByRevision;
use radicle::prelude::*;
use radicle::storage::git::Repository;

Expand All @@ -19,10 +20,15 @@ pub fn run(
profile: &Profile,
) -> anyhow::Result<()> {
let signer = term::signer(profile)?;
let mut patches = patch::Patches::open(repo)?;
let mut patches = profile.patches_mut(repo)?;

let revision_id = revision_id.resolve::<cob::EntryId>(&repo.backend)?;
let (patch_id, patch, revision_id, revision) = patches
let ByRevision {
id: patch_id,
patch,
revision_id,
revision,
} = patches
.find_by_revision(&patch::RevisionId::from(revision_id))?
.ok_or_else(|| anyhow!("Patch revision `{revision_id}` not found"))?;
let mut patch = patch::PatchMut::new(patch_id, patch, &mut patches);
Expand Down
3 changes: 1 addition & 2 deletions radicle-cli/src/commands/patch/delete.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use radicle::cob::patch;
use radicle::prelude::*;
use radicle::storage::git::Repository;

use super::*;

pub fn run(patch_id: &PatchId, profile: &Profile, repository: &Repository) -> anyhow::Result<()> {
let signer = &term::signer(profile)?;
let patches = patch::Patches::open(repository)?;
let mut patches = profile.patches_mut(repository)?;
patches.remove(patch_id, signer)?;

Ok(())
Expand Down
Loading

0 comments on commit 985b0af

Please sign in to comment.