git_zero_server/receive/
mod.rs1use git_zero_core::{Error, ObjectFormat, Result, io::Write};
5
6mod advertisement;
7mod ancestry;
8mod attributes;
9mod control;
10mod gitmodules;
11mod headers;
12mod report;
13mod tree;
14pub use advertisement::{Advertisement, write_advertisement};
15pub use ancestry::Ancestry;
16pub use attributes::FsckAttributes;
17pub use control::{Command, ControlDecoder, ControlEvent, ControlProgress, Limits};
18pub use gitmodules::FsckModules;
19pub use headers::FsckHeaders;
20pub use report::{Report, Status, write_progress};
21pub use tree::{BlobChecks, check_tree_entry};
22
23const FLAGS: &[(&[u8], u16)] = &[
24 (b"report-status", 1),
25 (b"report-status-v2", 2),
26 (b"delete-refs", 4),
27 (b"ofs-delta", 8),
28 (b"atomic", 16),
29 (b"push-options", 32),
30 (b"side-band-64k", 64),
31 (b"quiet", 128),
32];
33
34#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
37pub struct Capabilities(u16);
38
39impl Capabilities {
40 pub const REPORT_STATUS: Self = Self(1);
41 pub const REPORT_STATUS_V2: Self = Self(2);
42 pub const DELETE_REFS: Self = Self(4);
43 pub const OFS_DELTA: Self = Self(8);
44 pub const ATOMIC: Self = Self(16);
45 pub const PUSH_OPTIONS: Self = Self(32);
46 pub const SIDE_BAND_64K: Self = Self(64);
47 pub const QUIET: Self = Self(128);
48
49 pub const fn contains(self, capability: Self) -> bool {
50 self.0 & capability.0 == capability.0
51 }
52
53 fn parse(bytes: &[u8], format: ObjectFormat, offered: Self) -> Result<Self> {
54 let mut selected = 0;
55 let mut format_seen = false;
56 let mut agent_seen = false;
57 for token in bytes
58 .split(|&byte| byte == b' ')
59 .filter(|token| !token.is_empty())
60 {
61 if token == format.capability() {
62 if format_seen {
63 return Err(Error::InvalidRequest);
64 }
65 format_seen = true;
66 continue;
67 }
68 if let Some(agent) = token.strip_prefix(b"agent=") {
69 if agent_seen
70 || agent.is_empty()
71 || !agent.iter().all(|byte| byte.is_ascii_graphic())
72 {
73 return Err(Error::InvalidRequest);
74 }
75 agent_seen = true;
76 continue;
77 }
78 let flag = FLAGS
79 .iter()
80 .find_map(|&(name, flag)| (name == token).then_some(flag))
81 .ok_or(Error::Unsupported)?;
82 if offered.0 & flag == 0 || selected & flag != 0 {
83 return Err(Error::InvalidRequest);
84 }
85 selected |= flag;
86 }
87 if format == ObjectFormat::Sha256 && !format_seen {
88 return Err(Error::InvalidRequest);
89 }
90 Ok(Self(selected))
91 }
92
93 fn write(self, format: ObjectFormat, output: &mut impl Write) -> Result<()> {
94 output.write_all(format.capability())?;
95 for &(name, flag) in FLAGS {
96 if self.0 & flag != 0 {
97 output.write_all(b" ")?;
98 output.write_all(name)?;
99 }
100 }
101 output.write_all(b" agent=git-zero")
102 }
103}
104
105impl core::ops::BitOr for Capabilities {
106 type Output = Self;
107 fn bitor(self, rhs: Self) -> Self {
108 Self(self.0 | rhs.0)
109 }
110}