220 lines · 8.4 KB · Rust
RawDownload
use std::fmt; use bstr::{BStr, BString, ByteSlice, ByteVec}; /// The arguments passed to a server command.#[derive(Debug)]pub struct Arguments {    /// The active features/capabilities of the fetch invocation    #[cfg(any(feature = "async-client", feature = "blocking-client"))]    features: Vec<crate::command::Feature>,     args: Vec<BString>,    haves: Vec<BString>,     filter: bool,    shallow: bool,    deepen_since: bool,    deepen_not: bool,    deepen_relative: bool,    ref_in_want: bool,     features_for_first_want: Option<Vec<String>>,    #[cfg(any(feature = "async-client", feature = "blocking-client"))]    version: git_transport::Protocol,} impl Arguments {    /// Return true if there is no argument at all.    ///    /// This can happen if callers assure that they won't add 'wants' if their 'have' is the same, i.e. if the remote has nothing    /// new for them.    pub fn is_empty(&self) -> bool {        self.haves.is_empty() && !self.args.iter().rev().any(|arg| arg.starts_with_str("want "))    }    /// Return true if ref filters is supported.    pub fn can_use_filter(&self) -> bool {        self.filter    }    /// Return true if shallow refs are supported.    ///    /// This is relevant for partial clones when using `--depth X`.    pub fn can_use_shallow(&self) -> bool {        self.shallow    }    /// Return true if the 'deepen' capability is supported.    ///    /// This is relevant for partial clones when using `--depth X` and retrieving additional history.    pub fn can_use_deepen(&self) -> bool {        self.shallow    }    /// Return true if the 'deepen_since' capability is supported.    ///    /// This is relevant for partial clones when using `--depth X` and retrieving additional history    /// based on a date beyond which all history should be present.    pub fn can_use_deepen_since(&self) -> bool {        self.deepen_since    }    /// Return true if the 'deepen_not' capability is supported.    ///    /// This is relevant for partial clones when using `--depth X`.    pub fn can_use_deepen_not(&self) -> bool {        self.deepen_not    }    /// Return true if the 'deepen_relative' capability is supported.    ///    /// This is relevant for partial clones when using `--depth X`.    pub fn can_use_deepen_relative(&self) -> bool {        self.deepen_relative    }    /// Return true if the 'ref-in-want' capability is supported.    ///    /// This can be used to bypass 'ls-refs' entirely in protocol v2.    pub fn can_use_ref_in_want(&self) -> bool {        self.ref_in_want    }     /// Add the given `id` pointing to a commit to the 'want' list.    ///    /// As such it should be included in the server response as it's not present on the client.    pub fn want(&mut self, id: impl AsRef<git_hash::oid>) {        match self.features_for_first_want.take() {            Some(features) => self.prefixed("want ", format!("{} {}", id.as_ref(), features.join(" "))),            None => self.prefixed("want ", id.as_ref()),        }    }    /// Add the given ref to the 'want-ref' list.    ///    /// The server should respond with a corresponding 'wanted-refs' section if it will include the    /// wanted ref in the packfile response.    pub fn want_ref(&mut self, ref_path: &BStr) {        let mut arg = BString::from("want-ref ");        arg.push_str(ref_path);        self.args.push(arg);    }    /// Add the given `id` pointing to a commit to the 'have' list.    ///    /// As such it should _not_ be included in the server response as it's already present on the client.    pub fn have(&mut self, id: impl AsRef<git_hash::oid>) {        self.haves.push(format!("have {}", id.as_ref()).into());    }    /// Add the given `id` pointing to a commit to the 'shallow' list.    pub fn shallow(&mut self, id: impl AsRef<git_hash::oid>) {        assert!(self.shallow, "'shallow' feature required for 'shallow <id>'");        self.prefixed("shallow ", id.as_ref());    }    /// Deepen the commit history by `depth` amount of commits.    pub fn deepen(&mut self, depth: usize) {        assert!(self.shallow, "'shallow' feature required for deepen");        self.prefixed("deepen ", depth);    }    /// Deepen the commit history to include all commits from now to `seconds_since_unix_epoch`.    pub fn deepen_since(&mut self, seconds_since_unix_epoch: usize) {        assert!(self.deepen_since, "'deepen-since' feature required");        self.prefixed("deepen-since ", seconds_since_unix_epoch);    }    /// Deepen the commit history in a relative instead of absolute fashion.    pub fn deepen_relative(&mut self) {        assert!(self.deepen_relative, "'deepen-relative' feature required");        self.args.push("deepen-relative".into());    }    /// Do not include commits reachable by the given `ref_path` when deepening the history.    pub fn deepen_not(&mut self, ref_path: &BStr) {        assert!(self.deepen_not, "'deepen-not' feature required");        let mut line = BString::from("deepen-not ");        line.extend_from_slice(ref_path);        self.args.push(line);    }    /// Set the given filter `spec` when listing references.    pub fn filter(&mut self, spec: &str) {        assert!(self.filter, "'filter' feature required");        self.prefixed("filter ", spec);    }    fn prefixed(&mut self, prefix: &str, value: impl fmt::Display) {        self.args.push(format!("{}{}", prefix, value).into());    }    /// Create a new instance to help setting up arguments to send to the server as part of a `fetch` operation    /// for which `features` are the available and configured features to use.    #[cfg(any(feature = "async-client", feature = "blocking-client"))]    pub fn new(version: git_transport::Protocol, features: Vec<crate::command::Feature>) -> Self {        use crate::Command;        let has = |name: &str| features.iter().any(|f| f.0 == name);        let filter = has("filter");        let shallow = has("shallow");        let ref_in_want = has("ref-in-want");        let mut deepen_since = shallow;        let mut deepen_not = shallow;        let mut deepen_relative = shallow;        let (initial_arguments, features_for_first_want) = match version {            git_transport::Protocol::V1 => {                deepen_since = has("deepen-since");                deepen_not = has("deepen-not");                deepen_relative = has("deepen-relative");                let baked_features = features                    .iter()                    .map(|(n, v)| match v {                        Some(v) => format!("{}={}", n, v),                        None => n.to_string(),                    })                    .collect::<Vec<_>>();                (Vec::new(), Some(baked_features))            }            git_transport::Protocol::V2 => (Command::Fetch.initial_arguments(&features), None),        };         Arguments {            features,            version,            args: initial_arguments,            haves: Vec::new(),            filter,            shallow,            deepen_not,            deepen_relative,            ref_in_want,            deepen_since,            features_for_first_want,        }    }} #[cfg(any(feature = "blocking-client", feature = "async-client"))]mod shared {    use bstr::{BString, ByteSlice};    use git_transport::{client, client::MessageKind};     use crate::fetch::Arguments;     impl Arguments {        pub(in crate::fetch::arguments) fn prepare_v1(            &mut self,            transport_is_stateful: bool,            add_done_argument: bool,        ) -> Result<(MessageKind, Option<Vec<BString>>), client::Error> {            if self.haves.is_empty() {                assert!(add_done_argument, "If there are no haves, is_done must be true.");            }            let on_into_read = if add_done_argument {                client::MessageKind::Text(&b"done"[..])            } else {                client::MessageKind::Flush            };            let retained_state = if transport_is_stateful {                None            } else {                Some(self.args.clone())            };             if let Some(first_arg_position) = self.args.iter().position(|l| l.starts_with_str("want ")) {                self.args.swap(first_arg_position, 0);            }            Ok((on_into_read, retained_state))        }    }} #[cfg(feature = "async-client")]mod async_io; #[cfg(feature = "blocking-client")]mod blocking_io;