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, Transport, TransportV2Ext};
51    use maybe_async::maybe_async;
52
53    use super::{Action, Error};
54    use crate::{
55        handshake::{refs::from_v2_refs, Ref},
56        indicate_end_of_interaction, Command,
57    };
58
59    /// Invoke an ls-refs V2 command on `transport`, which requires a prior handshake that yielded
60    /// server `capabilities`. `prepare_ls_refs(capabilities, arguments, features)` can be used to alter the _ls-refs_. `progress` is used to provide feedback.
61    /// Note that `prepare_ls_refs()` is expected to add the `(agent, Some(name))` to the list of `features`.
62    /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate.
63    #[maybe_async]
64    pub async fn ls_refs(
65        mut transport: impl Transport,
66        capabilities: &Capabilities,
67        prepare_ls_refs: impl FnOnce(
68            &Capabilities,
69            &mut Vec<BString>,
70            &mut Vec<(&str, Option<Cow<'static, str>>)>,
71        ) -> std::io::Result<Action>,
72        progress: &mut impl Progress,
73        trace: bool,
74    ) -> Result<Vec<Ref>, Error> {
75        let _span = gix_features::trace::detail!("gix_protocol::ls_refs()", capabilities = ?capabilities);
76        let ls_refs = Command::LsRefs;
77        let mut ls_features = ls_refs.default_features(gix_transport::Protocol::V2, capabilities);
78        let mut ls_args = ls_refs.initial_v2_arguments(&ls_features);
79        if capabilities
80            .capability("ls-refs")
81            .and_then(|cap| cap.supports("unborn"))
82            .unwrap_or_default()
83        {
84            ls_args.push("unborn".into());
85        }
86        let refs = match prepare_ls_refs(capabilities, &mut ls_args, &mut ls_features) {
87            Ok(Action::Skip) => Vec::new(),
88            Ok(Action::Continue) => {
89                ls_refs.validate_argument_prefixes(
90                    gix_transport::Protocol::V2,
91                    capabilities,
92                    &ls_args,
93                    &ls_features,
94                )?;
95
96                progress.step();
97                progress.set_name("list refs".into());
98                let mut remote_refs = transport
99                    .invoke(
100                        ls_refs.as_str(),
101                        ls_features.into_iter(),
102                        if ls_args.is_empty() {
103                            None
104                        } else {
105                            Some(ls_args.into_iter())
106                        },
107                        trace,
108                    )
109                    .await?;
110                from_v2_refs(&mut remote_refs).await?
111            }
112            Err(err) => {
113                indicate_end_of_interaction(transport, trace).await?;
114                return Err(err.into());
115            }
116        };
117        Ok(refs)
118    }
119}