use crate::{Error, Parcel, WithError as _, bail};
use super::{Repo, parse_response};
#[derive(Debug, Default, Clone)]
pub struct Capabilities {
pub agent: Option<String>,
pub ls_refs: Vec<String>,
pub fetch: Vec<String>,
pub object_format: Option<String>,
}
impl Repo {
pub fn discover(&self) -> Result<Capabilities, Error> {
let agent = ureq::Agent::config_builder()
.http_status_as_error(false)
.build()
.new_agent();
let res = agent
.get(format!("{}/info/refs?service=git-upload-pack", self.url))
.header("Git-Protocol", "version=2")
.call()
.with_err_msg("could not send HTTP request")?;
let mut res = parse_response(res)?;
res.ignore_end = true;
let parcel: Parcel = res.collect::<Result<Parcel, Error>>()?;
let mut sections = parcel.sections.into_iter();
let header = sections.next().with_err_msg("header not found")?;
let expected_header = ["# service=git-upload-pack\n"];
if &header != expected_header.as_slice() {
bail!("unexpected header: {header:?}");
}
let caps = sections.next().with_err_msg("capabilities not found")?;
let raw_caps = caps.chunks.into_iter();
let mut caps = Capabilities::default();
for cap in raw_caps {
let cap = str::from_utf8(&cap).with_err_msg("could not decode capability as utf-8")?;
let line = cap.trim_end_matches('\n');
if let Some(value) = line.strip_prefix("agent=") {
caps.agent.replace(value.to_owned());
}
if let Some(value) = line.strip_prefix("ls-refs=") {
caps.ls_refs.extend(value.split(' ').map(str::to_owned));
}
if let Some(value) = line.strip_prefix("fetch=") {
caps.fetch.extend(value.split(' ').map(str::to_owned));
}
if let Some(value) = line.strip_prefix("object-format=") {
caps.object_format.replace(value.to_owned());
}
}
Ok(caps)
}
}