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
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
use std::{
    any::Any,
    borrow::Cow,
    ops::{Deref, DerefMut},
};

use bstr::BStr;

#[cfg(any(feature = "blocking-client", feature = "async-client"))]
use crate::client::{MessageKind, RequestWriter, WriteMode};
use crate::{client::Error, Protocol};

/// This trait represents all transport related functions that don't require any input/output to be done which helps
/// implementation to share more code across blocking and async programs.
pub trait TransportWithoutIO {
    /// If the handshake or subsequent reads failed with [`std::io::ErrorKind::PermissionDenied`], use this method to
    /// inform the transport layer about the identity to use for subsequent calls.
    /// If authentication continues to fail even with an identity set, consider communicating this to the provider
    /// of the identity in order to mark it as invalid. Otherwise the user might have difficulty updating obsolete
    /// credentials.
    /// Please note that most transport layers are unauthenticated and thus return [an error][Error::AuthenticationUnsupported] here.
    fn set_identity(&mut self, _identity: gix_sec::identity::Account) -> Result<(), Error> {
        Err(Error::AuthenticationUnsupported)
    }
    /// Get a writer for sending data and obtaining the response. It can be configured in various ways
    /// to support the task at hand.
    /// `write_mode` determines how calls to the `write(…)` method are interpreted, and `on_into_read` determines
    /// which message to write when the writer is turned into the response reader using [`into_read()`][RequestWriter::into_read()].
    /// If `trace` is `true`, then all packetlines written and received will be traced using facilities provided by the `gix_trace` crate.
    #[cfg(any(feature = "blocking-client", feature = "async-client"))]
    fn request(
        &mut self,
        write_mode: WriteMode,
        on_into_read: MessageKind,
        trace: bool,
    ) -> Result<RequestWriter<'_>, Error>;

    /// Returns the canonical URL pointing to the destination of this transport.
    fn to_url(&self) -> Cow<'_, BStr>;

    /// If the actually advertised server version is contained in the returned slice or it is empty, continue as normal,
    /// assume the server's protocol version is desired or acceptable.
    ///
    /// Otherwise, abort the fetch operation with an error to avoid continuing any interaction with the transport.
    ///
    /// In V1 this means a potentially large list of advertised refs won't be read, instead the connection is ignored
    /// leaving the server with a client who potentially unexpectedly terminated the connection.
    ///
    /// Note that `transport.close()` is not called explicitly.
    ///
    /// Custom transports can override this to prevent any use of older protocol versions.
    fn supported_protocol_versions(&self) -> &[Protocol] {
        &[]
    }

    /// Returns true if the transport provides persistent connections across multiple requests, or false otherwise.
    /// Not being persistent implies that certain information has to be resent on each 'turn'
    /// of the fetch negotiation or that the end of interaction (i.e. no further request will be made) has to be indicated
    /// to the server for most graceful termination of the connection.
    fn connection_persists_across_multiple_requests(&self) -> bool;

    /// Pass `config` can be cast and interpreted by the implementation, as documented separately.
    ///
    /// The caller must know how that `config` data looks like for the intended implementation.
    fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>;
}

// Would be nice if the box implementation could auto-forward to all implemented traits.
impl<T: TransportWithoutIO + ?Sized> TransportWithoutIO for Box<T> {
    fn set_identity(&mut self, identity: gix_sec::identity::Account) -> Result<(), Error> {
        self.deref_mut().set_identity(identity)
    }

    #[cfg(any(feature = "blocking-client", feature = "async-client"))]
    fn request(
        &mut self,
        write_mode: WriteMode,
        on_into_read: MessageKind,
        trace: bool,
    ) -> Result<RequestWriter<'_>, Error> {
        self.deref_mut().request(write_mode, on_into_read, trace)
    }

    fn to_url(&self) -> Cow<'_, BStr> {
        self.deref().to_url()
    }

    fn supported_protocol_versions(&self) -> &[Protocol] {
        self.deref().supported_protocol_versions()
    }

    fn connection_persists_across_multiple_requests(&self) -> bool {
        self.deref().connection_persists_across_multiple_requests()
    }

    fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
        self.deref_mut().configure(config)
    }
}

impl<T: TransportWithoutIO + ?Sized> TransportWithoutIO for &mut T {
    fn set_identity(&mut self, identity: gix_sec::identity::Account) -> Result<(), Error> {
        self.deref_mut().set_identity(identity)
    }

    #[cfg(any(feature = "blocking-client", feature = "async-client"))]
    fn request(
        &mut self,
        write_mode: WriteMode,
        on_into_read: MessageKind,
        trace: bool,
    ) -> Result<RequestWriter<'_>, Error> {
        self.deref_mut().request(write_mode, on_into_read, trace)
    }

    fn to_url(&self) -> Cow<'_, BStr> {
        self.deref().to_url()
    }

    fn supported_protocol_versions(&self) -> &[Protocol] {
        self.deref().supported_protocol_versions()
    }

    fn connection_persists_across_multiple_requests(&self) -> bool {
        self.deref().connection_persists_across_multiple_requests()
    }

    fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
        self.deref_mut().configure(config)
    }
}