git_zero_client/
reference.rs1use crate::Transport;
3use git_zero_core::{
4 Error, ObjectId, Result,
5 http::Request,
6 io::{SliceWriter, Write},
7 repository::validate_ref_name,
8};
9
10pub fn reference_name<'a>(name: &str, storage: &'a mut [u8]) -> Result<&'a str> {
12 let mut output = SliceWriter::new(storage);
13 if name != "HEAD" && !name.starts_with("refs/") {
14 output.write_all(b"refs/heads/")?;
15 }
16 output.write_all(name.as_bytes())?;
17 let bytes = output.finish();
18 let name = core::str::from_utf8(bytes).map_err(|_| Error::InvalidReference)?;
19 if name != "HEAD" {
20 validate_ref_name(name)?;
21 }
22 Ok(name)
23}
24
25fn encode_path<'a>(name: &str, storage: &'a mut [u8]) -> Result<&'a str> {
26 let mut output = SliceWriter::new(storage);
27 for byte in name.bytes() {
28 if byte.is_ascii_alphanumeric() || b"/._-".contains(&byte) {
29 output.write_all(&[byte])?;
30 } else {
31 const HEX: &[u8] = b"0123456789ABCDEF";
32 output.write_all(&[b'%', HEX[(byte >> 4) as usize], HEX[(byte & 15) as usize]])?;
33 }
34 }
35 core::str::from_utf8(output.finish()).map_err(|_| Error::InvalidReference)
36}
37
38pub fn resolve_reference(
42 transport: &mut impl Transport,
43 name: &str,
44 response: &mut [u8],
45 path: &mut [u8],
46 max_reads: u32,
47) -> Result<ObjectId> {
48 if name != "HEAD" {
49 validate_ref_name(name)?;
50 }
51 let mut request_path = encode_path(name, path)?;
52 for _ in 0..max_reads {
53 let (status, length) = transport.request(
54 Request {
55 content_type: None,
56 git_protocol: None,
57 method: "GET",
58 path: request_path,
59 query: None,
60 body: &[],
61 },
62 response,
63 )?;
64 if status == 404 {
65 return Err(Error::ObjectNotFound);
66 }
67 if status != 200 {
68 return Err(Error::Backend);
69 }
70 let line = response.get(..length).ok_or(Error::BufferTooSmall)?;
71 let line = line.strip_suffix(b"\n").unwrap_or(line);
72 if let Some(target) = line.strip_prefix(b"ref: ") {
73 let target = core::str::from_utf8(target).map_err(|_| Error::InvalidReference)?;
74 validate_ref_name(target)?;
75 request_path = encode_path(target, path)?;
76 } else {
77 return ObjectId::from_hex(line);
78 }
79 }
80 Err(Error::LimitExceeded)
81}