1
  2
  3
  4
  5
  6
  7
  8
  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
 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
use crate::fetch::{Arguments, Ref, Response};
use bstr::BString;
use git_features::progress::Progress;
use git_transport::client::Capabilities;
use std::io;

/// Defines what to do next after certain [`Delegate`] operations.
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
pub enum Action {
    /// Continue the typical flow of operations in this flow.
    Continue,
    /// Close the connection without making any further requests.
    Close,
}

/// The protocol delegate is the bare minimal interface needed to fully control the [`fetch`][crate::fetch()] operation.
///
/// Implementations of this trait are controlled by code with intricate knowledge about how fetching works in protocol version V1 and V2,
/// so you don't have to.
/// Everything is tucked away behind type-safety so 'nothing can go wrong'©. Runtime assertions assure invalid
/// features or arguments don't make it to the server in the first place.
/// Please note that this trait mostly corresponds to what V2 would look like, even though V1 is supported as well.
pub trait Delegate {
    /// Called before invoking 'ls-refs' on the server to allow providing it with additional `arguments` and to enable `features`.
    ///
    /// Note that some arguments are preset based on typical use, and `features` are preset to maximize options.
    /// The `server` capabilities can be used to see which additional capabilities the server supports as per the handshake which happened prior.
    /// Note that this is called only if we are using protocol version 2.
    fn prepare_ls_refs(
        &mut self,
        _server: &Capabilities,
        _arguments: &mut Vec<BString>,
        _features: &mut Vec<(&str, Option<&str>)>,
    ) {
    }

    /// Called before invoking the 'fetch' interaction with `features` pre-filled for typical use
    /// and to maximize capabilities to allow aborting an interaction early.
    ///
    /// `refs` is a list of known references on the remote based on the handshake or a prior call to ls_refs.
    /// These can be used to abort early in case the refs are already known here.
    ///
    /// As there will be another call allowing to post arguments conveniently in the correct format, i.e. `want hex-oid`,
    /// there is no way to set arguments at this time.
    ///
    /// `version` is the actually supported version as reported by the server, which is relevant in case the server requested a downgrade.
    /// `server` capabilities is a list of features the server supports for your information, along with enabled `features` that the server knows about.
    fn prepare_fetch(
        &mut self,
        _version: git_transport::Protocol,
        _server: &Capabilities,
        _features: &mut Vec<(&str, Option<&str>)>,
        _refs: &[Ref],
    ) -> Action {
        Action::Continue
    }

    /// A method called repeatedly to negotiate the objects to receive in [`receive_pack(…)`][Delegate::receive_pack()].
    ///
    /// The first call has `previous` set to `None` as there was no previous response. Every call that follows `previous`
    /// will be set to `Some`.
    ///
    /// ### If `previous` is `None`…
    ///
    /// Given a list of `arguments` to populate with wants, shallows, filters and other contextual information to be
    /// sent to the server. This method is called once.
    /// Send the objects you `have` have afterwards based on the tips of your refs, in preparation to walk down their parents
    /// with each call to `negotiate` to find the common base(s).
    ///
    /// Note that you should not `want` and object that you already have.
    /// `refs` are the the tips of on the server side, effectively the latest objects _they_ have.
    ///
    /// Return `Action::Close` if you know that there are no `haves` on your end to allow the server to send all of its objects
    /// as is the case during initial clones.
    ///
    /// ### If `previous` is `Some`…
    ///
    /// Populate `arguments` with the objects you `have` starting from the tips of _your_ refs, taking into consideration
    /// the `previous` response of the server to see which objects they acknowledged to have. You have to maintain
    /// enough state to be able to walk down from your tips on each call, if they are not in common, and keep setting `have`
    /// for those which are in common if that helps teaching the server about our state and to acknowledge their existence on _their_ end.
    /// This method is called until the other side signals they are ready to send a pack.
    /// Return `Action::Close` if you want to give up before finding a common base. This can happen if the remote repository
    /// has radically changed so there are no bases, or they are very far in the past, causing all objects to be sent.
    fn negotiate(&mut self, refs: &[Ref], arguments: &mut Arguments, previous: Option<&Response>) -> Action;

    /// Receive a pack provided from the given `input`.
    ///
    /// Use `progress` to emit your own progress messages when decoding the pack.
    ///
    /// `refs` of the remote side are provided for convenience, along with the parsed `previous` response in case you want
    /// to check additional acks.
    fn receive_pack(
        &mut self,
        input: impl io::BufRead,
        progress: impl Progress,
        refs: &[Ref],
        previous: &Response,
    ) -> io::Result<()>;
}