gunnar-sendpack 1.1.0

git's receive-pack wire format, both ends: the send-pack conversation gitoxide does not have, plus the server-side encoders for the same grammar. Plumbing only, no gunnar types.
Documentation
//! **send-pack**, and the receive-pack grammar under it — the half of git that
//! gitoxide does not have.
//!
//! Verified in gix 0.86: `gix-protocol/src/` carries `fetch/`, `ls_refs.rs` and
//! `handshake/`, and **nothing for push**. `Direction::Push` exists only as a
//! refspec direction. So a git client written on gix has to put this on the
//! wire itself, and a git *server* written on gix has to put the other side of
//! it on the wire itself, and until this crate existed those two were written
//! twice — once per direction, in two crates, in two different styles, agreeing
//! only by inspection.
//!
//! # One grammar, both directions
//!
//! That is the load-bearing idea, and it is why this crate is not called
//! `send-pack` alone. Four formats make up the v0 push conversation:
//!
//! | format | client | server |
//! |---|---|---|
//! | [`capabilities`] | parses the remote's, writes its own | writes its own, parses the client's |
//! | [`advertisement`] | [`advertisement::parse`] | [`advertisement::lines`] |
//! | [`command`] | [`command::lines`] | [`command::parse_line`] |
//! | [`report`] | [`report::parse`] | [`report::lines`] |
//!
//! Each row is **one** implementation with two entry points, so an encoder and
//! its decoder cannot drift: there is nothing for them to drift from. A test
//! that round-trips one row exercises both ends of a real conversation rather
//! than one end against a fixture written by the same hand.
//!
//! # Payloads, not packets
//!
//! Every `lines` function returns payloads **without pkt-line framing, without
//! a terminating flush-pkt, and without a trailing newline**, and every parser
//! takes them the same way. git frames these sections as *text* pkt-lines,
//! `<4-hex-length><payload>\n`, and the newline belongs to the framer.
//!
//! That split is what lets a server that already owns a pkt-line framer and a
//! socket use this crate without paying for a second framing layer: with
//! `default-features = false` the whole crate is `gix-hash` + `bstr` +
//! `thiserror`. The `blocking-io` feature — on by default, because a client is
//! the majority consumer — adds [`Transport`], the pkt-line reader and
//! [`send_pack`], framed with [`gix_packetline`].
//!
//! # Example: what a client does
//!
//! ```no_run
//! # fn main() -> Result<(), gunnar_sendpack::Error> {
//! use gunnar_sendpack::{read_advertisement, send_pack, IoPair, PushCommand, SendPackOptions};
//!
//! // Two pipes to a remote `git-receive-pack`.
//! # let (stdout, stdin): (std::fs::File, std::fs::File) = unimplemented!();
//! let mut transport = IoPair::new(stdout, stdin);
//! let (reader, _) = { use gunnar_sendpack::Transport as _; transport.io() };
//! let adv = read_advertisement(reader)?;
//!
//! let tip = adv.oid_of("refs/heads/main");
//! # let new_tip = tip;
//! let commands = vec![PushCommand::update("refs/heads/main", tip, new_tip)];
//! let report = send_pack(
//!     &mut transport,
//!     &adv,
//!     &commands,
//!     adv.hash_kind,
//!     Some(|w: &mut dyn std::io::Write| Ok(w.write_all(b"PACK...")?)),
//!     &SendPackOptions::default(),
//! )?;
//! assert!(report.is_ok(), "{:?}", report.failure_summary());
//! # Ok(())
//! # }
//! ```
//!
//! # Licence
//!
//! `MIT OR Apache-2.0`, to match gitoxide's.

#![deny(missing_docs)]
#![forbid(unsafe_code)]

pub mod advertisement;
pub mod capabilities;
pub mod command;
pub mod error;
pub mod options;
pub mod report;

#[cfg(feature = "blocking-io")]
pub mod driver;
#[cfg(feature = "blocking-io")]
pub mod transport;

pub use advertisement::{Advertisement, RemoteRef};
pub use capabilities::Capabilities;
pub use command::PushCommand;
pub use error::{Error, Result};
pub use options::SendPackOptions;
pub use report::{PushReport, RefStatus};

#[cfg(feature = "blocking-io")]
pub use driver::{read_advertisement, read_report, send_pack};
#[cfg(feature = "blocking-io")]
pub use transport::{IoPair, Transport};