Skip to content

Commit

Permalink
reorganize parsers module
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-codecov committed Apr 26, 2024
1 parent 3197e7c commit 9d42fd9
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 204 deletions.
181 changes: 0 additions & 181 deletions src/parsers.rs

This file was deleted.

179 changes: 179 additions & 0 deletions src/parsers/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
use std::{fmt, fmt::Debug, marker::PhantomData};

use crate::report::{Report, ReportBuilder};

#[derive(PartialEq)]
pub struct ReportBuilderCtx<R: Report, B: ReportBuilder<R>> {
pub report_builder: B,
_phantom: PhantomData<R>,
}

impl<R: Report, B: ReportBuilder<R>> ReportBuilderCtx<R, B> {
pub fn new(report_builder: B) -> ReportBuilderCtx<R, B> {
ReportBuilderCtx {
report_builder,
_phantom: PhantomData,
}
}
}

impl<R: Report, B: ReportBuilder<R>> Debug for ReportBuilderCtx<R, B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReportBuilderCtx")
// .field("report_builder", &self.report_builder)
.finish()
}
}

pub mod winnow {
use winnow::{
ascii::float,
combinator::alt,
error::ParserError,
stream::{AsBStr, Compare, ParseSlice, Stream, StreamIsPartial},
token::take_while,
PResult, Parser,
};

pub trait CharStream = Stream<Token = char> + StreamIsPartial;
pub trait StrStream = CharStream + for<'a> Compare<&'a str> + AsBStr
where
<Self as Stream>::IterOffsets: Clone,
<Self as Stream>::Slice: ParseSlice<f64>;

/// Characters considered whitespace for the `ws` parser.
const WHITESPACE: &[char] = &[' ', '\t', '\n', '\r'];

/// Parses a series of whitespace characters, returning the series as a
/// slice.
pub fn ws<S: CharStream>(buf: &mut S) -> PResult<<S as Stream>::Slice> {
take_while(0.., WHITESPACE).parse_next(buf)
}

/// Parses an unsigned decimal number with support for scientific notation.
/// Truncates floats, clamps numbers not in the `u32` range.
pub fn parse_u32<S: StrStream>(buf: &mut S) -> PResult<u32> {
float.map(move |x: f64| x as u32).parse_next(buf)
}

/// Combinator that will match the passed-in parser or `null`.
/// - If the passed-in parser matches, return `Some(output)`
/// - Of `null` matches, return `None`
/// - Otherwise, backtrack or whatever
pub fn nullable<Input: StrStream, Output, Error, ParseNext>(
parser: ParseNext,
) -> impl Parser<Input, Option<Output>, Error>
where
ParseNext: Parser<Input, Output, Error>,
Error: ParserError<Input>,
Output: Clone,
{
alt((parser.map(Some), "null".value(None::<Output>)))
}

#[cfg(test)]
mod tests {
use winnow::{
ascii::{alpha1, dec_uint},
error::{ContextError, ErrMode},
};

use super::*;

#[test]
fn test_ws() {
assert_eq!(ws.parse_peek(" \r\t\n"), Ok(("", " \r\t\n")));
assert_eq!(ws.parse_peek(" asd"), Ok(("asd", " ")));
assert_eq!(ws.parse_peek("asd "), Ok(("asd ", "")));
}

#[test]
fn test_parse_u32() {
assert_eq!(parse_u32.parse_peek("30"), Ok(("", 30)));
assert_eq!(parse_u32.parse_peek("30 "), Ok((" ", 30)));

// Floats are truncated, not rounded
assert_eq!(parse_u32.parse_peek("30.6 "), Ok((" ", 30)));
assert_eq!(parse_u32.parse_peek("30.1 "), Ok((" ", 30)));

// Scientific notation
assert_eq!(parse_u32.parse_peek("1e+0"), Ok(("", 1)));
assert_eq!(parse_u32.parse_peek("5.2e+5"), Ok(("", 520000)));
assert_eq!(parse_u32.parse_peek("1.2345e+2"), Ok(("", 123)));
assert_eq!(parse_u32.parse_peek("2.7e-5"), Ok(("", 0)));

// Numbers are clamped to `u32` range
assert_eq!(parse_u32.parse_peek("5000000000"), Ok(("", 4294967295)));
assert_eq!(parse_u32.parse_peek("2.7e+20"), Ok(("", 4294967295)));
assert_eq!(parse_u32.parse_peek("-1"), Ok(("", 0)));
assert_eq!(parse_u32.parse_peek("-100"), Ok(("", 0)));
assert_eq!(parse_u32.parse_peek("-4.2"), Ok(("", 0)));
assert_eq!(parse_u32.parse_peek("-4.2e-1"), Ok(("", 0)));

// Malformed
assert_eq!(
parse_u32.parse_peek(" 30"),
Err(ErrMode::Backtrack(ContextError::new()))
);
assert_eq!(
parse_u32.parse_peek("x30"),
Err(ErrMode::Backtrack(ContextError::new()))
);
}

#[test]
fn test_nullable() {
// with floats
assert_eq!(
nullable(float::<&str, f64, ContextError>).parse_peek("3.4"),
Ok(("", Some(3.4)))
);
assert_eq!(
nullable(float::<&str, f64, ContextError>).parse_peek("null"),
Ok(("", None))
);
assert_eq!(
nullable(float::<&str, f64, ContextError>).parse_peek("malformed"),
Err(ErrMode::Backtrack(ContextError::new())),
);
assert_eq!(
nullable(float::<&str, f64, ContextError>).parse_peek("nul"),
Err(ErrMode::Backtrack(ContextError::new())),
);

// with decimals
assert_eq!(
nullable(dec_uint::<&str, u64, ContextError>).parse_peek("3.4"),
Ok((".4", Some(3)))
);
assert_eq!(
nullable(dec_uint::<&str, u64, ContextError>).parse_peek("null"),
Ok(("", None))
);
assert_eq!(
nullable(dec_uint::<&str, u64, ContextError>).parse_peek("malformed"),
Err(ErrMode::Backtrack(ContextError::new())),
);
assert_eq!(
nullable(dec_uint::<&str, u64, ContextError>).parse_peek("nul"),
Err(ErrMode::Backtrack(ContextError::new())),
);

// with chars
assert_eq!(
nullable(alpha1::<&str, ContextError>).parse_peek("abcde"),
Ok(("", Some("abcde")))
);
// this is an edge case - `alpha1` has no problem matching `"null"` so we should
// let it
assert_eq!(
nullable(alpha1::<&str, ContextError>).parse_peek("null"),
Ok(("", Some("null")))
);
assert_eq!(
nullable(alpha1::<&str, ContextError>).parse_peek(".123."),
Err(ErrMode::Backtrack(ContextError::new())),
);
}
}
}
2 changes: 1 addition & 1 deletion src/parsers/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use winnow::{
PResult, Parser,
};

use crate::parsers::{ws, StrStream};
use super::common::winnow::*;

/*
* Parsers in this section return raw Rust types and may be useful to other
Expand Down
6 changes: 6 additions & 0 deletions src/parsers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod json;

#[cfg(feature = "pyreport_shim")]
pub mod pyreport_shim;

pub mod common;
2 changes: 1 addition & 1 deletion src/parsers/pyreport_shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use std::{fs::File, path::PathBuf};
use memmap2::Mmap;
use winnow::Parser;

use super::common::ReportBuilderCtx;
use crate::{
error::{CodecovError, Result},
parsers::ReportBuilderCtx,
report::{ReportBuilder, SqliteReport, SqliteReportBuilder},
};

Expand Down
Loading

0 comments on commit 9d42fd9

Please sign in to comment.