1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
#![deny(missing_docs)]
//! Functions and macros to compare strings, ignoring whitespace
#[macro_export]
/// Compare two strings, collapse consecutive whitespace, and assert their equality.
/// Panics if two strings are not equivalent after collapsing whitespace.
///
/// ```
/// #[macro_use]
/// use collapse::*;
///
/// collapsed_eq!("one space", "one space");
/// collapsed_eq!("two spaces", "two spaces");
/// collapsed_eq!("new\r\nlines", "new\nlines");
/// collapsed_eq!(" lead \t tail \r", "lead tail");
/// ```
macro_rules! collapsed_eq {
($input:expr, $output:expr) => {
assert_eq!(collapse($input), collapse($output));
};
($input:expr, $output:expr, $($arg:tt)+) => {
assert_eq!(collapse($input), collapse($output), $($arg)+);
};
}
#[macro_export]
/// Compare two strings, collapse consecutive whitespace, and assert their inequality.
/// Panics if two strings are equivalent after collapsing whitespace.
///
/// ```
/// #[macro_use]
/// use collapse::*;
///
/// collapsed_ne!("one space", "ONE SPACE");
/// collapsed_ne!("two spaces", "TWO SPACES");
/// collapsed_ne!("new\r\nlines", "NEW\nLINES");
/// collapsed_ne!(" lead \t tail \r", "LEAD TAIL");
/// ```
macro_rules! collapsed_ne {
($input:expr, $output:expr) => {
assert_ne!(collapse($input), collapse($output));
};
($input:expr, $output:expr, $($arg:tt)+) => {
assert_ne!(collapse($input), collapse($output), $($arg)+);
};
}
/// Trim leading and trailing whitespace and collapse all consecutive whitespace to a single space
/// character
/// ```
/// use collapse::collapse;
///
/// assert_eq!(collapse("two spaces"), "two spaces");
/// assert_eq!(collapse("new\r\nlines"), "new lines");
/// assert_eq!(collapse("\t lead\t tail \r"), "lead tail");
/// ```
pub fn collapse(s: &str) -> String {
let s = s.trim();
let mut collapsed = String::new();
for c in s.chars() {
if let Some(last) = collapsed.chars().last() {
if c.is_whitespace() {
if !last.is_whitespace() {
collapsed.push(' ');
}
} else {
collapsed.push(c);
}
} else {
collapsed.push(c);
}
}
collapsed
}
#[cfg(test)]
mod test {
use crate::*;
#[test]
fn macro_test() {
// https://www.reddit.com/r/learnrust/comments/yilsa1/a_macro_to_collapse_whitespace_into_single_space/
collapsed_eq!(
r#"CREATE TABLE "test" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT
)"#,
r#"CREATE TABLE "test" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT )"#,
"This is a panic message you should never see"
);
collapsed_ne!(
"This is not the same...",
"...as this string.",
"You should never see this panic message either."
);
}
macro_rules! test {
($(#[$attr:meta])* $name:ident, $input:expr, $output:expr) => {
#[test]
$(#[$attr])*
fn $name() {
collapsed_eq!($input, $output);
}
}
}
test!(two_spaces, "two spaces", "two spaces");
test!(space_tab, "space tab", "space tab");
test!(line_space, "line\r\nspace", "line space");
test!(new_lines, "new\r\n
lines", "new lines");
test!(tabs, "some tabs\there", "some tabs here");
test!(no_change, "no change", "no change");
test!(all_whitespace, "\r\n\t ", " \n\r\t\n ");
test!(empty, "", "");
test!(empty1, " ", "");
test!(empty2, " ", "\n");
test!(empty3, " ", "\r\n");
test!(empty4, " ", " \t\t ");
test!(lead_tail, " lead \t tail \r", "lead tail");
test!(#[should_panic] should_fail1, "should fail", "SHOULD FAIL");
test!(#[should_panic] should_fail2, "should fail", "SHOULD FAIL");
test!(#[should_panic] should_fail3, "should\n fail", "SHOULD\n FAIL");
}