tfp/
lib.rs

1use std::{
2  fs::File,
3  io::{BufReader, Read, Write},
4  path::Path,
5};
6
7pub fn tfp(fp: impl AsRef<Path>) -> std::io::Result<String> {
8  let file = File::open(&fp)?;
9  let mut reader = BufReader::new(file);
10  let mut buffer = [0; 1]; // Read one byte at a time
11
12  let mut change = false;
13  let mut li = Vec::new();
14  let mut line = Vec::new();
15
16  loop {
17    let bytes_read = reader.read(&mut buffer)?;
18    if bytes_read == 0 {
19      break;
20    }
21
22    let c = buffer[0];
23    match c {
24      b'\n' => {
25        let t = String::from_utf8_lossy(&line);
26        let trimmed_line = t.trim_end();
27        if trimmed_line.len() != t.len() {
28          change = true;
29        }
30        li.push(trimmed_line.to_owned());
31        line.clear();
32      }
33      b'\r' => {
34        change = true;
35        let t = String::from_utf8_lossy(&line);
36        let trimmed_line = t.trim_end();
37        li.push(trimmed_line.to_owned());
38        line.clear();
39        let bytes_read = reader.read(&mut buffer)?;
40        if bytes_read > 0 {
41          let c = buffer[0];
42          if c != b'\n' {
43            // Skip the next \n
44            if c == b'\r' {
45              li.push("".into());
46            } else {
47              line.push(c);
48            }
49          }
50        }
51      }
52      _ => {
53        line.push(c);
54      }
55    }
56  }
57
58  if !line.is_empty() {
59    let t = String::from_utf8_lossy(&line);
60    let trimmed_line = t.trim_end();
61    if trimmed_line.len() != t.len() {
62      change = true;
63    }
64    li.push(trimmed_line.to_owned());
65  }
66
67  while let Some(i) = li.last() {
68    if i.is_empty() {
69      change = true;
70      li.pop();
71    } else {
72      break;
73    }
74  }
75
76  let txt = li.join("\n");
77  if change {
78    let mut file = File::create(fp)?;
79    file.write_all(txt.as_bytes())?;
80  }
81
82  Ok(txt)
83}