gix_protocol/
ls_refs.rs

1#[cfg(any(feature = "blocking-client", feature = "async-client"))]
2mod error {
3    use crate::handshake::refs::parse;
4
5    /// The error returned by [`ls_refs()`][crate::ls_refs()].
6    #[derive(Debug, thiserror::Error)]
7    #[allow(missing_docs)]
8    pub enum Error {
9        #[error(transparent)]
10        Io(#[from] std::io::Error),
11        #[error(transparent)]
12        Transport(#[from] gix_transport::client::Error),
13        #[error(transparent)]
14        Parse(#[from] parse::Error),
15        #[error(transparent)]
16        ArgumentValidation(#[from] crate::command::validate_argument_prefixes::Error),
17    }
18
19    impl gix_transport::IsSpuriousError for Error {
20        fn is_spurious(&self) -> bool {
21            match self {
22                Error::Io(err) => err.is_spurious(),
23                Error::Transport(err) => err.is_spurious(),
24                _ => false,
25            }
26        }
27    }
28}
29#[cfg(any(feature = "blocking-client", feature = "async-client"))]
30pub use error::Error;
31
32/// What to do after preparing ls-refs in [`ls_refs()`][crate::ls_refs()].
33#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
34pub enum Action {
35    /// Continue by sending a 'ls-refs' command.
36    Continue,
37    /// Skip 'ls-refs' entirely.
38    ///
39    /// This is useful if the `ref-in-want` capability is taken advantage of. When fetching, one must must then send
40    /// `want-ref`s during the negotiation phase.
41    Skip,
42}
43
44#[cfg(any(feature = "blocking-client", feature = "async-client"))]
45pub(crate) mod function {
46    use std::borrow::Cow;
47
48    use bstr::BString;
49    use gix_features::progress::Progress;
50    use gix_transport::client::Capabilities;
51    use maybe_async::maybe_async;
52
53    use super::{Action, Error};
54    #[cfg(feature = "async-client")]
55    use crate::transport::client::async_io::{Transport, TransportV2Ext};
56    #[cfg(feature = "blocking-client")]
57    use crate::transport::client::blocking_io::{Transport, TransportV2Ext};
58    use crate::{
59        handshake::{refs::from_v2_refs, Ref},
60        indicate_end_of_interaction, Command,
61    };
62
63    /// Invoke a ls-refs V2 command on `transport`, which requires a prior handshake that yielded
64    /// server `capabilities`. `prepare_ls_refs(capabilities, arguments)` can be used to alter the _ls-refs_.
65    /// `progress` is used to provide feedback.
66    /// The `agent` information will be added to the features sent to the server.
67    /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate.
68    #[maybe_async]
69    pub async fn ls_refs(
70        mut transport: impl Transport,
71        capabilities: &Capabilities,
72        prepare_ls_refs: impl FnOnce(&Capabilities, &mut Vec<BString>) -> std::io::Result<Action>,
73        progress: &mut impl Progress,
74        trace: bool,
75        agent: (&'static str, Option<Cow<'static, str>>),
76    ) -> Result<Vec<Ref>, Error> {
77        let _span = gix_features::trace::detail!("gix_protocol::ls_refs()", capabilities = ?capabilities);
78        let ls_refs = Command::LsRefs;
79        let mut ls_features = ls_refs.default_features(gix_transport::Protocol::V2, capabilities);
80        ls_features.push(agent);
81        let mut ls_args = ls_refs.initial_v2_arguments(&ls_features);
82        if capabilities
83            .capability("ls-refs")
84            .and_then(|cap| cap.supports("unborn"))
85            .unwrap_or_default()
86        {
87            ls_args.push("unborn".into());
88        }
89        let refs = match prepare_ls_refs(capabilities, &mut ls_args) {
90            Ok(Action::Skip) => Vec::new(),
91            Ok(Action::Continue) => {
92                ls_refs.validate_argument_prefixes(
93                    gix_transport::Protocol::V2,
94                    capabilities,
95                    &ls_args,
96                    &ls_features,
97                )?;
98
99                progress.step();
100                progress.set_name("list refs".into());
101                let mut remote_refs = transport
102                    .invoke(
103                        ls_refs.as_str(),
104                        ls_features.into_iter(),
105                        if ls_args.is_empty() {
106                            None
107                        } else {
108                            Some(ls_args.into_iter())
109                        },
110                        trace,
111                    )
112                    .await?;
113                from_v2_refs(&mut remote_refs).await?
114            }
115            Err(err) => {
116                indicate_end_of_interaction(transport, trace).await?;
117                return Err(err.into());
118            }
119        };
120        Ok(refs)
121    }
122}