Skip to content

Commit

Permalink
[git-transport] simplify parsing capabilities from lines
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed May 13, 2021
1 parent 8ce28e7 commit 401af09
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 41 deletions.
2 changes: 1 addition & 1 deletion git-protocol/src/fetch/tests/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ mod v2 {
use git_transport::client::Capabilities;

fn capabilities(command: &str, input: &str) -> Capabilities {
Capabilities::from_lines(format!("version 2\n{}={}", command, input).as_bytes())
Capabilities::from_lines(Some(Ok("version 2".into())), format!("{}={}", command, input))
.expect("valid input for V2 capabilities")
}

Expand Down
74 changes: 34 additions & 40 deletions git-transport/src/client/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,32 +84,17 @@ impl Capabilities {
))
}

/// Returns true of the given `feature` is mentioned in this list of capabilities.
pub fn contains(&self, feature: &str) -> bool {
self.capability(feature).is_some()
}

/// Returns the capability with `name`.
pub fn capability(&self, name: &str) -> Option<Capability<'_>> {
self.iter().find(|c| c.name() == name.as_bytes().as_bstr())
}

/// Returns an iterator over all capabilities.
pub fn iter(&self) -> impl Iterator<Item = Capability<'_>> {
self.data
.split(move |b| *b == self.value_sep)
.map(|c| Capability(c.as_bstr()))
}
}

#[cfg(feature = "blocking-client")]
impl Capabilities {
/// Parse capabilities from the given `read`.
/// Parse capabilities from the given a `first_line` and the rest of the lines as single newline
/// separated string via `remaining_lines`.
///
/// Useful for parsing capabilities from a data sent from a server.
pub fn from_lines(read: impl io::BufRead) -> Result<Capabilities, Error> {
let mut lines = read.lines();
let version_line = lines.next().ok_or(Error::MissingVersionLine)??;
/// Useful for parsing capabilities from a data sent from a server, and to avoid having to deal with
/// blocking and async traits for as long as possible. There is no value in parsing a few bytes
/// in a non-blocking fashion.
pub fn from_lines(
first_line: Option<impl Into<std::io::Result<String>>>,
remaining_lines: impl Into<String>,
) -> Result<Capabilities, Error> {
let version_line = first_line.map(Into::into).ok_or(Error::MissingVersionLine)??;
let (name, value) = version_line.split_at(
version_line
.find(' ')
Expand All @@ -123,28 +108,33 @@ impl Capabilities {
}
Ok(Capabilities {
value_sep: b'\n',
data: lines
.inspect(|l| {
if let Ok(l) = l {
assert!(
!l.contains('\n'),
"newlines are not expected in keys or values, got '{}'",
l
)
}
})
.collect::<Result<Vec<_>, _>>()?
.join("\n")
.into(),
data: remaining_lines.into().into(),
})
}

/// Returns true of the given `feature` is mentioned in this list of capabilities.
pub fn contains(&self, feature: &str) -> bool {
self.capability(feature).is_some()
}

/// Returns the capability with `name`.
pub fn capability(&self, name: &str) -> Option<Capability<'_>> {
self.iter().find(|c| c.name() == name.as_bytes().as_bstr())
}

/// Returns an iterator over all capabilities.
pub fn iter(&self) -> impl Iterator<Item = Capability<'_>> {
self.data
.split(move |b| *b == self.value_sep)
.map(|c| Capability(c.as_bstr()))
}
}

#[cfg(feature = "blocking-client")]
pub(crate) mod recv {
use crate::{client, client::Capabilities, Protocol};
use bstr::ByteSlice;
use std::io;
use std::{io, io::BufRead};

pub struct Outcome<'a> {
pub capabilities: Capabilities,
Expand Down Expand Up @@ -189,7 +179,11 @@ pub(crate) mod recv {
})
}
Protocol::V2 => Ok(Outcome {
capabilities: Capabilities::from_lines(rd.as_read())?,
capabilities: {
let rd = rd.as_read();
let mut lines = rd.lines();
Capabilities::from_lines(lines.next(), lines.collect::<Result<Vec<_>, _>>()?.join("\n"))?
},
refs: None,
protocol: Protocol::V2,
}),
Expand Down

0 comments on commit 401af09

Please sign in to comment.