Enum git_protocol::Command
source · pub enum Command {
LsRefs,
Fetch,
}
Expand description
A selector for V2 commands to invoke on the server for purpose of pre-invocation validation.
Variants§
Implementations§
source§impl Command
impl Command
sourcepub fn default_features(
&self,
version: Protocol,
server_capabilities: &Capabilities
) -> Vec<Feature> ⓘ
Available on crate features async-client
or blocking-client
only.
pub fn default_features(
&self,
version: Protocol,
server_capabilities: &Capabilities
) -> Vec<Feature> ⓘ
async-client
or blocking-client
only.Turns on all modern features for V1 and all supported features for V2, returning them as a vector of features. Note that this is the basis for any fetch operation as these features fulfil basic requirements and reasonably up-to-date servers.
Examples found in repository?
src/ls_refs.rs (line 69)
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
pub async fn ls_refs(
mut transport: impl Transport,
capabilities: &Capabilities,
prepare_ls_refs: impl FnOnce(
&Capabilities,
&mut Vec<BString>,
&mut Vec<(&str, Option<Cow<'static, str>>)>,
) -> std::io::Result<Action>,
progress: &mut impl Progress,
) -> Result<Vec<Ref>, Error> {
let ls_refs = Command::LsRefs;
let mut ls_features = ls_refs.default_features(git_transport::Protocol::V2, capabilities);
let mut ls_args = ls_refs.initial_arguments(&ls_features);
if capabilities
.capability("ls-refs")
.and_then(|cap| cap.supports("unborn"))
.unwrap_or_default()
{
ls_args.push("unborn".into());
}
let refs = match prepare_ls_refs(capabilities, &mut ls_args, &mut ls_features) {
Ok(Action::Skip) => Vec::new(),
Ok(Action::Continue) => {
ls_refs.validate_argument_prefixes_or_panic(
git_transport::Protocol::V2,
capabilities,
&ls_args,
&ls_features,
);
progress.step();
progress.set_name("list refs");
let mut remote_refs = transport
.invoke(
ls_refs.as_str(),
ls_features.into_iter(),
if ls_args.is_empty() {
None
} else {
Some(ls_args.into_iter())
},
)
.await?;
from_v2_refs(&mut remote_refs).await?
}
Err(err) => {
indicate_end_of_interaction(transport).await?;
return Err(err.into());
}
};
Ok(refs)
}
More examples
src/fetch_fn.rs (line 98)
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
pub async fn fetch<F, D, T, P>(
mut transport: T,
mut delegate: D,
authenticate: F,
mut progress: P,
fetch_mode: FetchConnection,
agent: impl Into<String>,
) -> Result<(), Error>
where
F: FnMut(credentials::helper::Action) -> credentials::protocol::Result,
D: Delegate,
T: client::Transport,
P: Progress,
P::SubProgress: 'static,
{
let crate::handshake::Outcome {
server_protocol_version: protocol_version,
refs,
capabilities,
} = crate::fetch::handshake(
&mut transport,
authenticate,
delegate.handshake_extra_parameters(),
&mut progress,
)
.await?;
let agent = crate::agent(agent);
let refs = match refs {
Some(refs) => refs,
None => {
crate::ls_refs(
&mut transport,
&capabilities,
|a, b, c| {
let res = delegate.prepare_ls_refs(a, b, c);
c.push(("agent", Some(Cow::Owned(agent.clone()))));
res
},
&mut progress,
)
.await?
}
};
let fetch = Command::Fetch;
let mut fetch_features = fetch.default_features(protocol_version, &capabilities);
match delegate.prepare_fetch(protocol_version, &capabilities, &mut fetch_features, &refs) {
Ok(Action::Cancel) => {
return if matches!(protocol_version, git_transport::Protocol::V1)
|| matches!(fetch_mode, FetchConnection::TerminateOnSuccessfulCompletion)
{
indicate_end_of_interaction(transport).await.map_err(Into::into)
} else {
Ok(())
};
}
Ok(Action::Continue) => {
fetch.validate_argument_prefixes_or_panic(protocol_version, &capabilities, &[], &fetch_features);
}
Err(err) => {
indicate_end_of_interaction(transport).await?;
return Err(err.into());
}
}
Response::check_required_features(protocol_version, &fetch_features)?;
let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all");
fetch_features.push(("agent", Some(Cow::Owned(agent))));
let mut arguments = Arguments::new(protocol_version, fetch_features);
let mut previous_response = None::<Response>;
let mut round = 1;
'negotiation: loop {
progress.step();
progress.set_name(format!("negotiate (round {})", round));
round += 1;
let action = delegate.negotiate(&refs, &mut arguments, previous_response.as_ref())?;
let mut reader = arguments.send(&mut transport, action == Action::Cancel).await?;
if sideband_all {
setup_remote_progress(&mut progress, &mut reader);
}
let response = Response::from_line_reader(protocol_version, &mut reader).await?;
previous_response = if response.has_pack() {
progress.step();
progress.set_name("receiving pack");
if !sideband_all {
setup_remote_progress(&mut progress, &mut reader);
}
delegate.receive_pack(reader, progress, &refs, &response).await?;
break 'negotiation;
} else {
match action {
Action::Cancel => break 'negotiation,
Action::Continue => Some(response),
}
}
}
if matches!(protocol_version, git_transport::Protocol::V2)
&& matches!(fetch_mode, FetchConnection::TerminateOnSuccessfulCompletion)
{
indicate_end_of_interaction(transport).await?;
}
Ok(())
}
source§impl Command
impl Command
sourcepub fn as_str(&self) -> &'static str
pub fn as_str(&self) -> &'static str
Produce the name of the command as known by the server side.
Examples found in repository?
src/ls_refs.rs (line 92)
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
pub async fn ls_refs(
mut transport: impl Transport,
capabilities: &Capabilities,
prepare_ls_refs: impl FnOnce(
&Capabilities,
&mut Vec<BString>,
&mut Vec<(&str, Option<Cow<'static, str>>)>,
) -> std::io::Result<Action>,
progress: &mut impl Progress,
) -> Result<Vec<Ref>, Error> {
let ls_refs = Command::LsRefs;
let mut ls_features = ls_refs.default_features(git_transport::Protocol::V2, capabilities);
let mut ls_args = ls_refs.initial_arguments(&ls_features);
if capabilities
.capability("ls-refs")
.and_then(|cap| cap.supports("unborn"))
.unwrap_or_default()
{
ls_args.push("unborn".into());
}
let refs = match prepare_ls_refs(capabilities, &mut ls_args, &mut ls_features) {
Ok(Action::Skip) => Vec::new(),
Ok(Action::Continue) => {
ls_refs.validate_argument_prefixes_or_panic(
git_transport::Protocol::V2,
capabilities,
&ls_args,
&ls_features,
);
progress.step();
progress.set_name("list refs");
let mut remote_refs = transport
.invoke(
ls_refs.as_str(),
ls_features.into_iter(),
if ls_args.is_empty() {
None
} else {
Some(ls_args.into_iter())
},
)
.await?;
from_v2_refs(&mut remote_refs).await?
}
Err(err) => {
indicate_end_of_interaction(transport).await?;
return Err(err.into());
}
};
Ok(refs)
}
More examples
src/fetch/arguments/blocking_io.rs (line 47)
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
pub fn send<'a, T: client::Transport + 'a>(
&mut self,
transport: &'a mut T,
add_done_argument: bool,
) -> Result<Box<dyn client::ExtendedBufRead + Unpin + 'a>, client::Error> {
if self.haves.is_empty() {
assert!(add_done_argument, "If there are no haves, is_done must be true.");
}
match self.version {
git_transport::Protocol::V1 => {
let (on_into_read, retained_state) = self.prepare_v1(
transport.connection_persists_across_multiple_requests(),
add_done_argument,
)?;
let mut line_writer =
transport.request(client::WriteMode::OneLfTerminatedLinePerWriteCall, on_into_read)?;
let had_args = !self.args.is_empty();
for arg in self.args.drain(..) {
line_writer.write_all(&arg)?;
}
if had_args {
line_writer.write_message(client::MessageKind::Flush)?;
}
for line in self.haves.drain(..) {
line_writer.write_all(&line)?;
}
if let Some(next_args) = retained_state {
self.args = next_args;
}
Ok(line_writer.into_read()?)
}
git_transport::Protocol::V2 => {
let retained_state = self.args.clone();
self.args.append(&mut self.haves);
if add_done_argument {
self.args.push("done".into());
}
transport.invoke(
Command::Fetch.as_str(),
self.features
.iter()
.filter_map(|(k, v)| v.as_ref().map(|v| (*k, Some(v.as_ref())))),
Some(std::mem::replace(&mut self.args, retained_state).into_iter()),
)
}
}
}
src/command/mod.rs (line 142)
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
pub fn default_features(
&self,
version: git_transport::Protocol,
server_capabilities: &Capabilities,
) -> Vec<Feature> {
match self {
Command::Fetch => match version {
git_transport::Protocol::V1 => {
let has_multi_ack_detailed = server_capabilities.contains("multi_ack_detailed");
let has_sideband_64k = server_capabilities.contains("side-band-64k");
self.all_features(version)
.iter()
.copied()
.filter(|feature| match *feature {
"side-band" if has_sideband_64k => false,
"multi_ack" if has_multi_ack_detailed => false,
"no-progress" => false,
feature => server_capabilities.contains(feature),
})
.map(|s| (s, None))
.collect()
}
git_transport::Protocol::V2 => {
let supported_features: Vec<_> = server_capabilities
.iter()
.find_map(|c| {
if c.name() == Command::Fetch.as_str() {
c.values().map(|v| v.map(|f| f.to_owned()).collect())
} else {
None
}
})
.unwrap_or_default();
self.all_features(version)
.iter()
.copied()
.filter(|feature| supported_features.iter().any(|supported| supported == feature))
.map(|s| (s, None))
.collect()
}
},
Command::LsRefs => vec![],
}
}
/// Panics if the given arguments and features don't match what's statically known. It's considered a bug in the delegate.
pub(crate) fn validate_argument_prefixes_or_panic(
&self,
version: git_transport::Protocol,
server: &Capabilities,
arguments: &[BString],
features: &[Feature],
) {
let allowed = self.all_argument_prefixes();
for arg in arguments {
if allowed.iter().any(|allowed| arg.starts_with(allowed.as_bytes())) {
continue;
}
panic!("{}: argument {} is not known or allowed", self.as_str(), arg);
}
match version {
git_transport::Protocol::V1 => {
for (feature, _) in features {
if server
.iter()
.any(|c| feature.starts_with(c.name().to_str_lossy().as_ref()))
{
continue;
}
panic!("{}: capability {} is not supported", self.as_str(), feature);
}
}
git_transport::Protocol::V2 => {
let allowed = server
.iter()
.find_map(|c| {
if c.name() == self.as_str().as_bytes().as_bstr() {
c.values().map(|v| v.map(|f| f.to_string()).collect::<Vec<_>>())
} else {
None
}
})
.unwrap_or_default();
for (feature, _) in features {
if allowed.iter().any(|allowed| feature == allowed) {
continue;
}
match *feature {
"agent" => {}
_ => panic!("{}: V2 feature/capability {} is not supported", self.as_str(), feature),
}
}
}
}
}
Trait Implementations§
source§impl Ord for Command
impl Ord for Command
source§impl PartialEq<Command> for Command
impl PartialEq<Command> for Command
source§impl PartialOrd<Command> for Command
impl PartialOrd<Command> for Command
1.0.0 · source§fn le(&self, other: &Rhs) -> bool
fn le(&self, other: &Rhs) -> bool
This method tests less than or equal to (for
self
and other
) and is used by the <=
operator. Read more