Skip to main content

trim_newlines/
lib.rs

1//! # trim-newlines — trim newlines from the start and end of a string
2//!
3//! Remove leading and/or trailing newline characters (`\r` and `\n` only — not other
4//! whitespace) from a string. A faithful Rust port of the widely-used
5//! [`trim-newlines`](https://www.npmjs.com/package/trim-newlines) npm package.
6//!
7//! ```
8//! use trim_newlines::{trim_newlines, trim_newlines_start, trim_newlines_end};
9//!
10//! assert_eq!(trim_newlines("\n\nfoo\n\n"), "foo");
11//! assert_eq!(trim_newlines_start("\r\nbar\r\n"), "bar\r\n");
12//! assert_eq!(trim_newlines_end("\r\nbar\r\n"), "\r\nbar");
13//! ```
14//!
15//! Only `\r` and `\n` are trimmed, so surrounding spaces or tabs are preserved. The functions
16//! return a borrowed slice of the input (no allocation).
17//!
18//! **Zero dependencies** and `#![no_std]`.
19
20#![no_std]
21#![forbid(unsafe_code)]
22#![doc(html_root_url = "https://docs.rs/trim-newlines/0.1.0")]
23
24// Compile-test the README's examples as part of `cargo test`.
25#[cfg(doctest)]
26#[doc = include_str!("../README.md")]
27struct ReadmeDoctests;
28
29fn is_newline(c: char) -> bool {
30    c == '\r' || c == '\n'
31}
32
33/// Trim newlines (`\r` and `\n`) from both ends of `string`.
34///
35/// ```
36/// # use trim_newlines::trim_newlines;
37/// assert_eq!(trim_newlines("\r\n\r\nbar\r\n"), "bar");
38/// assert_eq!(trim_newlines("  spaced  "), "  spaced  "); // spaces kept
39/// ```
40#[must_use]
41pub fn trim_newlines(string: &str) -> &str {
42    string.trim_matches(is_newline)
43}
44
45/// Trim newlines (`\r` and `\n`) from the start of `string`.
46///
47/// ```
48/// # use trim_newlines::trim_newlines_start;
49/// assert_eq!(trim_newlines_start("\n\nfoo\n\n"), "foo\n\n");
50/// ```
51#[must_use]
52pub fn trim_newlines_start(string: &str) -> &str {
53    string.trim_start_matches(is_newline)
54}
55
56/// Trim newlines (`\r` and `\n`) from the end of `string`.
57///
58/// ```
59/// # use trim_newlines::trim_newlines_end;
60/// assert_eq!(trim_newlines_end("\n\nfoo\n\n"), "\n\nfoo");
61/// ```
62#[must_use]
63pub fn trim_newlines_end(string: &str) -> &str {
64    string.trim_end_matches(is_newline)
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn both_ends() {
73        assert_eq!(trim_newlines("\n\nfoo\n\n"), "foo");
74        assert_eq!(trim_newlines("\r\n\r\nbar\r\n"), "bar");
75        assert_eq!(trim_newlines("\r\rmid\r\r"), "mid");
76        assert_eq!(trim_newlines("no"), "no");
77        assert_eq!(trim_newlines(""), "");
78    }
79
80    #[test]
81    fn start_only() {
82        assert_eq!(trim_newlines_start("\n\nfoo\n\n"), "foo\n\n");
83        assert_eq!(trim_newlines_start("\r\n\r\nbar\r\n"), "bar\r\n");
84        assert_eq!(trim_newlines_start("no"), "no");
85    }
86
87    #[test]
88    fn end_only() {
89        assert_eq!(trim_newlines_end("\n\nfoo\n\n"), "\n\nfoo");
90        assert_eq!(trim_newlines_end("\r\n\r\nbar\r\n"), "\r\n\r\nbar");
91        assert_eq!(trim_newlines_end("no"), "no");
92    }
93
94    #[test]
95    fn only_trims_newlines_not_other_whitespace() {
96        assert_eq!(trim_newlines("  spaced  "), "  spaced  ");
97        assert_eq!(trim_newlines("\tfoo\t"), "\tfoo\t");
98        // A space between newlines stops the trim.
99        assert_eq!(trim_newlines("\n \nx\n \n"), " \nx\n ");
100        assert_eq!(trim_newlines_start("\n \nx\n \n"), " \nx\n \n");
101        assert_eq!(trim_newlines_end("\n \nx\n \n"), "\n \nx\n ");
102    }
103
104    #[test]
105    fn all_newlines() {
106        assert_eq!(trim_newlines("\n\r\n\r"), "");
107        assert_eq!(trim_newlines_start("\n\r\n\r"), "");
108        assert_eq!(trim_newlines_end("\n\r\n\r"), "");
109    }
110}