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
148
149
150
151
152
153
154
155
156
157
158
159
160
//! The line-ending policy: the style an output document is written with.
//!
//! [`detect_line_ending`] counts the document's line feed and carriage return
//! and line feed (CRLF) endings and selects the style holding the strict
//! majority, and [`serialize_lines`] re-terminates every reformatted line with
//! that style. Detection is per document, so no transform module needs to know
//! which terminator the source file uses.
//!
//! Everything here is a pure query over the text it is given, and nothing in
//! this module emits. The I/O boundary that formats a document reports the
//! decision afterwards: the rewrite helpers in `src/io/replace.rs` and the
//! executable's input/output boundaries.
//!
//! The rationale, the rejected alternatives and the known limitations are
//! recorded in `docs/adrs/0007-line-ending-detection.md`.
/// The line-ending style of a document.
///
/// Rewriting preserves the style that dominates the input so that a file
/// authored on Windows is not silently rewritten to line feeds, which would
/// otherwise show up as a whole-file diff that changes no Markdown content.
/// Selects the line-ending style holding a strict majority of `text`'s line
/// endings.
///
/// CRLF pairs are counted first and then subtracted from the total line-feed
/// count to obtain the lone line feeds. Counting line feeds directly would
/// count every CRLF twice and leave CRLF unable to win.
/// [`LineEnding::Crlf`] is selected only when CRLF pairs strictly outnumber
/// lone line feeds.
///
/// Text with an exact tie and text with no line endings at all both select
/// [`LineEnding::Lf`], so the result is deterministic and never depends on
/// which style happens to appear first.
///
/// Only CRLF and LF are recognized. A lone carriage return is content rather
/// than a line ending, matching the `str::lines` split used to read the
/// document.
///
/// # Examples
///
/// ```rust
/// use mdtablefix::{LineEnding, detect_line_ending};
///
/// assert_eq!(detect_line_ending("alpha\nbeta\n"), LineEnding::Lf);
/// assert_eq!(detect_line_ending("alpha\r\nbeta\r\n"), LineEnding::Crlf);
/// assert_eq!(detect_line_ending("alpha\r\nbeta\n"), LineEnding::Lf);
/// assert_eq!(detect_line_ending("alpha"), LineEnding::Lf);
/// ```
/// The line-ending counts of a document, and the style they select.
///
/// [`count_line_endings`] returns this so a caller can report or act on how
/// one-sided the majority vote was, rather than only on its outcome.
/// Counts `text`'s line endings and selects the majority style.
///
/// This is [`detect_line_ending`] with the counts that decided it, so a caller
/// that reports or acts on the vote does not have to restate the counting
/// rule. CRLF pairs are counted first and subtracted from the total line-feed
/// count to obtain the lone line feeds.
///
/// # Examples
///
/// ```rust
/// use mdtablefix::{LineEnding, count_line_endings};
///
/// let counts = count_line_endings("alpha\r\nbeta\r\ngamma\n");
///
/// assert_eq!(counts.ending, LineEnding::Crlf);
/// assert_eq!(counts.crlf_count, 2);
/// assert_eq!(counts.lone_lf_count, 1);
/// ```
/// Renders `lines` as one document whose lines end with `ending`.
///
/// An empty slice yields an empty string. Otherwise every line is followed by
/// `ending`, so a non-empty result always carries one trailing terminator.
///
/// # Examples
///
/// ```rust
/// use mdtablefix::{LineEnding, serialize_lines};
///
/// let lines = vec!["| A |".to_string(), "| 1 |".to_string()];
///
/// assert_eq!(serialize_lines(&lines, LineEnding::Lf), "| A |\n| 1 |\n");
/// assert_eq!(
/// serialize_lines(&lines, LineEnding::Crlf),
/// "| A |\r\n| 1 |\r\n"
/// );
/// assert_eq!(serialize_lines(&[], LineEnding::Crlf), "");
/// ```