Files
heartwood/radicle-cli/src/commands/patch/edit.rs
T
Fintan Halpenny 985b0af3f6 radicle: implement caching for issues and patches
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 <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
2024-02-23 16:03:21 +01:00

107 lines
2.6 KiB
Rust

use super::*;
use radicle::cob::{self, patch, resolve_embed};
use radicle::crypto;
use radicle::prelude::*;
use radicle::storage::git::Repository;
use crate::terminal as term;
pub fn run(
patch_id: &PatchId,
revision_id: Option<patch::RevisionId>,
message: term::patch::Message,
profile: &Profile,
repository: &Repository,
) -> anyhow::Result<()> {
let signer = term::signer(profile)?;
let mut patches = profile.patches_mut(repository)?;
let Ok(patch) = patches.get_mut(patch_id) else {
anyhow::bail!("Patch `{patch_id}` not found");
};
let (title, description) = term::patch::get_edit_message(message, &patch)?;
match revision_id {
Some(id) => edit_revision(patch, id, title, description, repository, &signer),
None => edit_root(patch, title, description, repository, &signer),
}
}
fn edit_root<G>(
mut patch: patch::PatchMut<'_, '_, Repository, cob::cache::StoreWriter>,
title: String,
description: String,
repository: &Repository,
signer: &G,
) -> anyhow::Result<()>
where
G: crypto::Signer,
{
let title = if title != patch.title() {
Some(title)
} else {
None
};
let description = if description != patch.description() {
Some(description)
} else {
None
};
if title.is_none() && description.is_none() {
// Nothing to do.
return Ok(());
}
let (root, _) = patch.root();
let target = patch.target();
let embeds = patch
.embeds()
.iter()
.filter_map(|embed| resolve_embed(repository, embed.clone()))
.collect::<Vec<_>>();
patch.transaction("Edit root", signer, |tx| {
if let Some(t) = title {
tx.edit(t, target)?;
}
if let Some(d) = description {
tx.edit_revision(root, d, embeds)?;
}
Ok(())
})?;
Ok(())
}
fn edit_revision<G>(
mut patch: patch::PatchMut<'_, '_, Repository, cob::cache::StoreWriter>,
revision: patch::RevisionId,
mut title: String,
description: String,
repository: &Repository,
signer: &G,
) -> anyhow::Result<()>
where
G: crypto::Signer,
{
let embeds = patch
.embeds()
.iter()
.filter_map(|embed| resolve_embed(repository, embed.clone()))
.collect::<Vec<_>>();
let description = if description.is_empty() {
title
} else {
title.push('\n');
title.push_str(&description);
title
};
patch.transaction("Edit revision", signer, |tx| {
tx.edit_revision(revision, description, embeds)?;
Ok(())
})?;
Ok(())
}