mirror of
https://seed.radicle.dev/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5.git
synced 2026-11-15 15:50:38 +08:00
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
107 lines
2.6 KiB
Rust
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(())
|
|
}
|