Skip to content

Commit

Permalink
do a little tidying and add specific_key helper
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-codecov committed Feb 14, 2024
1 parent f04b4fa commit 509a04c
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 22 deletions.
32 changes: 32 additions & 0 deletions src/parsers.rs
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
pub mod json;

use winnow::{
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)
}

#[cfg(test)]
mod tests {
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 ", "")));
}
}
70 changes: 48 additions & 22 deletions src/parsers/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,19 @@ use std::collections::HashMap;
use winnow::{
ascii::float,
combinator::{alt, fold_repeat, preceded, separated, separated_pair, terminated},
stream::{AsBStr, Compare, ParseSlice, Stream, StreamIsPartial},
token::{none_of, take_while},
error::ContextError,
stream::Stream,
token::none_of,
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>;
use crate::parsers::{ws, StrStream};

/*
* Parsers in this section return raw Rust types and may be useful to other
* parsers.
*/

/// 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 the string "null", returning "null" as a slice.
pub fn parse_null<S: StrStream>(buf: &mut S) -> PResult<<S as Stream>::Slice> {
"null".parse_next(buf)
Expand Down Expand Up @@ -154,19 +143,34 @@ pub fn json_value<S: StrStream>(buf: &mut S) -> PResult<JsonVal> {
.parse_next(buf)
}

/// Parses the next key + `:` delimiter and asserts that the key matches the
/// passed-in value. To get the corresponding value, parse with something like:
///
/// ```
/// # use codecov_rs::parsers::json::{specific_key, json_value, JsonVal};
/// # use winnow::combinator::preceded;
/// # use winnow::Parser;
/// let expected = Ok(("", JsonVal::Array(vec![])));
/// let result = preceded(specific_key("files"), json_value).parse_peek("\"files\": []");
/// assert_eq!(expected, result);
/// ```
///
/// Not used in generic json parsing but helpful when writing parsers for json
/// data that adheres to a schema.
pub fn specific_key<S: StrStream>(key: &str) -> impl Parser<S, String, ContextError> + '_ {
move |i: &mut S| {
preceded(ws, terminated(parse_str, (ws, ':', ws)))
.verify(move |s: &String| s == key)
.parse_next(i)
}
}

#[cfg(test)]
mod tests {
use winnow::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_null() {
// test that an exact match succeeds
Expand Down Expand Up @@ -654,4 +658,26 @@ mod tests {
))
);
}

#[test]
fn test_specific_key() {
assert_eq!(
specific_key("files").parse_peek("\"files\": {\"src/report.rs"),
Ok(("{\"src/report.rs", "files".to_string()))
);

// malformed
assert_eq!(
specific_key("files").parse_peek("files\": {\"src"),
Err(ErrMode::Backtrack(ContextError::new()))
);
assert_eq!(
specific_key("files").parse_peek("\"files: {\"src"),
Err(ErrMode::Backtrack(ContextError::new()))
);
assert_eq!(
specific_key("files").parse_peek("leading\"files\": {\"src"),
Err(ErrMode::Backtrack(ContextError::new()))
);
}
}

0 comments on commit 509a04c

Please sign in to comment.