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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#![deny(missing_docs)]

//! Methods for prefixing and suffixing the non-whitespace characters in a string.
//!
//! # Examples
//!
//! ```
//! let wrapped = outerspace::wrap_non_whitespace("\n\nHello hello\n\n", "**", "**");
//! assert_eq!(wrapped, "\n\n**Hello hello**\n\n");
//! ```
//!
//! ```
//! let prefixed = outerspace::prefix_non_whitespace("\n\nHello hello\n\n", "> ");
//! assert_eq!(prefixed, "\n\n> Hello hello\n\n");
//! ```
//!
//! ```
//! let suffixed = outerspace::suffix_non_whitespace("\n\nHello hello\n\n", "!");
//! assert_eq!(suffixed, "\n\nHello hello!\n\n");
//! ```

fn is_non_whitespace(char: char) -> bool {
    !char.is_whitespace()
}

fn format_wrap(
    s: &str,
    prefix: &str,
    suffix: &str,
    first_non_whitespace: Option<usize>,
    last_non_whitespace: Option<usize>,
) -> String {
    match (first_non_whitespace, last_non_whitespace) {
        (Some(start), Some(end)) => {
            let (leading_ws, rest) = s.split_at(start);
            let (rest, trailing_ws) = rest.split_at(end - start + 1);
            format!("{}{}{}{}{}", leading_ws, prefix, rest, suffix, trailing_ws)
        }
        (Some(start), None) => {
            let (leading_ws, rest) = s.split_at(start);
            format!("{}{}{}{}", leading_ws, prefix, rest, suffix)
        }
        (None, Some(end)) => {
            let (rest, trailing_ws) = s.split_at(end + 1);
            format!("{}{}{}{}", prefix, rest, suffix, trailing_ws)
        }
        (None, None) => {
            format!("{}{}{}", prefix, s, suffix)
        }
    }
}

/// Insert a prefix and a suffix into the string. The prefix is inserted before the first non-whitespace character. The suffix is inserted after the last non-whitespace character.
///
/// Returns a heap-allocated String.
///
/// # Example
///
/// ```
/// let wrapped = outerspace::wrap_non_whitespace("\n\nHello hello\n\n", "**", "**");
/// assert_eq!(wrapped, "\n\n**Hello hello**\n\n");
/// ```
pub fn wrap_non_whitespace(s: &str, prefix: &str, suffix: &str) -> String {
    let first_non_whitespace = s.find(is_non_whitespace);
    let last_non_whitespace = s.rfind(is_non_whitespace);
    format_wrap(s, prefix, suffix, first_non_whitespace, last_non_whitespace)
}

/// Insert a prefix into the string before the first non-whitespace character.
///
/// Returns a heap-allocated String.
///
/// # Example
///
/// ```
/// let prefixed = outerspace::prefix_non_whitespace("\n\nHello hello\n\n", "> ");
/// assert_eq!(prefixed, "\n\n> Hello hello\n\n");
/// ```
pub fn prefix_non_whitespace(s: &str, prefix: &str) -> String {
    let first_non_whitespace = s.find(is_non_whitespace);
    format_wrap(s, prefix, "", first_non_whitespace, None)
}

/// Insert a suffix into the string after the last non-whitespace character.
///
/// Returns a heap-allocated String.
///
/// # Example
///
/// ```
/// let suffixed = outerspace::suffix_non_whitespace("\n\nHello hello\n\n", "!");
/// assert_eq!(suffixed, "\n\nHello hello!\n\n");
/// ```
pub fn suffix_non_whitespace(s: &str, suffix: &str) -> String {
    let last_non_whitespace = s.rfind(is_non_whitespace);
    format_wrap(s, "", suffix, None, last_non_whitespace)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn wrap_works() {
        assert_eq!(wrap_non_whitespace("", "<", ">"), "<>");
        assert_eq!(wrap_non_whitespace("  \n ", "<", ">"), "<  \n >");
        assert_eq!(wrap_non_whitespace("cosy", "<", ">"), "<cosy>");
        assert_eq!(
            wrap_non_whitespace("cosy matthew", "<", ">"),
            "<cosy matthew>"
        );
        assert_eq!(
            wrap_non_whitespace("\n \ncosy \nmatthew\n \n", "<", ">"),
            "\n \n<cosy \nmatthew>\n \n"
        );
    }

    #[test]
    fn prefix_works() {
        assert_eq!(prefix_non_whitespace("", "**"), "**");
        assert_eq!(prefix_non_whitespace("  \n ", "**"), "**  \n ");
        assert_eq!(prefix_non_whitespace("emboldened", "**"), "**emboldened");
        assert_eq!(
            prefix_non_whitespace("emboldened matthew", "**"),
            "**emboldened matthew"
        );
        assert_eq!(
            prefix_non_whitespace("\n \nemboldened \nmatthew", "**"),
            "\n \n**emboldened \nmatthew"
        );
    }

    #[test]
    fn suffix_works() {
        assert_eq!(suffix_non_whitespace("", "**"), "**");
        assert_eq!(suffix_non_whitespace("  \n ", "**"), "  \n **");
        assert_eq!(suffix_non_whitespace("emboldened", "**"), "emboldened**");
        assert_eq!(
            suffix_non_whitespace("emboldened matthew", "**"),
            "emboldened matthew**"
        );
        assert_eq!(
            suffix_non_whitespace("emboldened \nmatthew\n \n", "**"),
            "emboldened \nmatthew**\n \n"
        );
    }
}