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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
|
|
@ -109,6 +118,12 @@ version = "0.6.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-vec"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
|
@ -447,10 +462,16 @@ version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
|
checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-set",
|
"bit-set 0.5.3",
|
||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filedescriptor"
|
name = "filedescriptor"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
|
@ -1042,7 +1063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1094,6 +1115,15 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
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]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.2.37"
|
version = "0.2.37"
|
||||||
|
|
@ -1113,6 +1143,42 @@ dependencies = [
|
||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.44"
|
version = "1.0.44"
|
||||||
|
|
@ -1134,7 +1200,27 @@ version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
|
|
@ -1143,6 +1229,24 @@ version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
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]]
|
[[package]]
|
||||||
name = "ratatui"
|
name = "ratatui"
|
||||||
version = "0.30.0"
|
version = "0.30.0"
|
||||||
|
|
@ -1286,6 +1390,8 @@ dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
"jiff",
|
"jiff",
|
||||||
"nix 0.31.1",
|
"nix 0.31.1",
|
||||||
|
"proptest",
|
||||||
|
"proptest-derive",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"ratatui-themes",
|
"ratatui-themes",
|
||||||
"regex",
|
"regex",
|
||||||
|
|
@ -1324,6 +1430,18 @@ version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
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]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
|
|
@ -1515,6 +1633,19 @@ dependencies = [
|
||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "terminfo"
|
name = "terminfo"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|
@ -1719,6 +1850,12 @@ version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unarray"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
|
|
@ -1793,6 +1930,15 @@ dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wait-timeout"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.1+wasi-snapshot-preview1"
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
|
@ -2111,6 +2257,26 @@ dependencies = [
|
||||||
"wasmparser",
|
"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]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
|
|
|
||||||
|
|
@ -22,3 +22,5 @@ regex = "1"
|
||||||
crossterm = "*"
|
crossterm = "*"
|
||||||
dumpster = "2.1"
|
dumpster = "2.1"
|
||||||
winnow = {version="1", features=["parser"]}
|
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},
|
fmt::{self, Display},
|
||||||
};
|
};
|
||||||
|
|
||||||
use winnow::{
|
#[cfg(test)]
|
||||||
Parser,
|
use proptest::prelude::*;
|
||||||
ascii::dec_uint,
|
|
||||||
combinator::impls::ByRef,
|
|
||||||
error::{FromExternalError, ParserError},
|
|
||||||
stream,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
use proptest_derive::Arbitrary;
|
||||||
|
use winnow::{Parser, error::ParserError};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Separator {
|
pub enum Separator {
|
||||||
Eq,
|
Eq,
|
||||||
Colon,
|
Colon,
|
||||||
|
|
@ -38,7 +36,7 @@ impl Display for Separator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, Arbitrary, PartialEq)]
|
||||||
pub enum QuoteType {
|
pub enum QuoteType {
|
||||||
Single,
|
Single,
|
||||||
Double,
|
Double,
|
||||||
|
|
@ -68,7 +66,7 @@ impl Display for QuoteType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Arbitrary, PartialEq)]
|
||||||
pub enum Delimiter {
|
pub enum Delimiter {
|
||||||
Paren,
|
Paren,
|
||||||
Bracket,
|
Bracket,
|
||||||
|
|
@ -96,7 +94,7 @@ impl Delimiter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct AnyString<'a> {
|
pub struct AnyString<'a> {
|
||||||
prefix: Cow<'a, str>,
|
prefix: Cow<'a, str>,
|
||||||
ty: QuoteType,
|
ty: QuoteType,
|
||||||
|
|
@ -105,6 +103,27 @@ pub struct AnyString<'a> {
|
||||||
suffix: Cow<'a, str>,
|
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> {
|
impl<'a> AnyString<'a> {
|
||||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||||
use winnow::{combinator::*, prelude::*, token::*};
|
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>);
|
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> {
|
impl<'a> Space<'a> {
|
||||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||||
use winnow::{prelude::*, token::*};
|
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 {
|
pub enum PathSep {
|
||||||
Slash,
|
Slash,
|
||||||
Backslash,
|
Backslash,
|
||||||
|
|
@ -202,12 +228,22 @@ impl Display for PathSep {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PathSegment<'a> {
|
pub struct PathSegment<'a> {
|
||||||
leading_separator: PathSep,
|
leading_separator: PathSep,
|
||||||
segment: Cow<'a, str>,
|
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> {
|
impl<'a> Display for PathSegment<'a> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.leading_separator)?;
|
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> {
|
pub struct FileLocation<'a> {
|
||||||
line: Cow<'a, str>,
|
line: Cow<'a, str>,
|
||||||
offset: Option<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> {
|
impl<'a> Display for FileLocation<'a> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.line)?;
|
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> {
|
pub struct FileName<'a> {
|
||||||
leading_separator: PathSep,
|
leading_separator: PathSep,
|
||||||
segment: Cow<'a, str>,
|
segment: Cow<'a, str>,
|
||||||
|
|
@ -239,6 +286,27 @@ pub struct FileName<'a> {
|
||||||
location: Option<FileLocation<'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> {
|
impl<'a> FileName<'a> {
|
||||||
fn parse(segment: PathSegment<'a>) -> Self {
|
fn parse(segment: PathSegment<'a>) -> Self {
|
||||||
fn rsplit<'a>(
|
fn rsplit<'a>(
|
||||||
|
|
@ -308,13 +376,30 @@ impl<'a> Display for FileName<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Path<'a> {
|
pub struct Path<'a> {
|
||||||
drive_excluding_colon: Option<char>,
|
drive_excluding_colon: Option<char>,
|
||||||
segments: Vec<PathSegment<'a>>,
|
segments: Vec<PathSegment<'a>>,
|
||||||
filename: FileName<'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> {
|
impl<'a> Path<'a> {
|
||||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||||
use winnow::{combinator::*, prelude::*, token::*};
|
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>);
|
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> {
|
impl<'a> Number<'a> {
|
||||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||||
use winnow::{ascii::*, combinator::*, prelude::*};
|
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`.
|
/// Anything that doesn't contain spaces, and that can be a prefix of `Delimited`.
|
||||||
/// i.e. an english word, or rust `::`-separated Path
|
/// i.e. an english word, or rust `::`-separated Path
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Atom<'a> {
|
pub enum Atom<'a> {
|
||||||
Text(Cow<'a, str>),
|
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> {
|
impl<'a> Atom<'a> {
|
||||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||||
use winnow::{combinator::*, prelude::*, token::*};
|
use winnow::{combinator::*, prelude::*, token::*};
|
||||||
|
|
@ -414,7 +516,7 @@ impl<'a> Atom<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Token<'a> {
|
pub enum Token<'a> {
|
||||||
True,
|
True,
|
||||||
False,
|
False,
|
||||||
|
|
@ -436,10 +538,32 @@ pub enum Token<'a> {
|
||||||
Atom(Atom<'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> {
|
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::*};
|
use winnow::{combinator::*, prelude::*};
|
||||||
|
|
||||||
|
let delimited: Box<dyn Parser<&'a str, Self, E>> =
|
||||||
|
Box::new(Delimited::parse().map(Self::Delimited));
|
||||||
|
|
||||||
alt((
|
alt((
|
||||||
"true".value(Self::True),
|
"true".value(Self::True),
|
||||||
"false".value(Self::False),
|
"false".value(Self::False),
|
||||||
|
|
@ -447,6 +571,8 @@ impl<'a> Token<'a> {
|
||||||
Path::parse().map(Self::Path),
|
Path::parse().map(Self::Path),
|
||||||
AnyString::parse().map(Self::String),
|
AnyString::parse().map(Self::String),
|
||||||
Number::parse().map(Self::Number),
|
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> {
|
pub struct Delimited<'a> {
|
||||||
prefix: Atom<'a>,
|
prefix: Atom<'a>,
|
||||||
delimiter: Delimiter,
|
delimiter: Delimiter,
|
||||||
contents: Segments<'a>,
|
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> {
|
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::*};
|
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> {
|
pub struct Segment<'a> {
|
||||||
leading_space: Space<'a>,
|
leading_space: Space<'a>,
|
||||||
token: Token<'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> {
|
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::*;
|
use winnow::prelude::*;
|
||||||
|
|
||||||
(Space::parse(), Token::parse()).map(|(leading_space, token)| Self {
|
(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> {
|
pub struct Segments<'a> {
|
||||||
segments: Vec<Segment<'a>>,
|
segments: Vec<Segment<'a>>,
|
||||||
trailing_space: Space<'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> {
|
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>,
|
end: impl Parser<&'a str, End, E>,
|
||||||
) -> impl Parser<&'a str, (Self, End), E> {
|
) -> impl Parser<&'a str, (Self, End), E> {
|
||||||
use winnow::{combinator::*, prelude::*};
|
use winnow::{combinator::*, prelude::*};
|
||||||
|
|
@ -567,3 +730,27 @@ impl<'a> Display for Segments<'a> {
|
||||||
write!(f, "{}", self.trailing_space)
|
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