Refactor push logic into SyncState methods

This commit is contained in:
2026-10-14 08:11:14 +07:00
parent 62f06fc52b
commit d5acef65a6
2 changed files with 76 additions and 281 deletions
-88
View File
@@ -1,11 +1,6 @@
use std::path::Path;
use anyhow::Error;
use nostr::prelude::Url;
use nostr_sdk::prelude::*;
use crate::state::{GitRef, SyncState};
/// `ws://` grasp servers, like ngit, use `http(s)://<host>`.
pub(crate) fn grasp_base_url(relay: &RelayUrl) -> Option<String> {
let parsed = Url::parse(relay.as_str()).ok()?;
@@ -116,87 +111,4 @@ impl PushOutcome {
self.failure_summary()
))
}
/// All staged events carry the same refs; the newest timestamp wins on the relays.
fn keep_newest(&mut self, event: Event) {
if self
.state_event
.as_ref()
.is_none_or(|current| event.created_at > current.created_at)
{
self.state_event = Some(event);
}
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn push_staged_to_grasps<S>(
client: &Client,
signer: &S,
repo_id: &str,
refs: &[GitRef],
head: Option<&str>,
path: &Path,
owner: &str,
servers: &[RelayUrl],
push: impl Fn(&Path, &str, &str, &str) -> Result<(), Error>,
) -> PushOutcome
where
S: AsyncGetPublicKey + AsyncSignEvent + ?Sized,
{
let mut outcome = PushOutcome::default();
for relay in servers {
let Some(base) = grasp_base_url(relay) else {
outcome
.servers
.push(GraspServer::failed(relay, "no domain"));
continue;
};
let event = match SyncState::build(repo_id, refs, head)
.finalize_async(signer)
.await
{
Ok(event) => event,
Err(e) => {
let reason = format!("could not sign the state event: {e}");
outcome.servers.push(GraspServer::failed(relay, reason));
continue;
}
};
// The relay parks the accepted event in purgatory, authorizing the paired git push.
let staging = match client.add_relay(relay).and_connect().await {
Ok(_) => match client.send_event(&event).to([relay.clone()]).await {
Ok(output) if output.success.contains_key(relay) => Ok(()),
Ok(output) => Err(output
.failed
.get(relay)
.cloned()
.unwrap_or_else(|| "relay did not confirm the event".to_owned())),
Err(e) => Err(format!("could not send the state event to {relay}: {e}")),
},
Err(e) => Err(format!("could not add relay {relay}: {e}")),
};
if let Err(reason) = staging {
outcome.servers.push(GraspServer::failed(relay, reason));
continue;
}
match push(path, &base, owner, repo_id) {
Ok(()) => {
outcome.keep_newest(event);
outcome.servers.push(GraspServer::ok(relay.clone()));
}
Err(e) => {
outcome
.servers
.push(GraspServer::failed(relay, e.to_string()));
}
}
}
outcome
}