diff --git a/Cargo.lock b/Cargo.lock index ddb41a698..b3114250e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,21 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.2.1" @@ -775,6 +790,38 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "proptest" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "quick-error 2.0.1", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.9" @@ -824,6 +871,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + [[package]] name = "rayon" version = "1.5.1" @@ -894,6 +950,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + [[package]] name = "rustversion" version = "1.0.6" @@ -1157,6 +1225,7 @@ dependencies = [ "num-format", "once_cell", "parking_lot", + "proptest", "rayon", "regex", "serde", @@ -1302,6 +1371,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.3.2" diff --git a/Cargo.toml b/Cargo.toml index eb6974b78..c1c8fe1fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,6 +75,7 @@ optional = true version = "0.8.15" [dev-dependencies] +proptest = "1.0.0" strum = "0.23.0" strum_macros = "0.23.0" regex = "1.4.6" diff --git a/src/utils/ext.rs b/src/utils/ext.rs index 5169fd51b..c9cd516dd 100644 --- a/src/utils/ext.rs +++ b/src/utils/ext.rs @@ -38,9 +38,10 @@ impl SliceExt for [u8] { .iter() .rposition(|c| c.is_line_ending_whitespace() || !c.is_whitespace()) .map_or_else( - || self.len(), + || self.len().saturating_sub(1), |i| { - if self[i.saturating_sub(1)] == b'\r' { + // Remove the entire `\r\n` in the case that it was the line ending whitespace + if self[i.saturating_sub(1)] == b'\r' && self[i] == b'\n' { i - 1 } else { i @@ -114,6 +115,8 @@ impl SliceExt for [u8] { mod tests { use super::*; + use proptest::prelude::*; + #[test] fn is_whitespace() { assert!(b' '.is_whitespace()); @@ -142,4 +145,19 @@ mod tests { assert!([1, 2, 3, 4, 5].contains_slice(&[2, 3, 4])); assert!(![1, 2, 3, 4, 5].contains_slice(&[])); } + + #[test] + fn trim_first_and_last_line_of_whitespace_edge_cases() { + assert_eq!(b"", b"\ra ".trim_first_and_last_line_of_whitespace()); + assert_eq!(b"a", b"\r\na ".trim_first_and_last_line_of_whitespace()); + + assert_eq!(b" ", b" ".trim_first_and_last_line_of_whitespace()); + } + + proptest! { + #[test] + fn trim_first_and_last_line_of_whitespace_doesnt_panic(input: Vec) { + let _ = &input.trim_first_and_last_line_of_whitespace(); + } + } }