punktf_lib/profile/
transform.rs1use std::fmt;
11
12use color_eyre::Result;
13
14pub trait Transform {
20 fn transform(&self, content: String) -> Result<String>;
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
33#[serde(deny_unknown_fields)]
34pub enum ContentTransformer {
35 LineTerminator(LineTerminator),
38}
39
40impl Transform for ContentTransformer {
41 fn transform(&self, content: String) -> Result<String> {
42 match self {
43 Self::LineTerminator(lt) => lt.transform(content),
44 }
45 }
46}
47
48impl fmt::Display for ContentTransformer {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
50 fmt::Display::fmt(&self, f)
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
57pub enum LineTerminator {
58 LF,
60
61 CRLF,
63}
64
65impl Transform for LineTerminator {
66 fn transform(&self, mut content: String) -> Result<String> {
67 match self {
68 Self::LF => Ok(content.replace("\r\n", "\n")),
69 Self::CRLF => {
70 let lf_idxs = content.match_indices('\n');
71 let mut cr_idxs = content.match_indices('\r').peekable();
72
73 #[allow(clippy::needless_collect)]
77 let lf_idxs = lf_idxs
78 .filter_map(|(lf_idx, _)| {
79 while matches!(cr_idxs.peek(), Some((cr_idx,_)) if cr_idx + 1 < lf_idx) {
80 let _ = cr_idxs.next().expect("Failed to advance peeked iterator");
82 }
83
84 if matches!(cr_idxs.peek(), Some((cr_idx, _)) if cr_idx + 1 == lf_idx) {
85 let _ = cr_idxs.next().expect("Failed to advance peeked iterator");
87 None
88 } else {
89 Some(lf_idx)
90 }
91 })
92 .collect::<Vec<_>>();
93
94 for (offset, lf_idx) in lf_idxs.into_iter().enumerate() {
95 content.insert(lf_idx + offset, '\r');
96 }
97
98 Ok(content)
99 }
100 }
101 }
102}
103
104impl fmt::Display for LineTerminator {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
106 fmt::Debug::fmt(&self, f)
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use pretty_assertions::assert_eq;
113
114 use super::*;
115
116 #[test]
117 fn line_terminator_lf() -> Result<()> {
118 const CONTENT: &str = "Hello\r\nWorld\nHow\nare\r\nyou today?\r\r\r\nLast line\r\\n";
119
120 assert_eq!(
121 LineTerminator::LF.transform(String::from(CONTENT))?,
122 "Hello\nWorld\nHow\nare\nyou today?\r\r\nLast line\r\\n"
123 );
124
125 Ok(())
126 }
127
128 #[test]
129 fn line_terminator_crlf() -> Result<()> {
130 const CONTENT: &str = "Hello\r\nWorld\nHow\nare\r\nyou today?\r\r\r\nLast line\r\\n";
131
132 assert_eq!(
133 LineTerminator::CRLF.transform(String::from(CONTENT))?,
134 "Hello\r\nWorld\r\nHow\r\nare\r\nyou today?\r\r\r\nLast line\r\\n"
135 );
136
137 Ok(())
138 }
139}