No description
Find a file
Markus Maiwald 75affe7759
feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl
Async Rust client for the progit-forged daemon, plus the GitBackend
trait used by the ProGit TUI to abstract over local-disk vs.
daemon-backed git data.

Two layers:

ForgeClient (raw gRPC) — thin wrapper over the tonic-generated client.
Full RPC coverage: connect, create_repo, delete_repo, stat, list_refs,
push, fetch (collected and streaming variants), create_ephemeral_branch,
merge_ref. Push chunks pack data at 64 KB; fetch drains the stream
into Vec<u8> with a fetch_streaming escape hatch for large repos.

GitBackend trait + ForgedBackend impl — wire-independent abstraction
in src/backend.rs. The trait carries its own RefEntry, RefUpdate,
PushOutcome, EphemeralBranch types so it stays stable across
daemon protocol versions; ForgedBackend converts between trait
types and proto types per call. BackendError carries semantic
variants (RepoNotFound, RepoExists, CasFailed, Unsupported, ...)
so consumers pattern-match on intent rather than scrape error
strings.

Architectural decision recorded:
The trait lives in this crate (rather than a separate
progit-git-backend crate) so the dependency graph stays minimal.
The TUI gains one direct dep on progit-forge-client; tonic is
the only meaningful new transitive cost. A future LocalGitBackend
in the TUI implements the same trait and operates against on-disk
git via gix.

The wire schema (proto/forge.proto) is duplicated from
progit-forged. Schema drift is mitigated by lockstep updates on
wire-version bumps; copying-rather-than-path-dep keeps the client
crate light (no sled / gix / object_store transitive cost for
TUI consumers).

Test totals: 37 passing (14 lib unit + 11 raw-client e2e + 11
backend-trait e2e + 1 doctest). Integration tests spin up the
daemon in-process and exercise every method end-to-end.

License: LSL-1.0.
2026-05-08 22:00:33 +02:00
proto feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl 2026-05-08 22:00:33 +02:00
src feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl 2026-05-08 22:00:33 +02:00
tests feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl 2026-05-08 22:00:33 +02:00
.gitignore feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl 2026-05-08 22:00:33 +02:00
build.rs feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl 2026-05-08 22:00:33 +02:00
Cargo.lock feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl 2026-05-08 22:00:33 +02:00
Cargo.toml feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl 2026-05-08 22:00:33 +02:00
LICENSE feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl 2026-05-08 22:00:33 +02:00
README.md feat: initial v0.1 — gRPC client + GitBackend trait + ForgedBackend impl 2026-05-08 22:00:33 +02:00

progit-forge-client

Async Rust client for the progit-forged daemon, plus the GitBackend trait used by the ProGit TUI to abstract over local-disk vs. daemon-backed git data.

Two layers

┌──────────────────────────────────────────────────┐
│  GitBackend trait        (wire-independent)       │  ← consumed by TUI
└──────────────────────────────────────────────────┘
                  │                │
       ForgedBackend          LocalGitBackend
       (this crate)           (in TUI crate;
            │                  uses gix)
            ▼
┌──────────────────────────────────────────────────┐
│  ForgeClient             (gRPC over tonic)        │  ← raw client
└──────────────────────────────────────────────────┘
            │
     ┌──────▼──────┐
     │ progit-forged│
     └─────────────┘

Use as a daemon-backed backend

use progit_forge_client::{ForgedBackend, backend::{GitBackend, RefUpdate}};

let backend = ForgedBackend::connect("http://127.0.0.1:7421").await?;
backend.create_repo("hello").await?;
backend.push("hello", vec![RefUpdate {
    ref_name: "refs/heads/main".into(),
    old_oid: String::new(),
    new_oid: blob_oid,
}], Some(pack_bytes)).await?;
let pack = backend.fetch("hello", vec![]).await?;

The TUI holds Box<dyn GitBackend> and stays oblivious to whether the bytes round-trip through a sidecar daemon or a local gix invocation.

Use as a raw client (lower-level)

use progit_forge_client::{ForgeClient, RefUpdate};

let mut client = ForgeClient::connect("http://127.0.0.1:7421").await?;
client.create_repo("hello").await?;
// ... full RPC surface, see RPC coverage table below.

RPC coverage

Method Status
connect / from_channel full
create_repo / delete_repo / stat full
list_refs (with optional prefix) full
push (refs + optional pack, 64 KB chunked) full
fetch (collected Vec<u8>) full, single-pack v0.1.3.0
fetch_streaming (raw tonic::Streaming) full
create_ephemeral_branch (TTL, optional name) full
merge_ref bound but server-side Unimplemented until v0.1.4

GitBackend coverage

Method Status
create_repo / delete_repo full
list_refs full
push (refs + optional pack) full
fetch full, single-pack
create_ephemeral_branch (default returns Unsupported) full on ForgedBackend

BackendError carries semantic variants (RepoNotFound, RepoExists, InvalidInput, CasFailed, Unsupported, …) so consumers pattern-match on intent rather than scraping error strings.

Status

v0.1 — full RPC surface, full GitBackend trait, 37 tests passing (14 lib + 22 e2e + 1 doctest).

License

LSL-1.0.