1#[cfg(any(feature = "blocking-client", feature = "async-client"))]
2mod error {
3 use crate::handshake::refs::parse;
4
5 #[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#[cfg(any(feature = "blocking-client", feature = "async-client"))]
33pub(crate) mod function {
34 use std::{borrow::Cow, collections::HashSet};
35
36 use bstr::{BString, ByteVec};
37 use gix_features::progress::Progress;
38 use gix_transport::client::Capabilities;
39
40 use super::Error;
41 #[cfg(feature = "async-client")]
42 use crate::transport::client::async_io::{self, TransportV2Ext as _};
43 #[cfg(feature = "blocking-client")]
44 use crate::transport::client::blocking_io::{self, TransportV2Ext as _};
45 use crate::{
46 handshake::{refs::from_v2_refs, Ref},
47 Command,
48 };
49
50 pub struct LsRefsCommand<'a> {
55 pub(crate) capabilities: &'a Capabilities,
56 features: Vec<(&'static str, Option<Cow<'static, str>>)>,
57 arguments: Vec<BString>,
58 }
59
60 impl<'a> LsRefsCommand<'a> {
61 pub fn new(
64 prefix_refspecs: Option<&[gix_refspec::RefSpec]>,
65 capabilities: &'a Capabilities,
66 agent: (&'static str, Option<Cow<'static, str>>),
67 ) -> Self {
68 let ls_refs = Command::LsRefs;
69 let mut features = ls_refs.default_features(gix_transport::Protocol::V2, capabilities);
70 features.push(agent);
71 let mut arguments = ls_refs.initial_v2_arguments(&features);
72 if capabilities
73 .capability("ls-refs")
74 .and_then(|cap| cap.supports("unborn"))
75 .unwrap_or_default()
76 {
77 arguments.push("unborn".into());
78 }
79
80 if let Some(refspecs) = prefix_refspecs {
81 let mut seen = HashSet::new();
82 for spec in refspecs {
83 let spec = spec.to_ref();
84 if seen.insert(spec.instruction()) {
85 let mut prefixes = Vec::with_capacity(1);
86 spec.expand_prefixes(&mut prefixes);
87 for mut prefix in prefixes {
88 prefix.insert_str(0, "ref-prefix ");
89 arguments.push(prefix);
90 }
91 }
92 }
93 }
94
95 Self {
96 capabilities,
97 features,
98 arguments,
99 }
100 }
101
102 #[cfg(feature = "async-client")]
107 pub async fn invoke_async(
108 self,
109 mut transport: impl async_io::Transport,
110 progress: &mut impl Progress,
111 trace: bool,
112 ) -> Result<Vec<Ref>, Error> {
113 let _span = gix_features::trace::detail!("gix_protocol::LsRefsCommand::invoke_async()");
114 Command::LsRefs.validate_argument_prefixes(
115 gix_transport::Protocol::V2,
116 self.capabilities,
117 &self.arguments,
118 &self.features,
119 )?;
120
121 progress.step();
122 progress.set_name("list refs".into());
123 let mut remote_refs = transport
124 .invoke(
125 Command::LsRefs.as_str(),
126 self.features.into_iter(),
127 if self.arguments.is_empty() {
128 None
129 } else {
130 Some(self.arguments.into_iter())
131 },
132 trace,
133 )
134 .await?;
135 Ok(from_v2_refs(&mut remote_refs).await?)
136 }
137
138 #[cfg(feature = "blocking-client")]
143 pub fn invoke_blocking(
144 self,
145 mut transport: impl blocking_io::Transport,
146 progress: &mut impl Progress,
147 trace: bool,
148 ) -> Result<Vec<Ref>, Error> {
149 let _span = gix_features::trace::detail!("gix_protocol::LsRefsCommand::invoke_blocking()");
150 Command::LsRefs.validate_argument_prefixes(
151 gix_transport::Protocol::V2,
152 self.capabilities,
153 &self.arguments,
154 &self.features,
155 )?;
156
157 progress.step();
158 progress.set_name("list refs".into());
159 let mut remote_refs = transport.invoke(
160 Command::LsRefs.as_str(),
161 self.features.into_iter(),
162 if self.arguments.is_empty() {
163 None
164 } else {
165 Some(self.arguments.into_iter())
166 },
167 trace,
168 )?;
169 Ok(from_v2_refs(&mut remote_refs)?)
170 }
171 }
172}