| mod error { |
| use crate::handshake::refs::parse; |
| |
| /// The error returned by [ls_refs()][crate::ls_refs()]. |
| #[derive(Debug, thiserror::Error)] |
| #[allow(missing_docs)] |
| pub enum Error { |
| #[error(transparent)] |
| Io(#[from] std::io::Error), |
| #[error(transparent)] |
| Transport(#[from] gix_transport::client::Error), |
| #[error(transparent)] |
| Parse(#[from] parse::Error), |
| } |
| |
| impl gix_transport::IsSpuriousError for Error { |
| fn is_spurious(&self) -> bool { |
| match self { |
| Error::Io(err) => err.is_spurious(), |
| Error::Transport(err) => err.is_spurious(), |
| _ => false, |
| } |
| } |
| } |
| } |
| pub use error::Error; |
| |
| /// What to do after preparing ls-refs in [ls_refs()][crate::ls_refs()]. |
| #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] |
| pub enum Action { |
| /// Continue by sending a 'ls-refs' command. |
| Continue, |
| /// Skip 'ls-refs' entirely. |
| /// |
| /// This is useful if the `ref-in-want` capability is taken advantage of. When fetching, one must must then send |
| /// `want-ref`s during the negotiation phase. |
| Skip, |
| } |
| |
| pub(crate) mod function { |
| use std::borrow::Cow; |
| |
| use bstr::BString; |
| use gix_features::progress::Progress; |
| use gix_transport::client::{Capabilities, Transport, TransportV2Ext}; |
| use maybe_async::maybe_async; |
| |
| use super::{Action, Error}; |
| use crate::{ |
| handshake::{refs::from_v2_refs, Ref}, |
| indicate_end_of_interaction, Command, |
| }; |
| |
| /// Invoke an ls-refs V2 command on `transport`, which requires a prior handshake that yielded |
| /// server `capabilities`. `prepare_ls_refs(capabilities, arguments, features)` can be used to alter the _ls-refs_. `progress` is used to provide feedback. |
| /// Note that `prepare_ls_refs()` is expected to add the `(agent, Some(name))` to the list of `features`. |
| #[maybe_async] |
| pub async fn ls_refs( |
| mut transport: impl Transport, |
| capabilities: &Capabilities, |
| prepare_ls_refs: impl FnOnce( |
| &Capabilities, |
| &mut Vec<BString>, |
| &mut Vec<(&str, Option<Cow<'static, str>>)>, |
| ) -> std::io::Result<Action>, |
| progress: &mut impl Progress, |
| ) -> Result<Vec<Ref>, Error> { |
| let ls_refs = Command::LsRefs; |
| let mut ls_features = ls_refs.default_features(gix_transport::Protocol::V2, capabilities); |
| let mut ls_args = ls_refs.initial_arguments(&ls_features); |
| if capabilities |
| .capability("ls-refs") |
| .and_then(|cap| cap.supports("unborn")) |
| .unwrap_or_default() |
| { |
| ls_args.push("unborn".into()); |
| } |
| let refs = match prepare_ls_refs(capabilities, &mut ls_args, &mut ls_features) { |
| Ok(Action::Skip) => Vec::new(), |
| Ok(Action::Continue) => { |
| ls_refs.validate_argument_prefixes_or_panic( |
| gix_transport::Protocol::V2, |
| capabilities, |
| &ls_args, |
| &ls_features, |
| ); |
| |
| progress.step(); |
| progress.set_name("list refs"); |
| let mut remote_refs = transport |
| .invoke( |
| ls_refs.as_str(), |
| ls_features.into_iter(), |
| if ls_args.is_empty() { |
| None |
| } else { |
| Some(ls_args.into_iter()) |
| }, |
| ) |
| .await?; |
| from_v2_refs(&mut remote_refs).await? |
| } |
| Err(err) => { |
| indicate_end_of_interaction(transport).await?; |
| return Err(err.into()); |
| } |
| }; |
| Ok(refs) |
| } |
| } |