Skip to content

Commit eb486ac

Browse files
committed
refactor(sync): consolidate session state into SessionCtx
- Introduce SessionCtx struct to group shared session configuration - Simplify live_loop and handle_incoming function signatures - Remove redundant loops in manifest drain and start signal logic - Fix mtime calculation by removing unnecessary i64 type casts - Consolidate symlink removal logic in apply_symlink - Replace manual IO error mapping with io::Error::other - Simplify live mode initialization sequence - Clean up event logging string formatting logic
1 parent a6b975a commit eb486ac

4 files changed

Lines changed: 64 additions & 84 deletions

File tree

src/agent.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,10 @@ pub async fn run(path: PathBuf) -> Result<()> {
8282

8383
// Drain client's manifest (we don't need to keep it; the client orchestrates).
8484
let mut client_count = 0usize;
85-
loop {
86-
match read_message(&mut reader).await? {
87-
Message::ManifestBegin => break,
88-
Message::Error(e) => anyhow::bail!("client: {e}"),
89-
m => anyhow::bail!("expected ManifestBegin, got {:?}", m),
90-
}
85+
match read_message(&mut reader).await? {
86+
Message::ManifestBegin => {}
87+
Message::Error(e) => anyhow::bail!("client: {e}"),
88+
m => anyhow::bail!("expected ManifestBegin, got {:?}", m),
9189
}
9290
loop {
9391
match read_message(&mut reader).await? {
@@ -209,7 +207,7 @@ pub async fn run(path: PathBuf) -> Result<()> {
209207
.ok()
210208
.map(|m| {
211209
use std::os::unix::fs::MetadataExt;
212-
m.mtime() * 1_000_000_000 + m.mtime_nsec() as i64
210+
m.mtime() * 1_000_000_000 + m.mtime_nsec()
213211
})
214212
.unwrap_or(0);
215213
suppress.mark_mtime(to, mt);
@@ -364,17 +362,12 @@ pub async fn run(path: PathBuf) -> Result<()> {
364362
}
365363

366364
// ── Live mode ──
367-
live_loop(
365+
let ctx = crate::peer::SessionCtx {
368366
root,
369-
reader,
370-
writer,
371367
mode,
372368
compress,
373-
false,
369+
is_client: false,
374370
ignores,
375-
suppress,
376-
pending,
377-
watcher_handle,
378-
)
379-
.await
371+
};
372+
live_loop(ctx, reader, writer, suppress, pending, watcher_handle).await
380373
}

src/peer.rs

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ fn lstat_mtime_ns(p: &Path) -> i64 {
3434
Ok(m) => m
3535
.mtime()
3636
.saturating_mul(1_000_000_000)
37-
.saturating_add(m.mtime_nsec() as i64),
37+
.saturating_add(m.mtime_nsec()),
3838
Err(_) => 0,
3939
}
4040
}
@@ -65,7 +65,7 @@ fn is_already_equal(root: &Path, entry: &Entry) -> bool {
6565
let mt = meta
6666
.mtime()
6767
.saturating_mul(1_000_000_000)
68-
.saturating_add(meta.mtime_nsec() as i64);
68+
.saturating_add(meta.mtime_nsec());
6969
if mt == entry.mtime {
7070
return true;
7171
}
@@ -116,10 +116,8 @@ pub fn apply_symlink(root: &Path, entry: &Entry) -> Result<()> {
116116
if let Some(parent) = full.parent() {
117117
fs::create_dir_all(parent)?;
118118
}
119-
if fs::symlink_metadata(&full).is_ok() {
120-
if fs::remove_file(&full).is_err() {
121-
let _ = fs::remove_dir_all(&full);
122-
}
119+
if fs::symlink_metadata(&full).is_ok() && fs::remove_file(&full).is_err() {
120+
let _ = fs::remove_dir_all(&full);
123121
}
124122
let target = entry
125123
.link_target
@@ -716,6 +714,17 @@ impl Suppression {
716714
// Live mode: a generic bidirectional loop driven by tokio::select.
717715
// ─────────────────────────────────────────────────────────────
718716

717+
/// Static per-session configuration shared between `live_loop`,
718+
/// `handle_incoming`, and helpers. Pulled out into a struct so those
719+
/// signatures stay narrow.
720+
pub struct SessionCtx {
721+
pub root: PathBuf,
722+
pub mode: SyncMode,
723+
pub compress: bool,
724+
pub is_client: bool,
725+
pub ignores: Arc<IgnoreStack>,
726+
}
727+
719728
fn directions(mode: SyncMode, is_client: bool) -> (bool, bool) {
720729
match (mode, is_client) {
721730
(SyncMode::Both, _) => (true, true),
@@ -727,13 +736,9 @@ fn directions(mode: SyncMode, is_client: bool) -> (bool, bool) {
727736
}
728737

729738
pub async fn live_loop<R, W>(
730-
root: PathBuf,
739+
ctx: SessionCtx,
731740
mut reader: R,
732741
writer: Arc<Mutex<W>>,
733-
mode: SyncMode,
734-
compress: bool,
735-
is_client: bool,
736-
ignores: Arc<IgnoreStack>,
737742
// Carried over from the init-sync apply phase: marks for every file we
738743
// wrote during initial sync stay valid here (TTL 60s) so the watcher's
739744
// FSEvents/inotify echoes for those writes are filtered, not bounced
@@ -749,7 +754,7 @@ where
749754
R: AsyncRead + AsyncReadExt + Unpin + Send + 'static,
750755
W: AsyncWrite + AsyncWriteExt + Unpin + Send,
751756
{
752-
let (send_local, apply_remote) = directions(mode, is_client);
757+
let (send_local, apply_remote) = directions(ctx.mode, ctx.is_client);
753758

754759
// Dedicated reader task → channel. read_exact is not cancel-safe in select!.
755760
let (msg_tx, mut msg_rx) =
@@ -785,7 +790,7 @@ where
785790
_ = &mut sigint => {
786791
tracing::info!("ctrl+c — closing");
787792
let mut w = writer.lock().await;
788-
let _ = write_message(&mut *w, &Message::Bye, compress).await;
793+
let _ = write_message(&mut *w, &Message::Bye, ctx.compress).await;
789794
break;
790795
}
791796

@@ -796,10 +801,10 @@ where
796801
// Per-op apply errors are non-fatal — log and
797802
// continue. Connection-level failures appear as
798803
// Err from the reader task (the next arm).
799-
if let Err(e) = handle_incoming(&root, m, &suppress, &pending, compress, &writer, apply_remote, Some(&ignores), is_client).await {
804+
if let Err(e) = handle_incoming(&ctx, m, &suppress, &pending, &writer, apply_remote).await {
800805
tracing::warn!("apply failed: {}", e);
801806
let mut w = writer.lock().await;
802-
let _ = write_message(&mut *w, &Message::Error(format!("{e}")), compress).await;
807+
let _ = write_message(&mut *w, &Message::Error(format!("{e}")), ctx.compress).await;
803808
}
804809
}
805810
Some(Err(e)) => {
@@ -813,7 +818,7 @@ where
813818
ev = event_rx.recv() => {
814819
let Some(events) = ev else { break };
815820
if send_local {
816-
forward_local_events(&root, events, &writer, compress, &suppress, is_client).await?;
821+
forward_local_events(&ctx.root, events, &writer, ctx.compress, &suppress, ctx.is_client).await?;
817822
}
818823
}
819824
}
@@ -823,32 +828,26 @@ where
823828
Ok(())
824829
}
825830

826-
/// True if `ignores` rejects this path. `None` means "no filter".
827-
fn ignored(ignores: Option<&IgnoreStack>, rel: &Path, is_dir: bool) -> bool {
828-
match ignores {
829-
Some(s) => s.is_ignored_rel(rel, is_dir),
830-
None => false,
831-
}
832-
}
833-
834831
pub async fn handle_incoming<W>(
835-
root: &Path,
832+
ctx: &SessionCtx,
836833
msg: Message,
837834
suppress: &Suppression,
838835
pending: &Pending,
839-
compress: bool,
840836
writer: &Arc<Mutex<W>>,
841837
apply_remote: bool,
842-
ignores: Option<&IgnoreStack>,
843-
is_client: bool,
844838
) -> Result<()>
845839
where
846840
W: AsyncWriteExt + Unpin,
847841
{
842+
// Locals so the existing body reads naturally and we don't repeat
843+
// `ctx.foo` access dozens of times. Cheap; nothing is cloned.
844+
let root: &Path = &ctx.root;
845+
let compress = ctx.compress;
846+
let ignores = &ctx.ignores;
848847
// Only the client prints user-facing event lines. The agent's stderr is
849848
// forwarded over SSH to the same terminal, so any logs there would just
850849
// duplicate the client's transcript.
851-
let log_event = is_client;
850+
let log_event = ctx.is_client;
852851
// If git is mid-operation locally, refuse to apply any change under
853852
// `.git/`. Otherwise the peer (who may NOT be busy) would clobber our
854853
// in-progress rebase/merge state and break ref locking.
@@ -881,7 +880,7 @@ where
881880
if !apply_remote {
882881
return Ok(());
883882
}
884-
if ignored(ignores, &entry.path, false) {
883+
if ignores.is_ignored_rel(&entry.path, false) {
885884
tracing::debug!("ignored (recv FileData): {}", entry.path.display());
886885
return Ok(());
887886
}
@@ -925,7 +924,7 @@ where
925924
if !apply_remote {
926925
return Ok(());
927926
}
928-
if ignored(ignores, &entry.path, false) {
927+
if ignores.is_ignored_rel(&entry.path, false) {
929928
tracing::debug!("ignored (recv FileStart): {}", entry.path.display());
930929
return Ok(());
931930
}
@@ -953,7 +952,7 @@ where
953952
if !apply_remote {
954953
return Ok(());
955954
}
956-
if ignored(ignores, &path, false) {
955+
if ignores.is_ignored_rel(&path, false) {
957956
return Ok(());
958957
}
959958
pending.chunk(&path, &data).await?;
@@ -962,7 +961,7 @@ where
962961
if !apply_remote {
963962
return Ok(());
964963
}
965-
if ignored(ignores, &path, false) {
964+
if ignores.is_ignored_rel(&path, false) {
966965
return Ok(());
967966
}
968967
if let Some(entry) = pending.end(root, &path).await? {
@@ -982,7 +981,7 @@ where
982981
if !apply_remote {
983982
return Ok(());
984983
}
985-
if ignored(ignores, &path, false) {
984+
if ignores.is_ignored_rel(&path, false) {
986985
return Ok(());
987986
}
988987
let full = root.join(&path);
@@ -1018,7 +1017,7 @@ where
10181017
if !apply_remote {
10191018
return Ok(());
10201019
}
1021-
if ignored(ignores, &entry.path, true) {
1020+
if ignores.is_ignored_rel(&entry.path, true) {
10221021
tracing::debug!("ignored (recv MkDir): {}", entry.path.display());
10231022
return Ok(());
10241023
}
@@ -1045,7 +1044,7 @@ where
10451044
if !apply_remote {
10461045
return Ok(());
10471046
}
1048-
if ignored(ignores, &entry.path, false) {
1047+
if ignores.is_ignored_rel(&entry.path, false) {
10491048
return Ok(());
10501049
}
10511050
if is_already_equal(root, &entry) {
@@ -1069,25 +1068,21 @@ where
10691068
if !apply_remote {
10701069
return Ok(());
10711070
}
1072-
if ignored(ignores, &path, false) && ignored(ignores, &path, true) {
1071+
if ignores.is_ignored_rel(&path, false) && ignores.is_ignored_rel(&path, true) {
10731072
return Ok(());
10741073
}
10751074
let existed_before = fs::symlink_metadata(root.join(&path)).is_ok();
10761075
apply_delete(root, &path)?;
10771076
suppress.mark_deleted(path.clone());
10781077
if existed_before && log_event {
1079-
eprintln!(
1080-
" {} {}",
1081-
"←".bright_cyan(),
1082-
format!("× {}", path.display())
1083-
);
1078+
eprintln!(" {} × {}", "←".bright_cyan(), path.display());
10841079
}
10851080
}
10861081
Message::Rename { from, to } => {
10871082
if !apply_remote {
10881083
return Ok(());
10891084
}
1090-
if ignored(ignores, &from, false) || ignored(ignores, &to, false) {
1085+
if ignores.is_ignored_rel(&from, false) || ignores.is_ignored_rel(&to, false) {
10911086
tracing::debug!(
10921087
"ignored (recv Rename): {} → {}",
10931088
from.display(),
@@ -1112,7 +1107,7 @@ where
11121107
suppress.mark_mtime(to, mt);
11131108
}
11141109
Message::FileGet { path } => {
1115-
if ignored(ignores, &path, false) && ignored(ignores, &path, true) {
1110+
if ignores.is_ignored_rel(&path, false) && ignores.is_ignored_rel(&path, true) {
11161111
return Ok(());
11171112
}
11181113
if let Some(entry) = build_entry(root, &path, None)? {
@@ -1246,7 +1241,7 @@ where
12461241
// (FSEvents on macOS is chatty during `rm`). The
12471242
// user's intent is a delete — treat it as such.
12481243
if log_event {
1249-
eprintln!(" {} {}", "→".bright_green(), format!("× {}", p.display()));
1244+
eprintln!(" {} × {}", "→".bright_green(), p.display());
12501245
}
12511246
{
12521247
let mut w = writer.lock().await;
@@ -1355,7 +1350,7 @@ where
13551350
}
13561351
FsEvent::Removed(p) => {
13571352
if log_event {
1358-
eprintln!(" {} {}", "→".bright_green(), format!("× {}", p.display()));
1353+
eprintln!(" {} × {}", "→".bright_green(), p.display());
13591354
}
13601355
{
13611356
let mut w = writer.lock().await;

src/protocol.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,7 @@ where
203203
let bytes =
204204
postcard::to_allocvec(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
205205
let (payload, flags) = if compress && bytes.len() > COMPRESS_THRESHOLD {
206-
let c = zstd::encode_all(&bytes[..], COMPRESS_LEVEL)
207-
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
206+
let c = zstd::encode_all(&bytes[..], COMPRESS_LEVEL).map_err(io::Error::other)?;
208207
// Only use the compressed form if it actually saves space.
209208
if c.len() + 5 < bytes.len() {
210209
(c, FLAG_COMPRESSED)

src/sync.rs

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ async fn run_inner(
545545
.ok()
546546
.map(|m| {
547547
use std::os::unix::fs::MetadataExt;
548-
m.mtime() * 1_000_000_000 + m.mtime_nsec() as i64
548+
m.mtime() * 1_000_000_000 + m.mtime_nsec()
549549
})
550550
.unwrap_or(entry.mtime);
551551
suppress.mark_mtime(path, mt);
@@ -560,7 +560,7 @@ async fn run_inner(
560560
.ok()
561561
.map(|m| {
562562
use std::os::unix::fs::MetadataExt;
563-
m.mtime() * 1_000_000_000 + m.mtime_nsec() as i64
563+
m.mtime() * 1_000_000_000 + m.mtime_nsec()
564564
})
565565
.unwrap_or(entry.mtime);
566566
suppress.mark_mtime(path, mt);
@@ -582,7 +582,7 @@ async fn run_inner(
582582
.ok()
583583
.map(|m| {
584584
use std::os::unix::fs::MetadataExt;
585-
m.mtime() * 1_000_000_000 + m.mtime_nsec() as i64
585+
m.mtime() * 1_000_000_000 + m.mtime_nsec()
586586
})
587587
.unwrap_or(0);
588588
suppress.mark_mtime(to, mt);
@@ -677,19 +677,14 @@ async fn run_inner(
677677
}
678678

679679
crate::ui::info("watching for changes — ctrl+c to stop");
680-
let result = live_loop(
681-
local_root,
682-
reader,
683-
writer,
684-
args.mode,
680+
let ctx = crate::peer::SessionCtx {
681+
root: local_root,
682+
mode: args.mode,
685683
compress,
686-
true,
684+
is_client: true,
687685
ignores,
688-
suppress,
689-
pending,
690-
watcher_handle,
691-
)
692-
.await;
686+
};
687+
let result = live_loop(ctx, reader, writer, suppress, pending, watcher_handle).await;
693688
let _ = child.wait().await;
694689
result
695690
}
@@ -702,12 +697,10 @@ async fn receive_manifest<R>(reader: &mut R) -> Result<Vec<Entry>>
702697
where
703698
R: tokio::io::AsyncReadExt + Unpin,
704699
{
705-
loop {
706-
match read_message(reader).await? {
707-
Message::ManifestBegin => break,
708-
Message::Error(e) => anyhow::bail!("remote: {e}"),
709-
m => anyhow::bail!("expected ManifestBegin, got {:?}", m),
710-
}
700+
match read_message(reader).await? {
701+
Message::ManifestBegin => {}
702+
Message::Error(e) => anyhow::bail!("remote: {e}"),
703+
m => anyhow::bail!("expected ManifestBegin, got {:?}", m),
711704
}
712705
let mut entries = Vec::new();
713706
loop {

0 commit comments

Comments
 (0)