proptesting
This commit is contained in:
parent
e3648ad8e6
commit
bedaa49754
4 changed files with 393 additions and 31 deletions
174
Cargo.lock
generated
174
Cargo.lock
generated
|
|
@ -100,7 +100,16 @@ version = "0.5.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"bit-vec 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -109,6 +118,12 @@ version = "0.6.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
|
@ -447,10 +462,16 @@ version = "0.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bit-set 0.5.3",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "filedescriptor"
|
||||
version = "0.8.3"
|
||||
|
|
@ -1042,7 +1063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1094,6 +1115,15 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
|
|
@ -1113,6 +1143,42 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
|
||||
dependencies = [
|
||||
"bit-set 0.8.0",
|
||||
"bit-vec 0.8.0",
|
||||
"bitflags 2.11.0",
|
||||
"num-traits",
|
||||
"rand 0.9.2",
|
||||
"rand_chacha",
|
||||
"rand_xorshift",
|
||||
"regex-syntax",
|
||||
"rusty-fork",
|
||||
"tempfile",
|
||||
"unarray",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proptest-derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c57924a81864dddafba92e1bf92f9bf82f97096c44489548a60e888e1547549b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
|
|
@ -1134,7 +1200,27 @@ version = "0.8.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1143,6 +1229,24 @@ version = "0.6.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||
dependencies = [
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.30.0"
|
||||
|
|
@ -1286,6 +1390,8 @@ dependencies = [
|
|||
"itertools",
|
||||
"jiff",
|
||||
"nix 0.31.1",
|
||||
"proptest",
|
||||
"proptest-derive",
|
||||
"ratatui",
|
||||
"ratatui-themes",
|
||||
"regex",
|
||||
|
|
@ -1324,6 +1430,18 @@ version = "1.0.22"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "rusty-fork"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"quick-error",
|
||||
"tempfile",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
|
|
@ -1515,6 +1633,19 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.4.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminfo"
|
||||
version = "0.9.0"
|
||||
|
|
@ -1719,6 +1850,12 @@ version = "0.1.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||
|
||||
[[package]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
|
|
@ -1793,6 +1930,15 @@ dependencies = [
|
|||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
|
|
@ -2111,6 +2257,26 @@ dependencies = [
|
|||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
|
|
|
|||
|
|
@ -22,3 +22,5 @@ regex = "1"
|
|||
crossterm = "*"
|
||||
dumpster = "2.1"
|
||||
winnow = {version="1", features=["parser"]}
|
||||
proptest = "1"
|
||||
proptest-derive = "0.8"
|
||||
|
|
|
|||
7
proptest-regressions/format_debug_output.txt
Normal file
7
proptest-regressions/format_debug_output.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Seeds for failure cases proptest has generated in the past. It is
|
||||
# automatically read and these particular cases re-run before any
|
||||
# novel cases are generated.
|
||||
#
|
||||
# It is recommended to check this file in to source control so that
|
||||
# everyone who runs the test benefits from these saved cases.
|
||||
cc 255ec5cdb18e04e16d465fb94cbf15040fb1d5d704c53226ce3a2e9333c6de7c # shrinks to original = Segments { segments: [Segment { leading_space: Space(""), token: Path(Path { drive_excluding_colon: None, segments: [], filename: FileName { leading_separator: Slash, segment: "", ext_excluding_dot: None, location: None } }) }], trailing_space: Space("") }
|
||||
|
|
@ -3,15 +3,13 @@ use std::{
|
|||
fmt::{self, Display},
|
||||
};
|
||||
|
||||
use winnow::{
|
||||
Parser,
|
||||
ascii::dec_uint,
|
||||
combinator::impls::ByRef,
|
||||
error::{FromExternalError, ParserError},
|
||||
stream,
|
||||
};
|
||||
#[cfg(test)]
|
||||
use proptest::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
use proptest_derive::Arbitrary;
|
||||
use winnow::{Parser, error::ParserError};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Separator {
|
||||
Eq,
|
||||
Colon,
|
||||
|
|
@ -38,7 +36,7 @@ impl Display for Separator {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Arbitrary, PartialEq)]
|
||||
pub enum QuoteType {
|
||||
Single,
|
||||
Double,
|
||||
|
|
@ -68,7 +66,7 @@ impl Display for QuoteType {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Arbitrary, PartialEq)]
|
||||
pub enum Delimiter {
|
||||
Paren,
|
||||
Bracket,
|
||||
|
|
@ -96,7 +94,7 @@ impl Delimiter {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AnyString<'a> {
|
||||
prefix: Cow<'a, str>,
|
||||
ty: QuoteType,
|
||||
|
|
@ -105,6 +103,27 @@ pub struct AnyString<'a> {
|
|||
suffix: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl AnyString<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
let prefix = "\\w*";
|
||||
let ty = any::<QuoteType>();
|
||||
let contents = "\\w*";
|
||||
let num_hashtags = 0usize..3;
|
||||
let suffix = "\\w*";
|
||||
|
||||
(prefix, ty, contents, num_hashtags, suffix).prop_map(
|
||||
|(prefix, ty, contents, num_hashtags, suffix)| Self {
|
||||
prefix: prefix.into(),
|
||||
ty,
|
||||
contents: contents.into(),
|
||||
num_hashtags,
|
||||
suffix: suffix.into(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AnyString<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*, token::*};
|
||||
|
|
@ -162,9 +181,16 @@ impl<'a> Display for AnyString<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Space<'a>(Cow<'a, str>);
|
||||
|
||||
impl Space<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
" *".prop_map(|spaces| Self(spaces.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Space<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{prelude::*, token::*};
|
||||
|
|
@ -179,7 +205,7 @@ impl<'a> Display for Space<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Arbitrary)]
|
||||
pub enum PathSep {
|
||||
Slash,
|
||||
Backslash,
|
||||
|
|
@ -202,12 +228,22 @@ impl Display for PathSep {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PathSegment<'a> {
|
||||
leading_separator: PathSep,
|
||||
segment: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl PathSegment<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
(any::<PathSep>(), "\\w*").prop_map(|(leading_separator, segment)| Self {
|
||||
leading_separator,
|
||||
segment: segment.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for PathSegment<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.leading_separator)?;
|
||||
|
|
@ -215,12 +251,23 @@ impl<'a> Display for PathSegment<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FileLocation<'a> {
|
||||
line: Cow<'a, str>,
|
||||
offset: Option<Cow<'a, str>>,
|
||||
}
|
||||
|
||||
impl FileLocation<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
use proptest::option::*;
|
||||
("[0-9]{0,4}", of("[0-9]{0,4}")).prop_map(|(line, offset)| Self {
|
||||
line: line.into(),
|
||||
offset: offset.map(Into::into),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for FileLocation<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.line)?;
|
||||
|
|
@ -231,7 +278,7 @@ impl<'a> Display for FileLocation<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FileName<'a> {
|
||||
leading_separator: PathSep,
|
||||
segment: Cow<'a, str>,
|
||||
|
|
@ -239,6 +286,27 @@ pub struct FileName<'a> {
|
|||
location: Option<FileLocation<'a>>,
|
||||
}
|
||||
|
||||
impl FileName<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
use proptest::option::*;
|
||||
(
|
||||
any::<PathSep>(),
|
||||
"\\w*",
|
||||
of(".{0,3}"),
|
||||
of(FileLocation::arb()),
|
||||
)
|
||||
.prop_map(
|
||||
|(leading_separator, segment, ext_excluding_dot, location)| Self {
|
||||
leading_separator,
|
||||
segment: segment.into(),
|
||||
ext_excluding_dot: ext_excluding_dot.map(Into::into),
|
||||
location,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FileName<'a> {
|
||||
fn parse(segment: PathSegment<'a>) -> Self {
|
||||
fn rsplit<'a>(
|
||||
|
|
@ -308,13 +376,30 @@ impl<'a> Display for FileName<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Path<'a> {
|
||||
drive_excluding_colon: Option<char>,
|
||||
segments: Vec<PathSegment<'a>>,
|
||||
filename: FileName<'a>,
|
||||
}
|
||||
|
||||
impl Path<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
use proptest::{char::*, collection::*, option::*};
|
||||
(
|
||||
of(range('A', 'Z')),
|
||||
vec(PathSegment::arb(), 0..3),
|
||||
FileName::arb(),
|
||||
)
|
||||
.prop_map(|(drive_excluding_colon, segments, filename)| Self {
|
||||
drive_excluding_colon,
|
||||
segments,
|
||||
filename,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Path<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*, token::*};
|
||||
|
|
@ -374,9 +459,19 @@ impl<'a> Display for Path<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Number<'a>(Cow<'a, str>);
|
||||
|
||||
impl Number<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
prop_oneof![
|
||||
any::<i64>().prop_map(|number| Self(number.to_string().into())),
|
||||
any::<f64>().prop_map(|number| Self(number.to_string().into()))
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Number<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{ascii::*, combinator::*, prelude::*};
|
||||
|
|
@ -393,7 +488,7 @@ impl<'a> Display for Number<'a> {
|
|||
|
||||
/// Anything that doesn't contain spaces, and that can be a prefix of `Delimited`.
|
||||
/// i.e. an english word, or rust `::`-separated Path
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Atom<'a> {
|
||||
Text(Cow<'a, str>),
|
||||
}
|
||||
|
|
@ -406,6 +501,13 @@ impl<'a> Display for Atom<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Atom<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
"\\w*".prop_map(|i| Self::Text(i.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Atom<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*, token::*};
|
||||
|
|
@ -414,7 +516,7 @@ impl<'a> Atom<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Token<'a> {
|
||||
True,
|
||||
False,
|
||||
|
|
@ -436,10 +538,32 @@ pub enum Token<'a> {
|
|||
Atom(Atom<'a>),
|
||||
}
|
||||
|
||||
impl Token<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
let leaf = prop_oneof![
|
||||
Just(Self::True),
|
||||
Just(Self::False),
|
||||
Just(Self::None),
|
||||
Path::arb().prop_map(Self::Path),
|
||||
AnyString::arb().prop_map(Self::String),
|
||||
Number::arb().prop_map(Self::Number),
|
||||
Atom::arb().prop_map(Self::Atom),
|
||||
];
|
||||
|
||||
leaf.prop_recursive(4, 64, 16, |token| {
|
||||
Delimited::arb(token).prop_map(Self::Delimited).boxed()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Token<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
fn parse<E: ParserError<&'a str> + 'a>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*};
|
||||
|
||||
let delimited: Box<dyn Parser<&'a str, Self, E>> =
|
||||
Box::new(Delimited::parse().map(Self::Delimited));
|
||||
|
||||
alt((
|
||||
"true".value(Self::True),
|
||||
"false".value(Self::False),
|
||||
|
|
@ -447,6 +571,8 @@ impl<'a> Token<'a> {
|
|||
Path::parse().map(Self::Path),
|
||||
AnyString::parse().map(Self::String),
|
||||
Number::parse().map(Self::Number),
|
||||
delimited,
|
||||
Atom::parse().map(Self::Atom),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -472,15 +598,28 @@ impl<'a> Display for Token<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Delimited<'a> {
|
||||
prefix: Atom<'a>,
|
||||
delimiter: Delimiter,
|
||||
contents: Segments<'a>,
|
||||
}
|
||||
|
||||
impl Delimited<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb(token: impl Strategy<Value = Token<'static>>) -> impl Strategy<Value = Self> {
|
||||
(Atom::arb(), any::<Delimiter>(), Segments::arb(token)).prop_map(
|
||||
|(prefix, delimiter, contents)| Self {
|
||||
prefix,
|
||||
delimiter,
|
||||
contents,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Delimited<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
fn parse<E: ParserError<&'a str> + 'a>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*, token::*};
|
||||
|
||||
(
|
||||
|
|
@ -509,14 +648,24 @@ impl<'a> Display for Delimited<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Segment<'a> {
|
||||
leading_space: Space<'a>,
|
||||
token: Token<'a>,
|
||||
}
|
||||
|
||||
impl Segment<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb(token: impl Strategy<Value = Token<'static>>) -> impl Strategy<Value = Self> {
|
||||
(Space::arb(), token).prop_map(|(leading_space, token)| Self {
|
||||
leading_space,
|
||||
token,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Segment<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
fn parse<E: ParserError<&'a str> + 'a>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::prelude::*;
|
||||
|
||||
(Space::parse(), Token::parse()).map(|(leading_space, token)| Self {
|
||||
|
|
@ -533,14 +682,28 @@ impl<'a> Display for Segment<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Segments<'a> {
|
||||
segments: Vec<Segment<'a>>,
|
||||
trailing_space: Space<'a>,
|
||||
}
|
||||
|
||||
impl Segments<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb(token: impl Strategy<Value = Token<'static>>) -> impl Strategy<Value = Self> {
|
||||
use proptest::collection::*;
|
||||
|
||||
(vec(Segment::arb(token), 1..10), Space::arb()).prop_map(|(segments, trailing_space)| {
|
||||
Self {
|
||||
segments,
|
||||
trailing_space,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Segments<'a> {
|
||||
fn parse<E: ParserError<&'a str>, End: 'a>(
|
||||
fn parse<E: ParserError<&'a str> + 'a, End: 'a>(
|
||||
end: impl Parser<&'a str, End, E>,
|
||||
) -> impl Parser<&'a str, (Self, End), E> {
|
||||
use winnow::{combinator::*, prelude::*};
|
||||
|
|
@ -567,3 +730,27 @@ impl<'a> Display for Segments<'a> {
|
|||
write!(f, "{}", self.trailing_space)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_input<'a>(i: &'a str) -> Result<Segments<'a>, String> {
|
||||
use winnow::combinator::eof;
|
||||
Segments::parse(eof::<&str, winnow::error::EmptyError>)
|
||||
.map(|(segments, _)| segments)
|
||||
.parse(i)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use proptest::proptest;
|
||||
|
||||
use crate::format_debug_output::{Segments, Token, parse_input};
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_some_function(original in Segments::arb(Token::arb())) {
|
||||
let stringified = original.to_string();
|
||||
let parsed = parse_input(&stringified).unwrap();
|
||||
assert_eq!(parsed, original, "left:\n `{parsed}`\nright: `{stringified}`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue