refactor module functions into methods on store types
This commit is contained in:
@@ -15,11 +15,10 @@ use signed_core::{
|
||||
use signed_git::{Nip34Binding, PatchParser, Repo};
|
||||
use signed_nostr::UniversalSigner;
|
||||
|
||||
use crate::backend::{
|
||||
Backend, BackendEvent, grasp_base_url, grasp06_prs_url, pr_clone_urls, require_relay_accepted,
|
||||
user_grasp_list_servers,
|
||||
};
|
||||
use crate::backend::{Backend, BackendEvent};
|
||||
use crate::bootstrap::user_grasp_list_servers;
|
||||
use crate::checkouts::CheckoutsStore;
|
||||
use crate::push::{GraspPush, PushOutcome, grasp_base_url, grasp06_prs_url, pr_clone_urls};
|
||||
use crate::repos::RepoListStore;
|
||||
|
||||
/// Maximum size of one patch event.
|
||||
@@ -700,19 +699,12 @@ impl RepoStore {
|
||||
return;
|
||||
};
|
||||
|
||||
let series: Vec<String> = PatchParser::split_patch_series(&patch)
|
||||
.into_iter()
|
||||
.map(str::to_owned)
|
||||
.collect();
|
||||
let series = PatchSeries::parse(&patch);
|
||||
|
||||
if let Some(oversized) = series
|
||||
.iter()
|
||||
.find(|part| part.len() > MAX_PATCH_EVENT_BYTES)
|
||||
{
|
||||
if let Some(oversized) = series.oversized_length() {
|
||||
self.last_error = Some(format!(
|
||||
"patch too large ({} bytes; NIP-34 suggests keeping each patch under {} bytes)",
|
||||
oversized.len(),
|
||||
MAX_PATCH_EVENT_BYTES
|
||||
oversized, MAX_PATCH_EVENT_BYTES
|
||||
));
|
||||
cx.notify();
|
||||
return;
|
||||
@@ -720,11 +712,7 @@ impl RepoStore {
|
||||
|
||||
// The tip of the series is its last commit.
|
||||
// `git format-patch` orders patches oldest first.
|
||||
let Some(current_commit) = series
|
||||
.last()
|
||||
.and_then(|part| patch_current_commit(part))
|
||||
.and_then(|hex| hex.parse::<Sha1Hash>().ok())
|
||||
else {
|
||||
let Some(current_commit) = series.tip_commit() else {
|
||||
self.last_error = Some(
|
||||
"Patch must be `git format-patch` output with a `From <commit-id>` header".into(),
|
||||
);
|
||||
@@ -930,11 +918,10 @@ impl RepoStore {
|
||||
}
|
||||
}
|
||||
|
||||
let publish_result: Result<Event, Error> = async {
|
||||
let output = client.send_event(&event).broadcast().await?;
|
||||
require_relay_accepted(output, event)
|
||||
}
|
||||
.await;
|
||||
let publish_result = {
|
||||
let pusher = GraspPush::new(client.clone(), signer.clone());
|
||||
pusher.send_accepted(event).await
|
||||
};
|
||||
|
||||
let pr_event = match publish_result {
|
||||
Ok(event) => event,
|
||||
@@ -1037,29 +1024,18 @@ impl RepoStore {
|
||||
return;
|
||||
}
|
||||
|
||||
let series: Vec<String> = PatchParser::split_patch_series(&patch)
|
||||
.into_iter()
|
||||
.map(str::to_owned)
|
||||
.collect();
|
||||
if let Some(oversized) = series
|
||||
.iter()
|
||||
.find(|part| part.len() > MAX_PATCH_EVENT_BYTES)
|
||||
{
|
||||
let series = PatchSeries::parse(&patch);
|
||||
if let Some(oversized) = series.oversized_length() {
|
||||
self.last_error = Some(format!(
|
||||
"patch too large ({} bytes; NIP-34 suggests keeping each patch under {} bytes)",
|
||||
oversized.len(),
|
||||
MAX_PATCH_EVENT_BYTES
|
||||
oversized, MAX_PATCH_EVENT_BYTES
|
||||
));
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
|
||||
// The new tip of the PR is the last commit of the series.
|
||||
let Some(current_commit) = series
|
||||
.last()
|
||||
.and_then(|part| patch_current_commit(part))
|
||||
.and_then(|hex| hex.parse::<Sha1Hash>().ok())
|
||||
else {
|
||||
let Some(current_commit) = series.tip_commit() else {
|
||||
self.last_error = Some(
|
||||
"Patch must be `git format-patch` output with a `From <commit-id>` header".into(),
|
||||
);
|
||||
@@ -1129,12 +1105,10 @@ impl RepoStore {
|
||||
}
|
||||
};
|
||||
|
||||
let publish_result: Result<Event, Error> = async {
|
||||
let event = builder.finalize_async(&signer).await?;
|
||||
let output = client.send_event(&event).broadcast().await?;
|
||||
require_relay_accepted(output, event)
|
||||
}
|
||||
.await;
|
||||
let publish_result = {
|
||||
let pusher = GraspPush::new(client.clone(), signer.clone());
|
||||
pusher.publish_one(builder).await
|
||||
};
|
||||
|
||||
if let Err(e) = publish_result {
|
||||
return this.update(cx, |this, cx| {
|
||||
@@ -1216,38 +1190,10 @@ impl RepoStore {
|
||||
return self.action_error("Repository announcement is not loaded yet", cx);
|
||||
};
|
||||
|
||||
self.pushing = true;
|
||||
self.last_error = None;
|
||||
self.last_push_warning = None;
|
||||
cx.notify();
|
||||
|
||||
let backend = Backend::global(cx);
|
||||
let push = backend.update(cx, |backend, cx| backend.push_repository(announcement, cx));
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
let result = push.await;
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
this.pushing = false;
|
||||
|
||||
match &result {
|
||||
Ok(outcome) => {
|
||||
this.last_error = None;
|
||||
// A push only some grasp servers accepted is a warning:
|
||||
// the repo is out of sync on the rest until it is republished.
|
||||
this.last_push_warning = outcome.partial_warning();
|
||||
}
|
||||
Err(e) => {
|
||||
this.last_error = Some(format!("Push failed: {e}"));
|
||||
this.last_push_warning = None;
|
||||
}
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
})?;
|
||||
|
||||
result.map(|_| ())
|
||||
})
|
||||
self.run_push(push, None, cx)
|
||||
}
|
||||
|
||||
pub fn push_checkout(
|
||||
@@ -1273,17 +1219,30 @@ impl RepoStore {
|
||||
// The checkout may be on a side branch.
|
||||
let head = self.head.clone();
|
||||
|
||||
self.pushing = true;
|
||||
self.last_error = None;
|
||||
self.last_push_warning = None;
|
||||
cx.notify();
|
||||
|
||||
let checkouts = CheckoutsStore::global(cx);
|
||||
let backend = Backend::global(cx);
|
||||
let push = backend.update(cx, |backend, cx| {
|
||||
backend.push_checkout(announcement, path.clone(), head, cx)
|
||||
});
|
||||
|
||||
self.run_push(push, Some((addr, path)), cx)
|
||||
}
|
||||
|
||||
/// Run a backend push task, tracking progress in [`Self::pushing`] and
|
||||
/// the outcome in [`Self::last_error`] and [`Self::last_push_warning`].
|
||||
///
|
||||
/// `pushed_checkout` names the checkout whose ready-to-push statuses
|
||||
/// should be recomputed after the remote moved.
|
||||
fn run_push(
|
||||
&mut self,
|
||||
push: Task<Result<PushOutcome, Error>>,
|
||||
pushed_checkout: Option<(RepoAddr, PathBuf)>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<(), Error>> {
|
||||
self.pushing = true;
|
||||
self.last_error = None;
|
||||
self.last_push_warning = None;
|
||||
cx.notify();
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
let result = push.await;
|
||||
|
||||
@@ -1296,10 +1255,12 @@ impl RepoStore {
|
||||
// A push only some grasp servers accepted is a warning:
|
||||
// the repo is out of sync on the rest until it is republished.
|
||||
this.last_push_warning = outcome.partial_warning();
|
||||
// The remote moved, so recompute the ready-to-push statuses.
|
||||
checkouts.update(cx, |store, cx| {
|
||||
store.checkout_pushed(&addr, &path, cx);
|
||||
});
|
||||
if let Some((addr, path)) = &pushed_checkout {
|
||||
// The remote moved, so recompute the ready-to-push statuses.
|
||||
CheckoutsStore::global(cx).update(cx, |store, cx| {
|
||||
store.checkout_pushed(addr, path, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
this.last_error = Some(format!("Push failed: {e}"));
|
||||
@@ -1431,12 +1392,8 @@ impl RepoStore {
|
||||
};
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.spawn(async move |this, cx| {
|
||||
let publish_result: Result<Event, Error> = async {
|
||||
let event = builder.finalize_async(&signer).await?;
|
||||
let output = client.send_event(&event).broadcast().await?;
|
||||
require_relay_accepted(output, event)
|
||||
}
|
||||
.await;
|
||||
let pusher = GraspPush::new(client, signer);
|
||||
let publish_result = pusher.publish_one(builder).await;
|
||||
|
||||
if let Err(e) = publish_result {
|
||||
this.update(cx, |this, cx| {
|
||||
@@ -1497,6 +1454,46 @@ fn patch_current_commit(patch: &str) -> Option<&str> {
|
||||
hex.split_whitespace().next().filter(|hex| hex.len() == 40)
|
||||
}
|
||||
|
||||
/// A `git format-patch` series with the facts derived from its parts.
|
||||
struct PatchSeries {
|
||||
parts: Vec<String>,
|
||||
}
|
||||
|
||||
impl PatchSeries {
|
||||
fn parse(patch: &str) -> Self {
|
||||
Self {
|
||||
parts: PatchParser::split_patch_series(patch)
|
||||
.into_iter()
|
||||
.map(str::to_owned)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The byte length of the first part over the NIP-34 size suggestion.
|
||||
fn oversized_length(&self) -> Option<usize> {
|
||||
self.parts
|
||||
.iter()
|
||||
.map(String::len)
|
||||
.find(|length| *length > MAX_PATCH_EVENT_BYTES)
|
||||
}
|
||||
|
||||
/// The tip of the series is its last commit;
|
||||
/// `git format-patch` orders patches oldest first.
|
||||
fn tip_commit(&self) -> Option<Sha1Hash> {
|
||||
self.parts
|
||||
.last()
|
||||
.and_then(|part| patch_current_commit(part))
|
||||
.and_then(|hex| hex.parse::<Sha1Hash>().ok())
|
||||
}
|
||||
|
||||
fn commit_of(&self, index: usize) -> Option<&str> {
|
||||
self.parts
|
||||
.get(index)
|
||||
.and_then(|part| patch_current_commit(part))
|
||||
.filter(|hex| hex.len() == 40)
|
||||
}
|
||||
}
|
||||
|
||||
/// Publish a `git format-patch` series as chained kind-1617 events.
|
||||
///
|
||||
/// Returns the root event, the one a PR references.
|
||||
@@ -1507,15 +1504,16 @@ async fn publish_patch_series(
|
||||
addr: &RepoAddr,
|
||||
owner: PublicKey,
|
||||
euc: Option<&str>,
|
||||
series: &[String],
|
||||
series: &PatchSeries,
|
||||
first_marker: &str,
|
||||
reply_to: Option<EventId>,
|
||||
) -> Result<Event, Error> {
|
||||
let pusher = GraspPush::new(client.clone(), signer.clone());
|
||||
let mut root: Option<Event> = None;
|
||||
let mut previous = reply_to;
|
||||
|
||||
for (ix, part) in series.iter().enumerate() {
|
||||
let Some(commit) = patch_current_commit(part).filter(|hex| hex.len() == 40) else {
|
||||
for (ix, part) in series.parts.iter().enumerate() {
|
||||
let Some(commit) = series.commit_of(ix) else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"patch {} of the series has no `From <commit-id>` header",
|
||||
ix + 1
|
||||
@@ -1557,9 +1555,7 @@ async fn publish_patch_series(
|
||||
}
|
||||
|
||||
let builder = EventBuilder::new(Kind::GitPatch, part.clone()).tags(tags);
|
||||
let event = builder.finalize_async(signer).await?;
|
||||
let output = client.send_event(&event).broadcast().await?;
|
||||
let event = require_relay_accepted(output, event)?;
|
||||
let event = pusher.publish_one(builder).await?;
|
||||
|
||||
if root.is_none() {
|
||||
root = Some(event.clone());
|
||||
|
||||
Reference in New Issue
Block a user