Sebastian Thielthanks clippy8e96ed31 year agoHistory
106 lines · 3.7 KB · Rust
RawDownload
use bstr::ByteSlice;use winnow::{    combinator::{opt, preceded, terminated},    prelude::*,    token::take_till,}; /// The information usually found in remote progress messages as sent by a git server during/// fetch, clone and push operations.#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]pub struct RemoteProgress<'a> {    #[cfg_attr(feature = "serde", serde(borrow))]    /// The name of the action, like "clone".    pub action: &'a bstr::BStr,    /// The percentage to indicate progress, between 0 and 100.    pub percent: Option<u32>,    /// The amount of items already processed.    pub step: Option<usize>,    /// The maximum expected amount of items. `step` / `max` * 100 = `percent`.    pub max: Option<usize>,} impl RemoteProgress<'_> {    /// Parse the progress from a typical git progress `line` as sent by the remote.    pub fn from_bytes(mut line: &[u8]) -> Option<RemoteProgress<'_>> {        parse_progress(&mut line).ok().and_then(|r| {            if r.percent.is_none() && r.step.is_none() && r.max.is_none() {                None            } else {                Some(r)            }        })    }     /// Parse `text`, which is interpreted as error if `is_error` is true, as [`RemoteProgress`] and call the respective    /// methods on the given `progress` instance.    pub fn translate_to_progress(is_error: bool, text: &[u8], progress: &mut impl gix_features::progress::Progress) {        fn progress_name(current: Option<String>, action: &[u8]) -> String {            match current {                Some(current) => format!(                    "{}: {}",                    current.split_once(':').map_or(&*current, |x| x.0),                    action.as_bstr()                ),                None => action.as_bstr().to_string(),            }        }        if is_error {            // ignore keep-alive packages sent with 'sideband-all'            if !text.is_empty() {                progress.fail(progress_name(None, text));            }        } else {            match RemoteProgress::from_bytes(text) {                Some(RemoteProgress {                    action,                    percent: _,                    step,                    max,                }) => {                    progress.set_name(progress_name(progress.name(), action));                    progress.init(max, gix_features::progress::count("objects"));                    if let Some(step) = step {                        progress.set(step);                    }                }                None => progress.set_name(progress_name(progress.name(), text)),            }        }    }} fn parse_number(i: &mut &[u8]) -> ModalResult<usize, ()> {    take_till(0.., |c: u8| !c.is_ascii_digit())        .try_map(gix_utils::btoi::to_signed)        .parse_next(i)} fn next_optional_percentage(i: &mut &[u8]) -> ModalResult<Option<u32>, ()> {    opt(terminated(        preceded(            take_till(0.., |c: u8| c.is_ascii_digit()),            parse_number.try_map(u32::try_from),        ),        b"%",    ))    .parse_next(i)} fn next_optional_number(i: &mut &[u8]) -> ModalResult<Option<usize>, ()> {    opt(preceded(take_till(0.., |c: u8| c.is_ascii_digit()), parse_number)).parse_next(i)} fn parse_progress<'i>(line: &mut &'i [u8]) -> ModalResult<RemoteProgress<'i>, ()> {    let action = take_till(1.., |c| c == b':').parse_next(line)?;    let percent = next_optional_percentage.parse_next(line)?;    let step = next_optional_number.parse_next(line)?;    let max = next_optional_number.parse_next(line)?;    Ok(RemoteProgress {        action: action.into(),        percent,        step,        max,    })}