Move patch pagination into the patch service

This commit is contained in:
Rūdolfs Ošiņš
2026-07-03 11:33:12 +02:00
parent f7f4b2b4ba
commit 9bd20fa15c
3 changed files with 55 additions and 82 deletions
+1 -37
View File
@@ -19,8 +19,6 @@ use radicle_types::traits::patch::PatchesMut;
use crate::AppState;
use either::Either;
#[tauri::command]
pub async fn list_patches(
ctx: tauri::State<'_, AppState>,
@@ -32,43 +30,9 @@ pub async fn list_patches(
take: Option<usize>,
) -> Result<types::cobs::PaginatedQuery<Vec<models::patch::Patch>>, Error> {
let profile = ctx.profile();
let cursor = skip.unwrap_or(0);
let aliases = profile.aliases();
let counts = sqlite_service.counts(rid)?;
let patches = match status.clone() {
None => Either::Left(sqlite_service.list(rid)?),
Some(s) => Either::Right(sqlite_service.list_by_status(rid, s.into())?),
};
match take {
None => {
let content = patches
.map(|(id, patch)| models::patch::Patch::new(id, &patch, &aliases))
.collect::<Vec<_>>();
Ok::<_, Error>(cobs::PaginatedQuery {
cursor: 0,
more: false,
content,
})
}
Some(take) => {
let total = status.map_or_else(|| counts.total(), |status| counts[status.into()]);
let more = cursor + take < total;
let content = patches
.skip(cursor)
.take(take)
.map(|(id, patch)| models::patch::Patch::new(id, &patch, &aliases))
.collect::<Vec<_>>();
Ok::<_, Error>(cobs::PaginatedQuery {
cursor,
more,
content,
})
}
}
Ok(sqlite_service.list_paginated(rid, status, skip, take, &aliases)?)
}
#[tauri::command]
@@ -1,8 +1,10 @@
use radicle::identity;
use radicle::node::AliasStore;
use radicle::patch;
use radicle::patch::Patch;
use radicle::patch::PatchId;
use crate::cobs;
use crate::domain::patch::models;
pub trait PatchStorage {
@@ -39,4 +41,49 @@ pub trait PatchService {
&self,
rid: identity::RepoId,
) -> Result<models::patch::PatchCounts, models::patch::CountsError>;
/// One page of patch summaries, shared by all drivers so pagination
/// behaves identically everywhere. Rows are consumed lazily: with `take`
/// set, patches beyond the requested page are never deserialized.
/// Without `take` the full list is returned and `skip` is ignored.
fn list_paginated(
&self,
rid: identity::RepoId,
status: Option<cobs::query::PatchStatus>,
skip: Option<usize>,
take: Option<usize>,
aliases: &impl AliasStore,
) -> Result<cobs::PaginatedQuery<Vec<models::patch::Patch>>, models::patch::ListPatchesError>
{
let patches: Box<dyn Iterator<Item = (PatchId, Patch)> + '_> = match status {
None => Box::new(self.list(rid)?),
Some(status) => Box::new(self.list_by_status(rid, status.into())?),
};
let summary =
|(id, patch): (PatchId, Patch)| models::patch::Patch::new(id, &patch, aliases);
match take {
None => Ok(cobs::PaginatedQuery {
cursor: 0,
more: false,
content: patches.map(summary).collect::<Vec<_>>(),
}),
Some(take) => {
let cursor = skip.unwrap_or(0);
let mut content = patches
.skip(cursor)
.take(take + 1)
.map(summary)
.collect::<Vec<_>>();
let more = content.len() > take;
content.truncate(take);
Ok(cobs::PaginatedQuery {
cursor,
more,
content,
})
}
}
}
}
+7 -45
View File
@@ -15,9 +15,9 @@ use radicle::node::NodeId;
use radicle::{git, identity};
use radicle_types as types;
use radicle_types::cobs::CobOptions;
use radicle_types::cobs::FromRadicleAction;
use radicle_types::cobs::issue;
use radicle_types::cobs::issue::NewIssue;
use radicle_types::cobs::{self, FromRadicleAction};
use radicle_types::config::Version;
use radicle_types::domain::issue::service::Service as IssueService;
use radicle_types::domain::issue::traits::IssueService as _;
@@ -627,7 +627,7 @@ async fn issue_threads_handler(
struct PatchesBody {
pub rid: identity::RepoId,
pub skip: Option<usize>,
pub take: Option<isize>,
pub take: Option<usize>,
pub status: Option<types::cobs::query::PatchStatus>,
}
@@ -640,50 +640,12 @@ async fn patches_handler(
status,
}): Json<PatchesBody>,
) -> impl IntoResponse {
let profile = ctx.profile;
let cursor = skip.unwrap_or(0);
let aliases = profile.aliases();
let aliases = ctx.profile.aliases();
let page = ctx
.patches
.list_paginated(rid, status, skip, take, &aliases)?;
let patches = match status {
None => ctx.patches.list(rid)?.collect::<Vec<_>>(),
Some(s) => ctx
.patches
.list_by_status(rid, s.into())?
.collect::<Vec<_>>(),
};
if let Some(t) = take
&& t < 0
{
// Return all patches
let content = patches
.into_iter()
.map(|(id, patch)| models::patch::Patch::new(id, &patch, &aliases))
.collect::<Vec<_>>();
return Ok::<_, Error>(Json(cobs::PaginatedQuery {
cursor: 0,
more: false,
content,
}));
}
let take = take.unwrap_or(20) as usize;
let more = cursor + take < patches.len();
let patches = patches
.into_iter()
.skip(cursor)
.take(take)
.map(|(id, patch)| models::patch::Patch::new(id, &patch, &aliases))
.collect::<Vec<_>>();
Ok::<_, Error>(Json(cobs::PaginatedQuery {
cursor,
more,
content: patches,
}))
Ok::<_, Error>(Json(page))
}
#[derive(Serialize, Deserialize)]