4 stable releases
new 1.2.0 | Feb 19, 2025 |
---|---|
1.1.0 | Feb 18, 2025 |
1.0.1 | Feb 18, 2025 |
#3 in #ensure
125 downloads per month
Used in 2 crates
(via string-auto-indent)
28KB
272 lines
Rust Line Endings
OS | Status |
---|---|
Ubuntu-latest | |
macOS-latest | |
Windows-latest |
A Rust crate to detect, normalize, and convert line endings across platforms. Ensures consistent handling of LF
, CRLF
, and CR
line endings in text processing.
Install
cargo add line-ending
Usage
Split into Multiple Strings
Split a string into a vector of strings using the auto-detected line ending parsed from the string.
use line_ending::LineEnding;
let crlf = LineEnding::split("first\r\nsecond\r\nthird");
let cr = LineEnding::split("first\rsecond\rthird");
let lf = LineEnding::split("first\nsecond\nthird");
let expected = vec!["first", "second", "third"];
assert_eq!(crlf, expected);
assert_eq!(cr, expected);
assert_eq!(lf, expected);
Join Multiple Strings into a Single String
Join a vector of strings using the specified line ending.
use line_ending::LineEnding;
let lines = vec![
"first".to_string(),
"second".to_string(),
"third".to_string(),
];
assert_eq!(
LineEnding::CRLF.join(lines.clone()),
"first\r\nsecond\r\nthird"
);
assert_eq!(
LineEnding::CR.join(lines.clone()),
"first\rsecond\rthird"
);
assert_eq!(
LineEnding::LF.join(lines.clone()),
"first\nsecond\nthird"
);
Change Line Ending Type
Apply a specific line ending type to an existing string.
use line_ending::LineEnding;
let mixed_text = "first line\r\nsecond line\rthird line\nfourth line\n";
assert_eq!(
LineEnding::CRLF.apply(mixed_text),
"first line\r\nsecond line\r\nthird line\r\nfourth line\r\n"
);
assert_eq!(
LineEnding::CR.apply(mixed_text),
"first line\rsecond line\rthird line\rfourth line\r"
);
assert_eq!(
LineEnding::LF.apply(mixed_text),
"first line\nsecond line\nthird line\nfourth line\n"
);
Auto-identify Line Ending Type
Detect the predominant line ending style used in the input string.
use line_ending::LineEnding;
let crlf = "first line\r\nsecond line\r\nthird line";
let cr = "first line\rsecond line\rthird line";
let lf = "first line\nsecond line\nthird line";
assert_eq!(LineEnding::from(crlf), LineEnding::CRLF);
assert_eq!(LineEnding::from(cr), LineEnding::CR);
assert_eq!(LineEnding::from(lf), LineEnding::LF);
Normalize
Convert all line endings in a string to LF
(\n
) for consistent processing.
use line_ending::LineEnding;
let crlf = "first\r\nsecond\r\nthird";
let cr = "first\rsecond\rthird";
let lf = "first\nsecond\nthird";
assert_eq!(LineEnding::normalize(crlf), lf);
assert_eq!(LineEnding::normalize(cr), lf);
assert_eq!(LineEnding::normalize(lf), lf);
Denormalize
Restore line endings in a string to the specified type.
use line_ending::LineEnding;
let lf = "first\nsecond\nthird";
let crlf_restored = LineEnding::CRLF.denormalize(lf);
let cr_restored = LineEnding::CR.denormalize(lf);
let lf_restored = LineEnding::LF.denormalize(lf);
assert_eq!(crlf_restored, "first\r\nsecond\r\nthird");
assert_eq!(cr_restored, "first\rsecond\rthird");
assert_eq!(lf_restored, "first\nsecond\nthird");
Handling Mixed-Type Line Endings
When a string contains multiple types of line endings (LF
, CRLF
, and CR
), the LineEnding::from
method will detect the most frequent line ending type and return it as the dominant one. This ensures a consistent approach to mixed-line-ending detection.
use line_ending::LineEnding;
let mixed_type = "line1\nline2\r\nline3\nline4\nline5\r\n";
assert_eq!(LineEnding::from(mixed_type), LineEnding::LF); // `LF` is the most common
The detection algorithm works as follows:
- Counts occurrences of each line ending type (
LF
,CRLF
,CR
). - Selects the most frequent one as the detected line ending.
- Defaults to
CRLF
if all are equally present or if the input is empty.
Edge Cases & Examples
Case 1: One Line Ending Type is Clearly Dominant
use line_ending::LineEnding;
let mostly_crlf = "line1\r\nline2\r\nline3\nline4\r\nline5\r\n";
assert_eq!(LineEnding::from(mostly_crlf), LineEnding::CRLF); // `CRLF` is the most common
let mostly_cr = "line1\rline2\rline3\nline4\rline5\r";
assert_eq!(LineEnding::from(mostly_cr), LineEnding::CR); // `CR` is the most common
Case 2: All Line Endings Appear Equally
If LF
, CRLF
, and CR
all appear the same number of times, the function will return CRLF
as a tie-breaker.
use line_ending::LineEnding;
let equal_mixed = "line1\r\nline2\nline3\rline4\r\nline5\nline6\r";
assert_eq!(LineEnding::from(equal_mixed), LineEnding::CRLF); // `CRLF` > `CR` > `LF`
CRLF
is chosen as a tie-breaker because it represents both CR
and LF
, making it the most inclusive option.
Case 3: Single Line Containing Multiple Line Endings
If a single line contains different line endings, the function still chooses the most frequent across the entire string.
use line_ending::LineEnding;
let mixed_on_one_line = "line1\r\nline2\rline3\r\nline4\r\nline5\r";
assert_eq!(LineEnding::from(mixed_on_one_line), LineEnding::CRLF); // `CRLF` appears the most overall
Case 4: Empty Input Defaults to CRLF
use line_ending::LineEnding;
let empty_text = "";
assert_eq!(LineEnding::from(empty_text), LineEnding::CRLF); // Defaults to `CRLF`
Additional Mixed-Type Code Examples
Counting Mixed Types
Count occurrences of each line ending type in the given string.
use line_ending::{LineEnding, LineEndingScores};
// `LineEndingScores` is a hash map that associates each line ending type with
// its occurrence count.
let mostly_lf = "line1\nline2\r\nline3\rline4\nline5\nline6\n";
assert_eq!(LineEnding::from(mostly_lf), LineEnding::LF);
assert_eq!(
LineEnding::score_mixed_types(mostly_lf,),
[
(LineEnding::CRLF, 1),
(LineEnding::CR, 1),
(LineEnding::LF, 4),
]
.into_iter()
.collect::<LineEndingScores>()
);
Split as a Specific Type
If you want to forcefully split by a certain type.
use line_ending::{LineEnding};
let mostly_lf = "line1\nline2\r\nline3\rline4\nline5\nline6\n";
let split_crlf = LineEnding::CRLF.split_with(mostly_lf);
assert_eq!(split_crlf, vec!["line1\nline2", "line3\rline4\nline5\nline6\n"]);
Escaped vs. Actual Line Endings
Rust treats \\n
as a literal sequence rather than an actual newline. This behavior ensures that escaped sequences are not mistakenly interpreted as real line breaks.
For example:
use line_ending::LineEnding;
let lf_with_escaped = "First\\nSecond\nThird";
let result = LineEnding::split(lf_with_escaped);
assert_eq!(result, vec!["First\\nSecond", "Third"]); // Escaped `\\n` remains intact
let lf = "First\nSecond\nThird";
let result_actual = LineEnding::split(lf);
assert_eq!(result_actual, vec!["First", "Second", "Third"]); // Actual `\n` splits
License
Licensed under MIT. See LICENSE
for details.
Dependencies
~9KB