summaryrefslogtreecommitdiffstats
path: root/vendor/gix-protocol/src/command
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-protocol/src/command
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-protocol/src/command')
-rw-r--r--vendor/gix-protocol/src/command/mod.rs214
-rw-r--r--vendor/gix-protocol/src/command/tests.rs156
2 files changed, 370 insertions, 0 deletions
diff --git a/vendor/gix-protocol/src/command/mod.rs b/vendor/gix-protocol/src/command/mod.rs
new file mode 100644
index 000000000..d560220d0
--- /dev/null
+++ b/vendor/gix-protocol/src/command/mod.rs
@@ -0,0 +1,214 @@
+//! V2 command abstraction to validate invocations and arguments, like a database of what we know about them.
+use std::borrow::Cow;
+
+use super::Command;
+
+/// A key value pair of values known at compile time.
+pub type Feature = (&'static str, Option<Cow<'static, str>>);
+
+impl Command {
+ /// Produce the name of the command as known by the server side.
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Command::LsRefs => "ls-refs",
+ Command::Fetch => "fetch",
+ }
+ }
+}
+
+#[cfg(any(test, feature = "async-client", feature = "blocking-client"))]
+mod with_io {
+ use bstr::{BString, ByteSlice};
+ use gix_transport::client::Capabilities;
+
+ use crate::{command::Feature, Command};
+
+ impl Command {
+ /// Only V2
+ fn all_argument_prefixes(&self) -> &'static [&'static str] {
+ match self {
+ Command::LsRefs => &["symrefs", "peel", "ref-prefix ", "unborn"],
+ Command::Fetch => &[
+ "want ", // hex oid
+ "have ", // hex oid
+ "done",
+ "thin-pack",
+ "no-progress",
+ "include-tag",
+ "ofs-delta",
+ // Shallow feature/capability
+ "shallow ", // hex oid
+ "deepen ", // commit depth
+ "deepen-relative",
+ "deepen-since ", // time-stamp
+ "deepen-not ", // rev
+ // filter feature/capability
+ "filter ", // filter-spec
+ // ref-in-want feature
+ "want-ref ", // ref path
+ // sideband-all feature
+ "sideband-all",
+ // packfile-uris feature
+ "packfile-uris ", // protocols
+ // wait-for-done feature
+ "wait-for-done",
+ ],
+ }
+ }
+
+ fn all_features(&self, version: gix_transport::Protocol) -> &'static [&'static str] {
+ match self {
+ Command::LsRefs => &[],
+ Command::Fetch => match version {
+ gix_transport::Protocol::V1 => &[
+ "multi_ack",
+ "thin-pack",
+ "side-band",
+ "side-band-64k",
+ "ofs-delta",
+ "shallow",
+ "deepen-since",
+ "deepen-not",
+ "deepen-relative",
+ "no-progress",
+ "include-tag",
+ "multi_ack_detailed",
+ "allow-tip-sha1-in-want",
+ "allow-reachable-sha1-in-want",
+ "no-done",
+ "filter",
+ ],
+ gix_transport::Protocol::V2 => &[
+ "shallow",
+ "filter",
+ "ref-in-want",
+ "sideband-all",
+ "packfile-uris",
+ "wait-for-done",
+ ],
+ },
+ }
+ }
+
+ /// Compute initial arguments based on the given `features`. They are typically provided by the `default_features(…)` method.
+ /// Only useful for V2
+ pub(crate) fn initial_arguments(&self, features: &[Feature]) -> Vec<BString> {
+ match self {
+ Command::Fetch => ["thin-pack", "ofs-delta"]
+ .iter()
+ .map(|s| s.as_bytes().as_bstr().to_owned())
+ .chain(
+ [
+ "sideband-all",
+ /* "packfile-uris" */ // packfile-uris must be configurable and can't just be used. Some servers advertise it and reject it later.
+ ]
+ .iter()
+ .filter(|f| features.iter().any(|(sf, _)| sf == *f))
+ .map(|f| f.as_bytes().as_bstr().to_owned()),
+ )
+ .collect(),
+ Command::LsRefs => vec![b"symrefs".as_bstr().to_owned(), b"peel".as_bstr().to_owned()],
+ }
+ }
+
+ /// 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.
+ pub fn default_features(
+ &self,
+ version: gix_transport::Protocol,
+ server_capabilities: &Capabilities,
+ ) -> Vec<Feature> {
+ match self {
+ Command::Fetch => match version {
+ gix_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()
+ }
+ gix_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: gix_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 {
+ gix_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);
+ }
+ }
+ gix_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),
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/vendor/gix-protocol/src/command/tests.rs b/vendor/gix-protocol/src/command/tests.rs
new file mode 100644
index 000000000..12c0eb6df
--- /dev/null
+++ b/vendor/gix-protocol/src/command/tests.rs
@@ -0,0 +1,156 @@
+mod v1 {
+ fn capabilities(input: &str) -> gix_transport::client::Capabilities {
+ gix_transport::client::Capabilities::from_bytes(format!("\0{input}").as_bytes())
+ .expect("valid input capabilities")
+ .0
+ }
+
+ const GITHUB_CAPABILITIES: &str = "multi_ack thin-pack side-band ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag allow-tip-sha1-in-want allow-reachable-sha1-in-want no-done symref=HEAD:refs/heads/main filter agent=git/github-gdf51a71f0236";
+ mod fetch {
+ mod default_features {
+ use crate::{
+ command::tests::v1::{capabilities, GITHUB_CAPABILITIES},
+ Command,
+ };
+
+ #[test]
+ fn it_chooses_the_best_multi_ack_and_sideband() {
+ assert_eq!(
+ Command::Fetch.default_features(
+ gix_transport::Protocol::V1,
+ &capabilities("multi_ack side-band side-band-64k multi_ack_detailed")
+ ),
+ &[("side-band-64k", None), ("multi_ack_detailed", None),]
+ );
+ }
+
+ #[test]
+ fn it_chooses_all_supported_non_stacking_capabilities_and_leaves_no_progress() {
+ assert_eq!(
+ Command::Fetch.default_features(gix_transport::Protocol::V1, &capabilities(GITHUB_CAPABILITIES)),
+ &[
+ ("multi_ack", None),
+ ("thin-pack", None),
+ ("side-band", None),
+ ("ofs-delta", None),
+ ("shallow", None),
+ ("deepen-since", None),
+ ("deepen-not", None),
+ ("deepen-relative", None),
+ ("include-tag", None),
+ ("allow-tip-sha1-in-want", None),
+ ("allow-reachable-sha1-in-want", None),
+ ("no-done", None),
+ ("filter", None),
+ ],
+ "we don't enforce no-progress"
+ );
+ }
+ }
+ }
+}
+
+mod v2 {
+ use gix_transport::client::Capabilities;
+
+ fn capabilities(command: &str, input: &str) -> Capabilities {
+ Capabilities::from_lines(format!("version 2\n{command}={input}").into())
+ .expect("valid input for V2 capabilities")
+ }
+
+ mod fetch {
+ mod default_features {
+ use crate::{command::tests::v2::capabilities, Command};
+
+ #[test]
+ fn all_features() {
+ assert_eq!(
+ Command::Fetch.default_features(
+ gix_transport::Protocol::V2,
+ &capabilities("fetch", "shallow filter ref-in-want sideband-all packfile-uris")
+ ),
+ ["shallow", "filter", "ref-in-want", "sideband-all", "packfile-uris"]
+ .iter()
+ .map(|s| (*s, None))
+ .collect::<Vec<_>>()
+ )
+ }
+ }
+
+ mod initial_arguments {
+ use bstr::ByteSlice;
+
+ use crate::{command::tests::v2::capabilities, Command};
+
+ #[test]
+ fn for_all_features() {
+ assert_eq!(
+ Command::Fetch.initial_arguments(&Command::Fetch.default_features(
+ gix_transport::Protocol::V2,
+ &capabilities("fetch", "shallow filter sideband-all packfile-uris")
+ )),
+ ["thin-pack", "ofs-delta", "sideband-all"]
+ .iter()
+ .map(|s| s.as_bytes().as_bstr().to_owned())
+ .collect::<Vec<_>>(),
+ "packfile-uris isn't really supported that well and we don't support it either yet"
+ )
+ }
+ }
+ }
+
+ mod ls_refs {
+ mod default_features {
+ use crate::{command::tests::v2::capabilities, Command};
+
+ #[test]
+ fn default_as_there_are_no_features() {
+ assert_eq!(
+ Command::LsRefs.default_features(
+ gix_transport::Protocol::V2,
+ &capabilities("something-else", "does not matter as there are none")
+ ),
+ &[]
+ );
+ }
+ }
+
+ mod validate {
+ use bstr::ByteSlice;
+
+ use crate::{command::tests::v2::capabilities, Command};
+
+ #[test]
+ fn ref_prefixes_can_always_be_used() {
+ Command::LsRefs.validate_argument_prefixes_or_panic(
+ gix_transport::Protocol::V2,
+ &capabilities("something else", "do-not-matter"),
+ &[b"ref-prefix hello/".as_bstr().into()],
+ &[],
+ );
+ }
+
+ #[test]
+ #[should_panic]
+ fn unknown_argument() {
+ Command::LsRefs.validate_argument_prefixes_or_panic(
+ gix_transport::Protocol::V2,
+ &capabilities("other", "do-not-matter"),
+ &[b"definitely-nothing-we-know".as_bstr().into()],
+ &[],
+ );
+ }
+
+ #[test]
+ #[should_panic]
+ fn unknown_feature() {
+ Command::LsRefs.validate_argument_prefixes_or_panic(
+ gix_transport::Protocol::V2,
+ &capabilities("other", "do-not-matter"),
+ &[],
+ &[("some-feature-that-does-not-exist", None)],
+ );
+ }
+ }
+ }
+}