use gix_hash::Kind;
use crate::advertisement::{object_format_name, Advertisement};
use crate::command::PushCommand;
use crate::error::{Error, Result};
#[derive(Debug, Clone)]
pub struct SendPackOptions {
pub agent: String,
pub atomic: bool,
pub side_band: bool,
pub quiet: bool,
pub push_options: Vec<String>,
pub report_status_v2: bool,
}
impl Default for SendPackOptions {
fn default() -> Self {
SendPackOptions {
agent: format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")),
atomic: false,
side_band: true,
quiet: false,
push_options: Vec::new(),
report_status_v2: true,
}
}
}
pub fn ofs_delta_agreed(adv: &Advertisement) -> bool {
adv.capabilities.has("ofs-delta")
}
pub fn negotiate(
adv: &Advertisement,
commands: &[PushCommand],
local_hash_kind: Kind,
opts: &SendPackOptions,
) -> Result<Vec<String>> {
let remote = &adv.capabilities;
let mut out: Vec<String> = Vec::new();
if local_hash_kind != adv.hash_kind {
return Err(Error::protocol(format!(
"the local repository is {local_hash_kind:?} but the remote is {:?}; \
git cannot push between object formats",
adv.hash_kind
)));
}
if remote.has("report-status-v2") && opts.report_status_v2 {
out.push("report-status-v2".to_string());
} else if remote.has("report-status") {
out.push("report-status".to_string());
}
if opts.side_band && remote.has("side-band-64k") {
out.push("side-band-64k".to_string());
}
if ofs_delta_agreed(adv) {
out.push("ofs-delta".to_string());
}
if opts.quiet && remote.has("quiet") {
out.push("quiet".to_string());
}
if opts.atomic {
if !remote.has("atomic") {
return Err(Error::protocol(
"atomic push was requested but the remote does not advertise `atomic`; \
refusing to push non-atomically under an atomic request",
));
}
out.push("atomic".to_string());
}
if !opts.push_options.is_empty() {
if !remote.has("push-options") {
return Err(Error::protocol(
"push options were given but the remote does not advertise `push-options`",
));
}
out.push("push-options".to_string());
}
if commands.iter().any(PushCommand::is_delete) && !remote.has("delete-refs") {
return Err(Error::protocol(
"the push deletes a ref but the remote does not advertise `delete-refs`",
));
}
if remote.has("object-format") {
out.push(format!(
"object-format={}",
object_format_name(local_hash_kind)?
));
}
if remote.has("agent") {
out.push(format!("agent={}", opts.agent));
}
Ok(out)
}