diff --git a/Cargo.lock b/Cargo.lock index a6db1c6a981..460246ce6fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1025,7 +1025,7 @@ dependencies = [ [[package]] name = "git-diff" -version = "0.8.2" +version = "0.9.0" dependencies = [ "git-hash", "git-object", @@ -1142,7 +1142,7 @@ dependencies = [ [[package]] name = "git-packetline" -version = "0.9.1" +version = "0.10.0" dependencies = [ "async-std", "bstr", @@ -1274,7 +1274,7 @@ dependencies = [ [[package]] name = "git-traverse" -version = "0.7.2" +version = "0.8.0" dependencies = [ "git-hash", "git-object", diff --git a/cargo-smart-release/src/command/release/manifest.rs b/cargo-smart-release/src/command/release/manifest.rs index b6e9755f626..1946eea2acb 100644 --- a/cargo-smart-release/src/command/release/manifest.rs +++ b/cargo-smart-release/src/command/release/manifest.rs @@ -13,7 +13,12 @@ use super::{ pub(in crate::command::release_impl) fn edit_version_and_fixup_dependent_crates<'repo>( meta: &Metadata, publishees: &[(&Package, String)], - Options { verbose, dry_run, .. }: Options, + Options { + verbose, + dry_run, + skip_publish, + .. + }: Options, ctx: &'repo Context, ) -> anyhow::Result>> { let mut locks_by_manifest_path = BTreeMap::new(); @@ -72,7 +77,11 @@ pub(in crate::command::release_impl) fn edit_version_and_fixup_dependent_crates< set_version_and_update_package_dependency(package_to_update, None, publishees, &mut lock, verbose)?; } - let message = format!("Release {}", names_and_versions(publishees)); + let message = format!( + "{} {}", + if skip_publish { "Bump" } else { "Release" }, + names_and_versions(publishees) + ); if verbose { log::info!("{} persist changes to manifests with: {:?}", will(dry_run), message); } diff --git a/git-diff/Cargo.toml b/git-diff/Cargo.toml index 4bfd81c0ebd..c2fdb81d871 100644 --- a/git-diff/Cargo.toml +++ b/git-diff/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "git-diff" -version = "0.8.2" +version = "0.9.0" repository = "https://github.com/Byron/gitoxide" license = "MIT/Apache-2.0" description = "Calculate differences between various git objects" diff --git a/git-pack/Cargo.toml b/git-pack/Cargo.toml index b3821c031a8..402a0a4bae7 100644 --- a/git-pack/Cargo.toml +++ b/git-pack/Cargo.toml @@ -35,8 +35,8 @@ all-features = true git-features = { version = "^0.16.0", path = "../git-features", features = ["crc32", "rustsha1", "progress", "zlib"] } git-hash = { version = "^0.5.0", path = "../git-hash" } git-object = { version ="^0.13.0", path = "../git-object" } -git-traverse = { version ="0.7.0", path = "../git-traverse" } -git-diff = { version ="0.8.0", path = "../git-diff" } +git-traverse = { version ="^0.8.0", path = "../git-traverse" } +git-diff = { version ="^0.9.0", path = "../git-diff" } git-tempfile = { version ="^1.0.0", path = "../git-tempfile" } smallvec = "1.3.0" diff --git a/git-packetline/CHANGELOG.md b/git-packetline/CHANGELOG.md new file mode 100644 index 00000000000..652e3b02ebf --- /dev/null +++ b/git-packetline/CHANGELOG.md @@ -0,0 +1,16 @@ +### 0.10.0 (2021-08-??) + +#### Breaking + +* **renames / moves** + - `immutable::PacketLine` -> `PacketLineRef` + - `immutable::Error` -> `ErrorRef` + - `immutable::Text` -> `TextRef` + - `immutable::Band` -> `BandRef` + - `immutable::DecodeBandError` -> `decode::band::Error` + - `pub immutable::` -> `line::` + - `pub write::` -> `write::` + +* **removals** + - `write::Writer` (is now only `Writer`) + - `read::StreamingPeekableIter` (is now only `StreamingPeekableIter`) diff --git a/git-packetline/Cargo.toml b/git-packetline/Cargo.toml index 0ec038a967e..c1ae58064a2 100644 --- a/git-packetline/Cargo.toml +++ b/git-packetline/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "git-packetline" -version = "0.9.1" +version = "0.10.0" repository = "https://github.com/Byron/gitoxide" license = "MIT/Apache-2.0" description = "A WIP crate of the gitoxide project implementing the pkt-line serialization format" authors = ["Sebastian Thiel "] edition = "2018" -include = ["src/**/*"] +include = ["src/**/*", "CHANGELOG.md"] [lib] doctest = false diff --git a/git-packetline/src/decode.rs b/git-packetline/src/decode.rs index b5a797f8b05..19321361b4f 100644 --- a/git-packetline/src/decode.rs +++ b/git-packetline/src/decode.rs @@ -1,7 +1,7 @@ use bstr::BString; use quick_error::quick_error; -use crate::{PacketLine, DELIMITER_LINE, FLUSH_LINE, MAX_DATA_LEN, MAX_LINE_LEN, RESPONSE_END_LINE, U16_HEX_BYTES}; +use crate::{PacketLineRef, DELIMITER_LINE, FLUSH_LINE, MAX_DATA_LEN, MAX_LINE_LEN, RESPONSE_END_LINE, U16_HEX_BYTES}; quick_error! { /// The error used in the [`decode`][crate::decode] module @@ -29,13 +29,31 @@ quick_error! { } } +/// +pub mod band { + use quick_error::quick_error; + quick_error! { + /// The error used in [`PacketLineRef::decode_band()`]. + #[derive(Debug)] + #[allow(missing_docs)] + pub enum Error { + InvalidSideBand(band: u8) { + display("attempt to decode a non-side channel line or input was malformed: {}", band) + } + NonDataLine { + display("attempt to decode a non-data line into a side-channel band") + } + } + } +} + /// A utility return type to support incremental parsing of packet lines. #[derive(Debug, Clone)] pub enum Stream<'a> { /// Indicate a single packet line was parsed completely Complete { /// The parsed packet line - line: PacketLine<'a>, + line: PacketLineRef<'a>, /// The amount of bytes consumed from input bytes_consumed: usize, }, @@ -49,7 +67,7 @@ pub enum Stream<'a> { /// The result of [`hex_prefix()`] indicating either a special packet line or the amount of wanted bytes pub enum PacketLineOrWantedSize<'a> { /// The special kind of packet line decoded from the hex prefix. It never contains actual data. - Line(PacketLine<'a>), + Line(PacketLineRef<'a>), /// The amount of bytes indicated by the hex prefix of the packet line. Wanted(u16), } @@ -58,9 +76,9 @@ pub enum PacketLineOrWantedSize<'a> { pub fn hex_prefix(four_bytes: &[u8]) -> Result, Error> { debug_assert_eq!(four_bytes.len(), 4, "need four hex bytes"); for (line_bytes, line_type) in &[ - (FLUSH_LINE, PacketLine::Flush), - (DELIMITER_LINE, PacketLine::Delimiter), - (RESPONSE_END_LINE, PacketLine::ResponseEnd), + (FLUSH_LINE, PacketLineRef::Flush), + (DELIMITER_LINE, PacketLineRef::Delimiter), + (RESPONSE_END_LINE, PacketLineRef::ResponseEnd), ] { if four_bytes == *line_bytes { return Ok(PacketLineOrWantedSize::Line(*line_type)); @@ -85,12 +103,12 @@ pub fn hex_prefix(four_bytes: &[u8]) -> Result, Error } /// Obtain a `PacketLine` from `data` after assuring `data` is small enough to fit. -pub fn to_data_line(data: &[u8]) -> Result, Error> { +pub fn to_data_line(data: &[u8]) -> Result, Error> { if data.len() > MAX_LINE_LEN { return Err(Error::DataLengthLimitExceeded(data.len())); } - Ok(PacketLine::Data(data)) + Ok(PacketLineRef::Data(data)) } /// Decode `data` as packet line while reporting whether the data is complete or not using a [`Stream`]. @@ -129,7 +147,7 @@ pub fn streaming(data: &[u8]) -> Result, Error> { /// /// Note that failure also happens if there is not enough data to parse a complete packet line, as opposed to [`streaming()`] decoding /// succeeds in that case, stating how much more bytes are required. -pub fn all_at_once(data: &[u8]) -> Result, Error> { +pub fn all_at_once(data: &[u8]) -> Result, Error> { match streaming(data)? { Stream::Complete { line, .. } => Ok(line), Stream::Incomplete { bytes_needed } => Err(Error::NotEnoughData(bytes_needed)), diff --git a/git-packetline/src/immutable/mod.rs b/git-packetline/src/immutable/mod.rs deleted file mode 100644 index a1339f71310..00000000000 --- a/git-packetline/src/immutable/mod.rs +++ /dev/null @@ -1,139 +0,0 @@ -use bstr::BStr; - -use crate::{Channel, ERR_PREFIX}; - -/// A borrowed packet line as it refers to a slice of data by reference. -#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] -#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] -pub enum PacketLine<'a> { - /// A chunk of raw data. - Data(&'a [u8]), - /// A flush packet. - Flush, - /// A delimiter packet. - Delimiter, - /// The end of the response. - ResponseEnd, -} - -impl<'a> PacketLine<'a> { - /// Return this instance as slice if it's [`Data`][PacketLine::Data]. - pub fn as_slice(&self) -> Option<&[u8]> { - match self { - PacketLine::Data(d) => Some(d), - PacketLine::Flush | PacketLine::Delimiter | PacketLine::ResponseEnd => None, - } - } - /// Return this instance's [`as_slice()`][PacketLine::as_slice()] as [`BStr`]. - pub fn as_bstr(&self) -> Option<&BStr> { - self.as_slice().map(Into::into) - } - /// Interpret this instance's [`as_slice()`][PacketLine::as_slice()] as [`Error`]. - /// - /// This works for any data received in an error [channel][crate::Channel]. - /// - /// Note that this creates an unchecked error using the slice verbatim, which is useful to [serialize it][Error::write_to()]. - /// See [`check_error()`][PacketLine::check_error()] for a version that assures the error information is in the expected format. - pub fn as_error(&self) -> Option> { - self.as_slice().map(Error) - } - /// Check this instance's [`as_slice()`][PacketLine::as_slice()] is a valid [`Error`] and return it. - /// - /// This works for any data received in an error [channel][crate::Channel]. - pub fn check_error(&self) -> Option> { - self.as_slice().and_then(|data| { - if data.len() >= ERR_PREFIX.len() && &data[..ERR_PREFIX.len()] == ERR_PREFIX { - Some(Error(&data[ERR_PREFIX.len()..])) - } else { - None - } - }) - } - /// Return this instance as text, with the trailing newline truncated if present. - pub fn as_text(&self) -> Option> { - self.as_slice().map(Into::into) - } - - /// Interpret the data in this [`slice`][PacketLine::as_slice()] as [`Band`] according to the given `kind` of channel. - /// - /// Note that this is only relevant in a side-band channel. - /// See [`decode_band()`][PacketLine::decode_band()] in case `kind` is unknown. - pub fn as_band(&self, kind: Channel) -> Option> { - self.as_slice().map(|d| match kind { - Channel::Data => Band::Data(d), - Channel::Progress => Band::Progress(d), - Channel::Error => Band::Error(d), - }) - } - - /// Decode the band of this [`slice`][PacketLine::as_slice()], or panic if it is not actually a side-band line. - pub fn decode_band(&self) -> Result, DecodeBandError> { - let d = self.as_slice().ok_or(DecodeBandError::NonDataLine)?; - Ok(match d[0] { - 1 => Band::Data(&d[1..]), - 2 => Band::Progress(&d[1..]), - 3 => Band::Error(&d[1..]), - band => return Err(DecodeBandError::InvalidSideBand(band)), - }) - } -} - -use quick_error::quick_error; -quick_error! { - /// The error used in [`decode_band()`][PacketLine::decode_band()]. - #[derive(Debug)] - #[allow(missing_docs)] - pub enum DecodeBandError { - InvalidSideBand(band: u8) { - display("attempt to decode a non-side channel line or input was malformed: {}", band) - } - NonDataLine { - display("attempt to decode a non-data line into a side-channel band") - } - } -} - -/// A packet line representing an Error in a side-band channel. -#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] -#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] -pub struct Error<'a>(pub &'a [u8]); - -/// A packet line representing text, which may include a trailing newline. -#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] -#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] -pub struct Text<'a>(pub &'a [u8]); - -impl<'a> From<&'a [u8]> for Text<'a> { - fn from(d: &'a [u8]) -> Self { - let d = if d[d.len() - 1] == b'\n' { &d[..d.len() - 1] } else { d }; - Text(d) - } -} - -impl<'a> Text<'a> { - /// Return this instance's data. - pub fn as_slice(&self) -> &[u8] { - self.0 - } - /// Return this instance's data as [`BStr`]. - pub fn as_bstr(&self) -> &BStr { - self.0.into() - } -} - -/// A band in a side-band channel. -#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] -#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] -pub enum Band<'a> { - /// A band carrying data. - Data(&'a [u8]), - /// A band carrying user readable progress information. - Progress(&'a [u8]), - /// A band carrying user readable errors. - Error(&'a [u8]), -} - -#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] -mod async_io; -#[cfg(feature = "blocking-io")] -mod blocking_io; diff --git a/git-packetline/src/lib.rs b/git-packetline/src/lib.rs index 359c55369e4..a5b3f42e865 100644 --- a/git-packetline/src/lib.rs +++ b/git-packetline/src/lib.rs @@ -1,6 +1,6 @@ //! Read and write the git packet line wire format without copying it. //! -//! For reading the packet line format use the [`StreamingPeekableIter`], and for writing the `Writer`. +//! For reading the packet line format use the [`StreamingPeekableIter`], and for writing the [`Writer`]. #![deny(unsafe_code, rust_2018_idioms, missing_docs)] const U16_HEX_BYTES: usize = 4; @@ -23,21 +23,69 @@ pub enum Channel { Error = 3, } -/// -pub mod immutable; -pub use immutable::PacketLine; - +mod line; /// pub mod read; -#[doc(inline)] -pub use read::StreamingPeekableIter; /// #[cfg(any(feature = "async-io", feature = "blocking-io"))] -pub mod write; -#[cfg(any(feature = "async-io", feature = "blocking-io"))] -#[doc(inline)] -pub use write::Writer; +mod write; +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +pub use write::async_io::Writer; +#[cfg(feature = "blocking-io")] +pub use write::blocking_io::Writer; + +/// A borrowed packet line as it refers to a slice of data by reference. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub enum PacketLineRef<'a> { + /// A chunk of raw data. + Data(&'a [u8]), + /// A flush packet. + Flush, + /// A delimiter packet. + Delimiter, + /// The end of the response. + ResponseEnd, +} + +/// A packet line representing an Error in a side-band channel. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub struct ErrorRef<'a>(pub &'a [u8]); + +/// A packet line representing text, which may include a trailing newline. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub struct TextRef<'a>(pub &'a [u8]); + +/// A band in a side-band channel. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub enum BandRef<'a> { + /// A band carrying data. + Data(&'a [u8]), + /// A band carrying user readable progress information. + Progress(&'a [u8]), + /// A band carrying user readable errors. + Error(&'a [u8]), +} + +/// Read pack lines one after another, without consuming more than needed from the underlying +/// [`Read`][std::io::Read]. [`Flush`][PacketLineRef::Flush] lines cause the reader to stop producing lines forever, +/// leaving [`Read`][std::io::Read] at the start of whatever comes next. +/// +/// This implementation tries hard not to allocate at all which leads to quite some added complexity and plenty of extra memory copies. +pub struct StreamingPeekableIter { + read: T, + peek_buf: Vec, + #[cfg(any(feature = "blocking-io", feature = "async-io"))] + buf: Vec, + fail_on_err_lines: bool, + delimiters: &'static [PacketLineRef<'static>], + is_done: bool, + stopped_at: Option>, +} /// Utilities to help decoding packet lines pub mod decode; diff --git a/git-packetline/src/immutable/async_io.rs b/git-packetline/src/line/async_io.rs similarity index 62% rename from git-packetline/src/immutable/async_io.rs rename to git-packetline/src/line/async_io.rs index 99917c326fa..7f20aa030b7 100644 --- a/git-packetline/src/immutable/async_io.rs +++ b/git-packetline/src/line/async_io.rs @@ -2,34 +2,30 @@ use std::io; use futures_io::AsyncWrite; -use crate::{ - encode, - immutable::{Band, Error, Text}, - Channel, PacketLine, -}; +use crate::{encode, BandRef, Channel, ErrorRef, PacketLineRef, TextRef}; -impl<'a> Band<'a> { +impl<'a> BandRef<'a> { /// Serialize this instance to `out`, returning the amount of bytes written. /// /// The data written to `out` can be decoded with [`Borrowed::decode_band()]`. pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result { match self { - Band::Data(d) => encode::band_to_write(Channel::Data, d, out), - Band::Progress(d) => encode::band_to_write(Channel::Progress, d, out), - Band::Error(d) => encode::band_to_write(Channel::Error, d, out), + BandRef::Data(d) => encode::band_to_write(Channel::Data, d, out), + BandRef::Progress(d) => encode::band_to_write(Channel::Progress, d, out), + BandRef::Error(d) => encode::band_to_write(Channel::Error, d, out), } .await } } -impl<'a> Text<'a> { +impl<'a> TextRef<'a> { /// Serialize this instance to `out`, appending a newline if there is none, returning the amount of bytes written. pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result { encode::text_to_write(self.0, out).await } } -impl<'a> Error<'a> { +impl<'a> ErrorRef<'a> { /// Serialize this line as error to `out`. /// /// This includes a marker to allow decoding it outside of a side-band channel, returning the amount of bytes written. @@ -38,14 +34,14 @@ impl<'a> Error<'a> { } } -impl<'a> PacketLine<'a> { +impl<'a> PacketLineRef<'a> { /// Serialize this instance to `out` in git `packetline` format, returning the amount of bytes written to `out`. pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result { match self { - PacketLine::Data(d) => encode::data_to_write(d, out).await, - PacketLine::Flush => encode::flush_to_write(out).await, - PacketLine::Delimiter => encode::delim_to_write(out).await, - PacketLine::ResponseEnd => encode::response_end_to_write(out).await, + PacketLineRef::Data(d) => encode::data_to_write(d, out).await, + PacketLineRef::Flush => encode::flush_to_write(out).await, + PacketLineRef::Delimiter => encode::delim_to_write(out).await, + PacketLineRef::ResponseEnd => encode::response_end_to_write(out).await, } } } diff --git a/git-packetline/src/immutable/blocking_io.rs b/git-packetline/src/line/blocking_io.rs similarity index 60% rename from git-packetline/src/immutable/blocking_io.rs rename to git-packetline/src/line/blocking_io.rs index d569f2fbbc7..9e4ede311da 100644 --- a/git-packetline/src/immutable/blocking_io.rs +++ b/git-packetline/src/line/blocking_io.rs @@ -1,32 +1,28 @@ use std::io; -use crate::{ - encode, - immutable::{Band, Error, Text}, - Channel, PacketLine, -}; +use crate::{encode, BandRef, Channel, ErrorRef, PacketLineRef, TextRef}; -impl<'a> Band<'a> { +impl<'a> BandRef<'a> { /// Serialize this instance to `out`, returning the amount of bytes written. /// /// The data written to `out` can be decoded with [`Borrowed::decode_band()]`. pub fn write_to(&self, out: impl io::Write) -> io::Result { match self { - Band::Data(d) => encode::band_to_write(Channel::Data, d, out), - Band::Progress(d) => encode::band_to_write(Channel::Progress, d, out), - Band::Error(d) => encode::band_to_write(Channel::Error, d, out), + BandRef::Data(d) => encode::band_to_write(Channel::Data, d, out), + BandRef::Progress(d) => encode::band_to_write(Channel::Progress, d, out), + BandRef::Error(d) => encode::band_to_write(Channel::Error, d, out), } } } -impl<'a> Text<'a> { +impl<'a> TextRef<'a> { /// Serialize this instance to `out`, appending a newline if there is none, returning the amount of bytes written. pub fn write_to(&self, out: impl io::Write) -> io::Result { encode::text_to_write(self.0, out) } } -impl<'a> Error<'a> { +impl<'a> ErrorRef<'a> { /// Serialize this line as error to `out`. /// /// This includes a marker to allow decoding it outside of a side-band channel, returning the amount of bytes written. @@ -35,14 +31,14 @@ impl<'a> Error<'a> { } } -impl<'a> PacketLine<'a> { +impl<'a> PacketLineRef<'a> { /// Serialize this instance to `out` in git `packetline` format, returning the amount of bytes written to `out`. pub fn write_to(&self, out: impl io::Write) -> io::Result { match self { - PacketLine::Data(d) => encode::data_to_write(d, out), - PacketLine::Flush => encode::flush_to_write(out), - PacketLine::Delimiter => encode::delim_to_write(out), - PacketLine::ResponseEnd => encode::response_end_to_write(out), + PacketLineRef::Data(d) => encode::data_to_write(d, out), + PacketLineRef::Flush => encode::flush_to_write(out), + PacketLineRef::Delimiter => encode::delim_to_write(out), + PacketLineRef::ResponseEnd => encode::response_end_to_write(out), } } } diff --git a/git-packetline/src/line/mod.rs b/git-packetline/src/line/mod.rs new file mode 100644 index 00000000000..ffe3977d7b7 --- /dev/null +++ b/git-packetline/src/line/mod.rs @@ -0,0 +1,88 @@ +use bstr::BStr; + +use crate::{decode, BandRef, Channel, ErrorRef, PacketLineRef, TextRef, ERR_PREFIX}; + +impl<'a> PacketLineRef<'a> { + /// Return this instance as slice if it's [`Data`][PacketLineRef::Data]. + pub fn as_slice(&self) -> Option<&[u8]> { + match self { + PacketLineRef::Data(d) => Some(d), + PacketLineRef::Flush | PacketLineRef::Delimiter | PacketLineRef::ResponseEnd => None, + } + } + /// Return this instance's [`as_slice()`][PacketLineRef::as_slice()] as [`BStr`]. + pub fn as_bstr(&self) -> Option<&BStr> { + self.as_slice().map(Into::into) + } + /// Interpret this instance's [`as_slice()`][PacketLineRef::as_slice()] as [`ErrorRef`]. + /// + /// This works for any data received in an error [channel][crate::Channel]. + /// + /// Note that this creates an unchecked error using the slice verbatim, which is useful to [serialize it][ErrorRef::write_to()]. + /// See [`check_error()`][PacketLineRef::check_error()] for a version that assures the error information is in the expected format. + pub fn as_error(&self) -> Option> { + self.as_slice().map(ErrorRef) + } + /// Check this instance's [`as_slice()`][PacketLineRef::as_slice()] is a valid [`ErrorRef`] and return it. + /// + /// This works for any data received in an error [channel][crate::Channel]. + pub fn check_error(&self) -> Option> { + self.as_slice().and_then(|data| { + if data.len() >= ERR_PREFIX.len() && &data[..ERR_PREFIX.len()] == ERR_PREFIX { + Some(ErrorRef(&data[ERR_PREFIX.len()..])) + } else { + None + } + }) + } + /// Return this instance as text, with the trailing newline truncated if present. + pub fn as_text(&self) -> Option> { + self.as_slice().map(Into::into) + } + + /// Interpret the data in this [`slice`][PacketLineRef::as_slice()] as [`BandRef`] according to the given `kind` of channel. + /// + /// Note that this is only relevant in a side-band channel. + /// See [`decode_band()`][PacketLineRef::decode_band()] in case `kind` is unknown. + pub fn as_band(&self, kind: Channel) -> Option> { + self.as_slice().map(|d| match kind { + Channel::Data => BandRef::Data(d), + Channel::Progress => BandRef::Progress(d), + Channel::Error => BandRef::Error(d), + }) + } + + /// Decode the band of this [`slice`][PacketLineRef::as_slice()], or panic if it is not actually a side-band line. + pub fn decode_band(&self) -> Result, decode::band::Error> { + let d = self.as_slice().ok_or(decode::band::Error::NonDataLine)?; + Ok(match d[0] { + 1 => BandRef::Data(&d[1..]), + 2 => BandRef::Progress(&d[1..]), + 3 => BandRef::Error(&d[1..]), + band => return Err(decode::band::Error::InvalidSideBand(band)), + }) + } +} + +impl<'a> From<&'a [u8]> for TextRef<'a> { + fn from(d: &'a [u8]) -> Self { + let d = if d[d.len() - 1] == b'\n' { &d[..d.len() - 1] } else { d }; + TextRef(d) + } +} + +impl<'a> TextRef<'a> { + /// Return this instance's data. + pub fn as_slice(&self) -> &[u8] { + self.0 + } + /// Return this instance's data as [`BStr`]. + pub fn as_bstr(&self) -> &BStr { + self.0.into() + } +} + +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +mod async_io; +#[cfg(feature = "blocking-io")] +mod blocking_io; diff --git a/git-packetline/src/read/async_io.rs b/git-packetline/src/read/async_io.rs index 2a80e953a2f..2cfa2040247 100644 --- a/git-packetline/src/read/async_io.rs +++ b/git-packetline/src/read/async_io.rs @@ -7,7 +7,7 @@ use futures_lite::AsyncReadExt; use crate::{ decode, read::{ExhaustiveOutcome, WithSidebands}, - PacketLine, StreamingPeekableIter, MAX_LINE_LEN, U16_HEX_BYTES, + PacketLineRef, StreamingPeekableIter, MAX_LINE_LEN, U16_HEX_BYTES, }; /// Non-IO methods @@ -19,7 +19,7 @@ where async fn read_line_inner<'a>( reader: &mut T, buf: &'a mut Vec, - ) -> io::Result, decode::Error>> { + ) -> io::Result, decode::Error>> { let (hex_bytes, data_bytes) = buf.split_at_mut(4); reader.read_exact(hex_bytes).await?; let num_data_bytes = match decode::hex_prefix(hex_bytes) { @@ -41,7 +41,7 @@ where async fn read_line_inner_exhaustive<'a>( reader: &mut T, buf: &'a mut Vec, - delimiters: &[PacketLine<'static>], + delimiters: &[PacketLineRef<'static>], fail_on_err_lines: bool, buf_resize: bool, ) -> ExhaustiveOutcome<'a> { @@ -89,7 +89,7 @@ where /// * natural EOF /// * ERR packet line encountered if [`fail_on_err_lines()`][StreamingPeekableIter::fail_on_err_lines()] is true. /// * A `delimiter` packet line encountered - pub async fn read_line(&mut self) -> Option, decode::Error>>> { + pub async fn read_line(&mut self) -> Option, decode::Error>>> { if self.is_done { return None; } @@ -118,7 +118,7 @@ where /// Peek the next packet line without consuming it. /// /// Multiple calls to peek will return the same packet line, if there is one. - pub async fn peek_line(&mut self) -> Option, decode::Error>>> { + pub async fn peek_line(&mut self) -> Option, decode::Error>>> { if self.is_done { return None; } diff --git a/git-packetline/src/read/blocking_io.rs b/git-packetline/src/read/blocking_io.rs index f11f17c2a8a..bb577407fc3 100644 --- a/git-packetline/src/read/blocking_io.rs +++ b/git-packetline/src/read/blocking_io.rs @@ -5,7 +5,7 @@ use bstr::ByteSlice; use crate::{ decode, read::{ExhaustiveOutcome, WithSidebands}, - PacketLine, StreamingPeekableIter, MAX_LINE_LEN, U16_HEX_BYTES, + PacketLineRef, StreamingPeekableIter, MAX_LINE_LEN, U16_HEX_BYTES, }; /// Non-IO methods @@ -13,7 +13,10 @@ impl StreamingPeekableIter where T: io::Read, { - fn read_line_inner<'a>(reader: &mut T, buf: &'a mut Vec) -> io::Result, decode::Error>> { + fn read_line_inner<'a>( + reader: &mut T, + buf: &'a mut Vec, + ) -> io::Result, decode::Error>> { let (hex_bytes, data_bytes) = buf.split_at_mut(4); reader.read_exact(hex_bytes)?; let num_data_bytes = match decode::hex_prefix(hex_bytes) { @@ -35,7 +38,7 @@ where fn read_line_inner_exhaustive<'a>( reader: &mut T, buf: &'a mut Vec, - delimiters: &[PacketLine<'static>], + delimiters: &[PacketLineRef<'static>], fail_on_err_lines: bool, buf_resize: bool, ) -> ExhaustiveOutcome<'a> { @@ -83,7 +86,7 @@ where /// * natural EOF /// * ERR packet line encountered if [`fail_on_err_lines()`][StreamingPeekableIter::fail_on_err_lines()] is true. /// * A `delimiter` packet line encountered - pub fn read_line(&mut self) -> Option, decode::Error>>> { + pub fn read_line(&mut self) -> Option, decode::Error>>> { if self.is_done { return None; } @@ -111,7 +114,7 @@ where /// Peek the next packet line without consuming it. /// /// Multiple calls to peek will return the same packet line, if there is one. - pub fn peek_line(&mut self) -> Option, decode::Error>>> { + pub fn peek_line(&mut self) -> Option, decode::Error>>> { if self.is_done { return None; } diff --git a/git-packetline/src/read/mod.rs b/git-packetline/src/read/mod.rs index e87b283b52e..c51d34f6f2b 100644 --- a/git-packetline/src/read/mod.rs +++ b/git-packetline/src/read/mod.rs @@ -1,33 +1,17 @@ #[cfg(any(feature = "blocking-io", feature = "async-io"))] use crate::MAX_LINE_LEN; -use crate::{PacketLine, U16_HEX_BYTES}; +use crate::{PacketLineRef, StreamingPeekableIter, U16_HEX_BYTES}; #[cfg(any(feature = "blocking-io", feature = "async-io"))] type ExhaustiveOutcome<'a> = ( - bool, // is_done - Option>, // stopped_at - Option, crate::decode::Error>>>, // actual method result + bool, // is_done + Option>, // stopped_at + Option, crate::decode::Error>>>, // actual method result ); -/// Read pack lines one after another, without consuming more than needed from the underlying -/// [`Read`][std::io::Read]. [`Flush`][PacketLine::Flush] lines cause the reader to stop producing lines forever, -/// leaving [`Read`][std::io::Read] at the start of whatever comes next. -/// -/// This implementation tries hard not to allocate at all which leads to quite some added complexity and plenty of extra memory copies. -pub struct StreamingPeekableIter { - read: T, - peek_buf: Vec, - #[cfg(any(feature = "blocking-io", feature = "async-io"))] - buf: Vec, - fail_on_err_lines: bool, - delimiters: &'static [PacketLine<'static>], - is_done: bool, - stopped_at: Option>, -} - impl StreamingPeekableIter { /// Return a new instance from `read` which will stop decoding packet lines when receiving one of the given `delimiters`. - pub fn new(read: T, delimiters: &'static [PacketLine<'static>]) -> Self { + pub fn new(read: T, delimiters: &'static [PacketLineRef<'static>]) -> Self { StreamingPeekableIter { read, #[cfg(any(feature = "blocking-io", feature = "async-io"))] @@ -58,7 +42,7 @@ impl StreamingPeekableIter { /// Returns the packet line that stopped the iteration, or /// `None` if the end wasn't reached yet, on EOF, or if [`fail_on_err_lines()`][StreamingPeekableIter::fail_on_err_lines()] was true. - pub fn stopped_at(&self) -> Option> { + pub fn stopped_at(&self) -> Option> { self.stopped_at } @@ -71,7 +55,7 @@ impl StreamingPeekableIter { } /// Similar to [`reset()`][StreamingPeekableIter::reset()] with support to changing the `delimiters`. - pub fn reset_with(&mut self, delimiters: &'static [PacketLine<'static>]) { + pub fn reset_with(&mut self, delimiters: &'static [PacketLineRef<'static>]) { self.delimiters = delimiters; self.is_done = false; self.stopped_at = None; diff --git a/git-packetline/src/read/sidebands/async_io.rs b/git-packetline/src/read/sidebands/async_io.rs index ceb094bec0d..637e94bde2e 100644 --- a/git-packetline/src/read/sidebands/async_io.rs +++ b/git-packetline/src/read/sidebands/async_io.rs @@ -7,13 +7,9 @@ use std::{ use futures_io::{AsyncBufRead, AsyncRead}; use futures_lite::ready; -use crate::{ - decode, - immutable::{Band, Text}, - PacketLine, StreamingPeekableIter, U16_HEX_BYTES, -}; +use crate::{decode, BandRef, PacketLineRef, StreamingPeekableIter, TextRef, U16_HEX_BYTES}; -type ReadLineResult<'a> = Option, decode::Error>>>; +type ReadLineResult<'a> = Option, decode::Error>>>; /// An implementor of [`AsyncBufRead`] yielding packet lines on each call to [`read_line()`][AsyncBufRead::read_line()]. /// It's also possible to hide the underlying packet lines using the [`Read`][AsyncRead] implementation which is useful /// if they represent binary data, like the one of a pack file. @@ -116,14 +112,14 @@ where } /// Forwards to the parent [StreamingPeekableIter::reset_with()] - pub fn reset_with(&mut self, delimiters: &'static [PacketLine<'static>]) { + pub fn reset_with(&mut self, delimiters: &'static [PacketLineRef<'static>]) { if let State::Idle { ref mut parent } = self.state { parent.as_mut().unwrap().reset_with(delimiters) } } /// Forwards to the parent [StreamingPeekableIter::stopped_at()] - pub fn stopped_at(&self) -> Option> { + pub fn stopped_at(&self) -> Option> { match self.state { State::Idle { ref parent } => parent.as_ref().unwrap().stopped_at, _ => None, @@ -140,7 +136,7 @@ where pub async fn peek_data_line(&mut self) -> Option>> { match self.state { State::Idle { ref mut parent } => match parent.as_mut().unwrap().peek_line().await { - Some(Ok(Ok(crate::PacketLine::Data(line)))) => Some(Ok(Ok(line))), + Some(Ok(Ok(crate::PacketLineRef::Data(line)))) => Some(Ok(Ok(line))), Some(Ok(Err(err))) => Some(Ok(Err(err))), Some(Err(err)) => Some(Err(err)), _ => None, @@ -238,13 +234,13 @@ where .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; const ENCODED_BAND: usize = 1; match band { - Band::Data(d) => break (U16_HEX_BYTES + ENCODED_BAND, d.len()), - Band::Progress(d) => { - let text = Text::from(d).0; + BandRef::Data(d) => break (U16_HEX_BYTES + ENCODED_BAND, d.len()), + BandRef::Progress(d) => { + let text = TextRef::from(d).0; handle_progress(false, text); } - Band::Error(d) => { - let text = Text::from(d).0; + BandRef::Error(d) => { + let text = TextRef::from(d).0; handle_progress(true, text); } }; diff --git a/git-packetline/src/read/sidebands/blocking_io.rs b/git-packetline/src/read/sidebands/blocking_io.rs index a8a5b7b5d0f..3fc8ea865b0 100644 --- a/git-packetline/src/read/sidebands/blocking_io.rs +++ b/git-packetline/src/read/sidebands/blocking_io.rs @@ -1,9 +1,6 @@ use std::{io, io::BufRead}; -use crate::{ - immutable::{Band, Text}, - PacketLine, StreamingPeekableIter, U16_HEX_BYTES, -}; +use crate::{BandRef, PacketLineRef, StreamingPeekableIter, TextRef, U16_HEX_BYTES}; /// An implementor of [`BufRead`][io::BufRead] yielding packet lines on each call to [`read_line()`][io::BufRead::read_line()]. /// It's also possible to hide the underlying packet lines using the [`Read`][io::Read] implementation which is useful @@ -71,12 +68,12 @@ where } /// Forwards to the parent [StreamingPeekableIter::reset_with()] - pub fn reset_with(&mut self, delimiters: &'static [PacketLine<'static>]) { + pub fn reset_with(&mut self, delimiters: &'static [PacketLineRef<'static>]) { self.parent.reset_with(delimiters) } /// Forwards to the parent [StreamingPeekableIter::stopped_at()] - pub fn stopped_at(&self) -> Option> { + pub fn stopped_at(&self) -> Option> { self.parent.stopped_at } @@ -89,7 +86,7 @@ where /// next on a call to [`read_line()`][io::BufRead::read_line()]. pub fn peek_data_line(&mut self) -> Option>> { match self.parent.peek_line() { - Some(Ok(Ok(crate::PacketLine::Data(line)))) => Some(Ok(Ok(line))), + Some(Ok(Ok(crate::PacketLineRef::Data(line)))) => Some(Ok(Ok(line))), Some(Ok(Err(err))) => Some(Ok(Err(err))), Some(Err(err)) => Some(Err(err)), _ => None, @@ -116,13 +113,13 @@ where .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; const ENCODED_BAND: usize = 1; match band { - Band::Data(d) => break (U16_HEX_BYTES + ENCODED_BAND, d.len()), - Band::Progress(d) => { - let text = Text::from(d).0; + BandRef::Data(d) => break (U16_HEX_BYTES + ENCODED_BAND, d.len()), + BandRef::Progress(d) => { + let text = TextRef::from(d).0; handle_progress(false, text); } - Band::Error(d) => { - let text = Text::from(d).0; + BandRef::Error(d) => { + let text = TextRef::from(d).0; handle_progress(true, text); } }; diff --git a/git-packetline/src/write/mod.rs b/git-packetline/src/write/mod.rs index e96caa74e0b..f40a6bdaea3 100644 --- a/git-packetline/src/write/mod.rs +++ b/git-packetline/src/write/mod.rs @@ -1,12 +1,10 @@ +use crate::Writer; + #[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] -mod async_io; -#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] -pub use async_io::Writer; +pub(crate) mod async_io; #[cfg(feature = "blocking-io")] -mod blocking_io; -#[cfg(feature = "blocking-io")] -pub use blocking_io::Writer; +pub(crate) mod blocking_io; /// Common methods impl Writer { diff --git a/git-packetline/tests/decode/mod.rs b/git-packetline/tests/decode/mod.rs index 59f09fe5901..f2ca860ff3d 100644 --- a/git-packetline/tests/decode/mod.rs +++ b/git-packetline/tests/decode/mod.rs @@ -1,8 +1,7 @@ mod streaming { use git_packetline::{ decode::{self, streaming, Stream}, - immutable::Error, - PacketLine, + ErrorRef, PacketLineRef, }; use crate::assert_err_display; @@ -10,7 +9,7 @@ mod streaming { fn assert_complete( res: Result, expected_consumed: usize, - expected_value: PacketLine, + expected_value: PacketLineRef, ) -> crate::Result { match res? { Stream::Complete { line, bytes_consumed } => { @@ -24,7 +23,7 @@ mod streaming { mod round_trip { use bstr::ByteSlice; - use git_packetline::{decode, decode::streaming, Channel, PacketLine}; + use git_packetline::{decode, decode::streaming, Channel, PacketLineRef}; use crate::decode::streaming::assert_complete; @@ -45,10 +44,10 @@ mod streaming { #[maybe_async::test(feature = "blocking-io", async(feature = "async-io", async_std::test))] async fn all_kinds_of_packetlines() -> crate::Result { for (line, bytes) in &[ - (PacketLine::ResponseEnd, 4), - (PacketLine::Delimiter, 4), - (PacketLine::Flush, 4), - (PacketLine::Data(b"hello there"), 15), + (PacketLineRef::ResponseEnd, 4), + (PacketLineRef::Delimiter, 4), + (PacketLineRef::Flush, 4), + (PacketLineRef::Data(b"hello there"), 15), ] { let mut out = Vec::new(); line.write_to(&mut out).await?; @@ -60,7 +59,7 @@ mod streaming { #[maybe_async::test(feature = "blocking-io", async(feature = "async-io", async_std::test))] async fn error_line() -> crate::Result { let mut out = Vec::new(); - PacketLine::Data(b"the error") + PacketLineRef::Data(b"the error") .as_error() .expect("data line") .write_to(&mut out) @@ -74,7 +73,7 @@ mod streaming { async fn side_bands() -> crate::Result { for channel in &[Channel::Data, Channel::Error, Channel::Progress] { let mut out = Vec::new(); - let band = PacketLine::Data(b"band data") + let band = PacketLineRef::Data(b"band data") .as_band(*channel) .expect("data is valid for band"); band.write_to(&mut out).await?; @@ -87,17 +86,17 @@ mod streaming { #[test] fn flush() -> crate::Result { - assert_complete(streaming(b"0000someotherstuff"), 4, PacketLine::Flush) + assert_complete(streaming(b"0000someotherstuff"), 4, PacketLineRef::Flush) } #[test] fn trailing_line_feeds_are_not_removed_automatically() -> crate::Result { - assert_complete(streaming(b"0006a\n"), 6, PacketLine::Data(b"a\n")) + assert_complete(streaming(b"0006a\n"), 6, PacketLineRef::Data(b"a\n")) } #[test] fn ignore_extra_bytes() -> crate::Result { - assert_complete(streaming(b"0006a\nhello"), 6, PacketLine::Data(b"a\n")) + assert_complete(streaming(b"0006a\nhello"), 6, PacketLineRef::Data(b"a\n")) } #[test] @@ -110,7 +109,7 @@ mod streaming { #[test] fn error_on_error_line() -> crate::Result { - let line = PacketLine::Data(b"ERR the error"); + let line = PacketLineRef::Data(b"ERR the error"); assert_complete( streaming(b"0011ERR the error-and just ignored because not part of the size"), 17, @@ -118,7 +117,7 @@ mod streaming { )?; assert_eq!( line.check_error().expect("error to be parsed here"), - Error(b"the error") + ErrorRef(b"the error") ); Ok(()) } diff --git a/git-packetline/tests/read/mod.rs b/git-packetline/tests/read/mod.rs index 13f3b53aed0..ca3d51f956b 100644 --- a/git-packetline/tests/read/mod.rs +++ b/git-packetline/tests/read/mod.rs @@ -4,7 +4,7 @@ pub mod streaming_peek_iter { use std::{io, path::PathBuf}; use bstr::ByteSlice; - use git_packetline::PacketLine; + use git_packetline::PacketLineRef; fn fixture_path(path: &str) -> PathBuf { PathBuf::from("tests/fixtures").join(path) @@ -14,22 +14,22 @@ pub mod streaming_peek_iter { std::fs::read(fixture_path(path)).expect("readable fixture") } - fn first_line() -> PacketLine<'static> { - PacketLine::Data(b"7814e8a05a59c0cf5fb186661d1551c75d1299b5 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed symref=HEAD:refs/heads/master object-format=sha1 agent=git/2.28.0\n") + fn first_line() -> PacketLineRef<'static> { + PacketLineRef::Data(b"7814e8a05a59c0cf5fb186661d1551c75d1299b5 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed symref=HEAD:refs/heads/master object-format=sha1 agent=git/2.28.0\n") } #[maybe_async::test(feature = "blocking-io", async(feature = "async-io", async_std::test))] async fn peek_follows_read_line_delimiter_logic() -> crate::Result { - let mut rd = git_packetline::StreamingPeekableIter::new(&b"0005a00000005b"[..], &[PacketLine::Flush]); + let mut rd = git_packetline::StreamingPeekableIter::new(&b"0005a00000005b"[..], &[PacketLineRef::Flush]); let res = rd.peek_line().await; - assert_eq!(res.expect("line")??, PacketLine::Data(b"a")); + assert_eq!(res.expect("line")??, PacketLineRef::Data(b"a")); rd.read_line().await; let res = rd.peek_line().await; assert!(res.is_none(), "we hit the delmiter, and thus are EOF"); assert_eq!( rd.stopped_at(), - Some(PacketLine::Flush), + Some(PacketLineRef::Flush), "Stopped tracking is done even when peeking" ); let res = rd.peek_line().await; @@ -38,7 +38,7 @@ pub mod streaming_peek_iter { let res = rd.peek_line().await; assert_eq!( res.expect("line")??, - PacketLine::Data(b"b"), + PacketLineRef::Data(b"b"), "after resetting, we get past the delimiter" ); Ok(()) @@ -46,10 +46,10 @@ pub mod streaming_peek_iter { #[maybe_async::test(feature = "blocking-io", async(feature = "async-io", async_std::test))] async fn peek_follows_read_line_err_logic() -> crate::Result { - let mut rd = git_packetline::StreamingPeekableIter::new(&b"0005a0009ERR e0000"[..], &[PacketLine::Flush]); + let mut rd = git_packetline::StreamingPeekableIter::new(&b"0005a0009ERR e0000"[..], &[PacketLineRef::Flush]); rd.fail_on_err_lines(true); let res = rd.peek_line().await; - assert_eq!(res.expect("line")??, PacketLine::Data(b"a")); + assert_eq!(res.expect("line")??, PacketLineRef::Data(b"a")); rd.read_line().await; let res = rd.peek_line().await; assert_eq!( @@ -65,7 +65,7 @@ pub mod streaming_peek_iter { assert!(res.is_none(), "it should stop due to the delimiter"); assert_eq!( rd.stopped_at(), - Some(PacketLine::Flush), + Some(PacketLineRef::Flush), "Stopped tracking is done even when peeking" ); Ok(()) @@ -73,14 +73,14 @@ pub mod streaming_peek_iter { #[maybe_async::test(feature = "blocking-io", async(feature = "async-io", async_std::test))] async fn peek_non_data() -> crate::Result { - let mut rd = git_packetline::StreamingPeekableIter::new(&b"000000010002"[..], &[PacketLine::ResponseEnd]); + let mut rd = git_packetline::StreamingPeekableIter::new(&b"000000010002"[..], &[PacketLineRef::ResponseEnd]); let res = rd.read_line().await; - assert_eq!(res.expect("line")??, PacketLine::Flush); + assert_eq!(res.expect("line")??, PacketLineRef::Flush); let res = rd.read_line().await; - assert_eq!(res.expect("line")??, PacketLine::Delimiter); - rd.reset_with(&[PacketLine::Flush]); + assert_eq!(res.expect("line")??, PacketLineRef::Delimiter); + rd.reset_with(&[PacketLineRef::Flush]); let res = rd.read_line().await; - assert_eq!(res.expect("line")??, PacketLine::ResponseEnd); + assert_eq!(res.expect("line")??, PacketLineRef::ResponseEnd); for _ in 0..2 { let res = rd.peek_line().await; assert_eq!( @@ -102,7 +102,7 @@ pub mod streaming_peek_iter { let input = b"00010009ERR e0002"; let mut rd = git_packetline::StreamingPeekableIter::new(&input[..], &[]); let res = rd.read_line().await; - assert_eq!(res.expect("line")??, PacketLine::Delimiter); + assert_eq!(res.expect("line")??, PacketLineRef::Delimiter); let res = rd.read_line().await; assert_eq!( res.expect("line")??.as_bstr(), @@ -113,7 +113,7 @@ pub mod streaming_peek_iter { let mut rd = git_packetline::StreamingPeekableIter::new(&input[..], &[]); rd.fail_on_err_lines(true); let res = rd.read_line().await; - assert_eq!(res.expect("line")??, PacketLine::Delimiter); + assert_eq!(res.expect("line")??, PacketLineRef::Delimiter); let res = rd.read_line().await; assert_eq!( res.expect("line").unwrap_err().to_string(), @@ -125,7 +125,7 @@ pub mod streaming_peek_iter { rd.replace(input); let res = rd.read_line().await; - assert_eq!(res.expect("line")??, PacketLine::Delimiter); + assert_eq!(res.expect("line")??, PacketLineRef::Delimiter); let res = rd.read_line().await; assert_eq!( res.expect("line")??.as_bstr(), @@ -138,7 +138,7 @@ pub mod streaming_peek_iter { #[maybe_async::test(feature = "blocking-io", async(feature = "async-io", async_std::test))] async fn peek() -> crate::Result { let bytes = fixture_bytes("v1/fetch/01-many-refs.response"); - let mut rd = git_packetline::StreamingPeekableIter::new(&bytes[..], &[PacketLine::Flush]); + let mut rd = git_packetline::StreamingPeekableIter::new(&bytes[..], &[PacketLineRef::Flush]); let res = rd.peek_line().await; assert_eq!(res.expect("line")??, first_line(), "peek returns first line"); let res = rd.peek_line().await; @@ -165,7 +165,7 @@ pub mod streaming_peek_iter { assert_eq!(res, 1559); assert_eq!( rd.stopped_at(), - Some(PacketLine::Flush), + Some(PacketLineRef::Flush), "A flush packet line ends every pack file" ); Ok(()) @@ -175,7 +175,7 @@ pub mod streaming_peek_iter { async fn read_from_file_and_reader_advancement() -> crate::Result { let mut bytes = fixture_bytes("v1/fetch/01-many-refs.response"); bytes.extend(fixture_bytes("v1/fetch/01-many-refs.response").into_iter()); - let mut rd = git_packetline::StreamingPeekableIter::new(&bytes[..], &[PacketLine::Flush]); + let mut rd = git_packetline::StreamingPeekableIter::new(&bytes[..], &[PacketLineRef::Flush]); let res = rd.read_line().await; assert_eq!(res.expect("line")??, first_line()); let res = exhaust(&mut rd).await; diff --git a/git-packetline/tests/read/sideband.rs b/git-packetline/tests/read/sideband.rs index 78052dcced5..084b1d3a551 100644 --- a/git-packetline/tests/read/sideband.rs +++ b/git-packetline/tests/read/sideband.rs @@ -5,7 +5,7 @@ use bstr::{BString, ByteSlice}; #[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] use futures_lite::io::AsyncReadExt; use git_odb::pack; -use git_packetline::PacketLine; +use git_packetline::PacketLineRef; use crate::read::streaming_peek_iter::fixture_bytes; @@ -38,7 +38,7 @@ mod util { #[maybe_async::test(feature = "blocking-io", async(feature = "async-io", async_std::test))] async fn read_pack_with_progress_extraction() -> crate::Result { let buf = fixture_bytes("v1/01-clone.combined-output"); - let mut rd = git_packetline::StreamingPeekableIter::new(&buf[..], &[PacketLine::Flush]); + let mut rd = git_packetline::StreamingPeekableIter::new(&buf[..], &[PacketLineRef::Flush]); // Read without sideband decoding let mut out = Vec::new(); @@ -99,7 +99,7 @@ async fn read_pack_with_progress_extraction() -> crate::Result { async fn read_line_trait_method_reads_one_packet_line_at_a_time() -> crate::Result { let buf = fixture_bytes("v1/01-clone.combined-output-no-binary"); - let mut rd = git_packetline::StreamingPeekableIter::new(&buf[..], &[PacketLine::Flush]); + let mut rd = git_packetline::StreamingPeekableIter::new(&buf[..], &[PacketLineRef::Flush]); let mut out = String::new(); let mut r = rd.as_read(); @@ -116,7 +116,7 @@ async fn read_line_trait_method_reads_one_packet_line_at_a_time() -> crate::Resu assert_eq!(out, "", "…which can't be overcome unless the reader is reset"); assert_eq!( r.stopped_at(), - Some(PacketLine::Flush), + Some(PacketLineRef::Flush), "it knows what stopped the reader" ); @@ -168,7 +168,7 @@ async fn peek_past_an_actual_eof_is_an_error() -> crate::Result { #[maybe_async::test(feature = "blocking-io", async(feature = "async-io", async_std::test))] async fn peek_past_a_delimiter_is_no_error() -> crate::Result { let input = b"0009hello0000"; - let mut rd = git_packetline::StreamingPeekableIter::new(&input[..], &[PacketLine::Flush]); + let mut rd = git_packetline::StreamingPeekableIter::new(&input[..], &[PacketLineRef::Flush]); let mut reader = rd.as_read(); let res = reader.peek_data_line().await; assert_eq!(res.expect("one line")??, b"hello"); @@ -204,7 +204,7 @@ async fn handling_of_err_lines() { 0, "it stops reading after an error despite there being more to read" ); - reader.reset_with(&[PacketLine::Flush]); + reader.reset_with(&[PacketLineRef::Flush]); let res = reader.read(buf.as_mut()).await; assert_eq!( res.unwrap_err().to_string(), diff --git a/git-protocol/Cargo.toml b/git-protocol/Cargo.toml index 7445fe069aa..eab98565a80 100644 --- a/git-protocol/Cargo.toml +++ b/git-protocol/Cargo.toml @@ -45,5 +45,5 @@ maybe-async = "0.2.6" [dev-dependencies] async-std = { version = "1.9.0", features = ["attributes"] } -git-packetline = { path = "../git-packetline" ,version ="0.9.0"} +git-packetline = { path = "../git-packetline" ,version ="^0.10.0"} git-testtools = { path = "../tests/tools" } diff --git a/git-protocol/tests/fetch/response.rs b/git-protocol/tests/fetch/response.rs index 82d97be966c..5099ac4afbf 100644 --- a/git-protocol/tests/fetch/response.rs +++ b/git-protocol/tests/fetch/response.rs @@ -3,7 +3,7 @@ use crate::fetch::Cursor; fn mock_reader(path: &str) -> git_packetline::StreamingPeekableIter { use crate::fixture_bytes; let buf = fixture_bytes(path); - git_packetline::StreamingPeekableIter::new(Cursor::new(buf), &[git_packetline::PacketLine::Flush]) + git_packetline::StreamingPeekableIter::new(Cursor::new(buf), &[git_packetline::PacketLineRef::Flush]) } fn id(hex: &str) -> git_hash::ObjectId { diff --git a/git-repository/Cargo.toml b/git-repository/Cargo.toml index 473f1102498..f8b08a03849 100644 --- a/git-repository/Cargo.toml +++ b/git-repository/Cargo.toml @@ -46,9 +46,9 @@ git-actor = { version ="^0.5.0", path = "../git-actor" } git-pack = { version ="^0.9.0", path = "../git-pack" } git-url = { version = "0.3.0", path = "../git-url", optional = true } -git-traverse = { version ="0.7.0", path = "../git-traverse", optional = true } +git-traverse = { version ="^0.8.0", path = "../git-traverse", optional = true } git-protocol = { version ="^0.9.0", path = "../git-protocol", optional = true } -git-diff = { version ="0.8.0", path = "../git-diff", optional = true } +git-diff = { version ="^0.9.0", path = "../git-diff", optional = true } git-features = { version = "^0.16.0", path = "../git-features", features = ["progress"] } signal-hook = { version = "0.3.9", default-features = false } diff --git a/git-repository/src/ext/object_id.rs b/git-repository/src/ext/object_id.rs index 0e86ae13f01..c7e85e792ab 100644 --- a/git-repository/src/ext/object_id.rs +++ b/git-repository/src/ext/object_id.rs @@ -1,6 +1,5 @@ #![allow(missing_docs)] use git_hash::ObjectId; -use git_object::CommitRefIter; #[cfg(feature = "git-traverse")] use git_traverse::commit::ancestors::{Ancestors, State}; @@ -12,7 +11,7 @@ pub trait ObjectIdExt: Sealed { #[cfg(feature = "git-traverse")] fn ancestors_iter(self, find: Find) -> Ancestors bool, State> where - Find: for<'a> FnMut(&git_hash::oid, &'a mut Vec) -> Option>; + Find: for<'a> FnMut(&git_hash::oid, &'a mut Vec) -> Option>; fn attach(self, access: &A) -> easy::Oid<'_, A>; } @@ -23,7 +22,7 @@ impl ObjectIdExt for ObjectId { #[cfg(feature = "git-traverse")] fn ancestors_iter(self, find: Find) -> Ancestors bool, State> where - Find: for<'a> FnMut(&git_hash::oid, &'a mut Vec) -> Option>, + Find: for<'a> FnMut(&git_hash::oid, &'a mut Vec) -> Option>, { Ancestors::new(Some(self), State::default(), find) } diff --git a/git-transport/Cargo.toml b/git-transport/Cargo.toml index 33d5788cd92..4f8e8f26a1a 100644 --- a/git-transport/Cargo.toml +++ b/git-transport/Cargo.toml @@ -38,7 +38,7 @@ required-features = ["async-client"] [dependencies] git-features = { version = "^0.16.0", path = "../git-features" } git-url = { version = "^0.3.0", path = "../git-url" } -git-packetline = { version ="0.9.0", path = "../git-packetline" } +git-packetline = { version ="^0.10.0", path = "../git-packetline" } serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]} quick-error = "2.0.0" diff --git a/git-transport/src/client/async_io/bufread_ext.rs b/git-transport/src/client/async_io/bufread_ext.rs index 196019ac419..b681be8a564 100644 --- a/git-transport/src/client/async_io/bufread_ext.rs +++ b/git-transport/src/client/async_io/bufread_ext.rs @@ -69,18 +69,19 @@ impl<'a, T: AsyncRead + Unpin> ExtendedBufRead for git_packetline::read::WithSid } fn reset(&mut self, version: Protocol) { match version { - Protocol::V1 => self.reset_with(&[git_packetline::PacketLine::Flush]), - Protocol::V2 => { - self.reset_with(&[git_packetline::PacketLine::Delimiter, git_packetline::PacketLine::Flush]) - } + Protocol::V1 => self.reset_with(&[git_packetline::PacketLineRef::Flush]), + Protocol::V2 => self.reset_with(&[ + git_packetline::PacketLineRef::Delimiter, + git_packetline::PacketLineRef::Flush, + ]), } } fn stopped_at(&self) -> Option { self.stopped_at().map(|l| match l { - git_packetline::PacketLine::Flush => MessageKind::Flush, - git_packetline::PacketLine::Delimiter => MessageKind::Delimiter, - git_packetline::PacketLine::ResponseEnd => MessageKind::ResponseEnd, - git_packetline::PacketLine::Data(_) => unreachable!("data cannot be a delimiter"), + git_packetline::PacketLineRef::Flush => MessageKind::Flush, + git_packetline::PacketLineRef::Delimiter => MessageKind::Delimiter, + git_packetline::PacketLineRef::ResponseEnd => MessageKind::ResponseEnd, + git_packetline::PacketLineRef::Data(_) => unreachable!("data cannot be a delimiter"), }) } } diff --git a/git-transport/src/client/async_io/request.rs b/git-transport/src/client/async_io/request.rs index aecc5bb78a4..9328bed7452 100644 --- a/git-transport/src/client/async_io/request.rs +++ b/git-transport/src/client/async_io/request.rs @@ -61,25 +61,21 @@ impl<'a> RequestWriter<'a> { pub async fn write_message(&mut self, message: MessageKind) -> io::Result<()> { match message { MessageKind::Flush => { - git_packetline::PacketLine::Flush + git_packetline::PacketLineRef::Flush .write_to(self.writer.inner_mut()) .await } MessageKind::Delimiter => { - git_packetline::PacketLine::Delimiter + git_packetline::PacketLineRef::Delimiter .write_to(self.writer.inner_mut()) .await } MessageKind::ResponseEnd => { - git_packetline::PacketLine::ResponseEnd - .write_to(self.writer.inner_mut()) - .await - } - MessageKind::Text(t) => { - git_packetline::immutable::Text::from(t) + git_packetline::PacketLineRef::ResponseEnd .write_to(self.writer.inner_mut()) .await } + MessageKind::Text(t) => git_packetline::TextRef::from(t).write_to(self.writer.inner_mut()).await, } .map(|_| ()) } diff --git a/git-transport/src/client/blocking_io/bufread_ext.rs b/git-transport/src/client/blocking_io/bufread_ext.rs index 2a02c337e7d..e894d51374f 100644 --- a/git-transport/src/client/blocking_io/bufread_ext.rs +++ b/git-transport/src/client/blocking_io/bufread_ext.rs @@ -60,18 +60,19 @@ impl<'a, T: io::Read> ExtendedBufRead for git_packetline::read::WithSidebands<'a } fn reset(&mut self, version: Protocol) { match version { - Protocol::V1 => self.reset_with(&[git_packetline::PacketLine::Flush]), - Protocol::V2 => { - self.reset_with(&[git_packetline::PacketLine::Delimiter, git_packetline::PacketLine::Flush]) - } + Protocol::V1 => self.reset_with(&[git_packetline::PacketLineRef::Flush]), + Protocol::V2 => self.reset_with(&[ + git_packetline::PacketLineRef::Delimiter, + git_packetline::PacketLineRef::Flush, + ]), } } fn stopped_at(&self) -> Option { self.stopped_at().map(|l| match l { - git_packetline::PacketLine::Flush => MessageKind::Flush, - git_packetline::PacketLine::Delimiter => MessageKind::Delimiter, - git_packetline::PacketLine::ResponseEnd => MessageKind::ResponseEnd, - git_packetline::PacketLine::Data(_) => unreachable!("data cannot be a delimiter"), + git_packetline::PacketLineRef::Flush => MessageKind::Flush, + git_packetline::PacketLineRef::Delimiter => MessageKind::Delimiter, + git_packetline::PacketLineRef::ResponseEnd => MessageKind::ResponseEnd, + git_packetline::PacketLineRef::Data(_) => unreachable!("data cannot be a delimiter"), }) } } diff --git a/git-transport/src/client/blocking_io/http/mod.rs b/git-transport/src/client/blocking_io/http/mod.rs index 71e0de46851..7f30032258a 100644 --- a/git-transport/src/client/blocking_io/http/mod.rs +++ b/git-transport/src/client/blocking_io/http/mod.rs @@ -4,7 +4,7 @@ use std::{ io::{self, BufRead, Read}, }; -use git_packetline::PacketLine; +use git_packetline::PacketLineRef; pub use traits::{Error, GetResponse, Http, PostResponse}; use crate::{ @@ -199,7 +199,7 @@ impl client::Transport for Transport { let line_reader = self .line_provider - .get_or_insert_with(|| git_packetline::StreamingPeekableIter::new(body, &[PacketLine::Flush])); + .get_or_insert_with(|| git_packetline::StreamingPeekableIter::new(body, &[PacketLineRef::Flush])); let mut announced_service = String::new(); line_reader.as_read().read_to_string(&mut announced_service)?; diff --git a/git-transport/src/client/blocking_io/request.rs b/git-transport/src/client/blocking_io/request.rs index 890aaba2632..75339e789b0 100644 --- a/git-transport/src/client/blocking_io/request.rs +++ b/git-transport/src/client/blocking_io/request.rs @@ -47,10 +47,10 @@ impl<'a> RequestWriter<'a> { /// Write the given message as packet line. pub fn write_message(&mut self, message: MessageKind) -> io::Result<()> { match message { - MessageKind::Flush => git_packetline::PacketLine::Flush.write_to(self.writer.inner_mut()), - MessageKind::Delimiter => git_packetline::PacketLine::Delimiter.write_to(self.writer.inner_mut()), - MessageKind::ResponseEnd => git_packetline::PacketLine::ResponseEnd.write_to(self.writer.inner_mut()), - MessageKind::Text(t) => git_packetline::immutable::Text::from(t).write_to(self.writer.inner_mut()), + MessageKind::Flush => git_packetline::PacketLineRef::Flush.write_to(self.writer.inner_mut()), + MessageKind::Delimiter => git_packetline::PacketLineRef::Delimiter.write_to(self.writer.inner_mut()), + MessageKind::ResponseEnd => git_packetline::PacketLineRef::ResponseEnd.write_to(self.writer.inner_mut()), + MessageKind::Text(t) => git_packetline::TextRef::from(t).write_to(self.writer.inner_mut()), } .map(|_| ()) } diff --git a/git-transport/src/client/capabilities.rs b/git-transport/src/client/capabilities.rs index 23e555dfce8..dc50a3f09c4 100644 --- a/git-transport/src/client/capabilities.rs +++ b/git-transport/src/client/capabilities.rs @@ -143,8 +143,8 @@ impl Capabilities { #[cfg(any(feature = "blocking-client", feature = "async-client"))] impl Capabilities { fn extract_protocol<'a>( - capabilities_or_version: &'a git_packetline::PacketLine<'_>, - ) -> Result<(git_packetline::immutable::Text<'a>, Protocol), client::Error> { + capabilities_or_version: &'a git_packetline::PacketLineRef<'_>, + ) -> Result<(git_packetline::TextRef<'a>, Protocol), client::Error> { let first_line = capabilities_or_version .as_text() .ok_or(client::Error::ExpectedLine("text"))?; diff --git a/git-transport/src/client/git/async_io.rs b/git-transport/src/client/git/async_io.rs index d6f5b8a6f32..0771b02c01f 100644 --- a/git-transport/src/client/git/async_io.rs +++ b/git-transport/src/client/git/async_io.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use bstr::BString; use futures_io::{AsyncRead, AsyncWrite}; use futures_lite::AsyncWriteExt; -use git_packetline::PacketLine; +use git_packetline::PacketLineRef; use crate::{ client::{self, capabilities, git, Capabilities, SetServiceResponse}, @@ -114,7 +114,7 @@ where ) -> Self { git::Connection { writer: write, - line_provider: git_packetline::StreamingPeekableIter::new(read, &[PacketLine::Flush]), + line_provider: git_packetline::StreamingPeekableIter::new(read, &[PacketLineRef::Flush]), path: repository_path.into(), virtual_host: virtual_host.map(|(h, p)| (h.into(), p)), desired_version, diff --git a/git-transport/src/client/git/blocking_io.rs b/git-transport/src/client/git/blocking_io.rs index 9a3632cd4ed..544a2e618b9 100644 --- a/git-transport/src/client/git/blocking_io.rs +++ b/git-transport/src/client/git/blocking_io.rs @@ -1,7 +1,7 @@ use std::{io, io::Write}; use bstr::BString; -use git_packetline::PacketLine; +use git_packetline::PacketLineRef; use crate::{ client::{self, capabilities, git, Capabilities, SetServiceResponse}, @@ -113,7 +113,7 @@ where ) -> Self { git::Connection { writer: write, - line_provider: git_packetline::StreamingPeekableIter::new(read, &[PacketLine::Flush]), + line_provider: git_packetline::StreamingPeekableIter::new(read, &[PacketLineRef::Flush]), path: repository_path.into(), virtual_host: virtual_host.map(|(h, p)| (h.into(), p)), desired_version, diff --git a/git-traverse/Cargo.toml b/git-traverse/Cargo.toml index b14b15590da..71f06abdb0e 100644 --- a/git-traverse/Cargo.toml +++ b/git-traverse/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "git-traverse" -version = "0.7.2" +version = "0.8.0" repository = "https://github.com/Byron/gitoxide" license = "MIT/Apache-2.0" description = "A WIP crate of the gitoxide project"