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
use std::io::Read;

pub fn read(mut reader: impl Read) -> (String, bool) {
  let mut buffer = [0; 1]; // Read one byte at a time

  let mut change = false;
  let mut li = Vec::new();
  let mut line = Vec::new();

  while let Ok(bytes_read) = xerr::ok!(reader.read(&mut buffer)) {
    if bytes_read == 0 {
      break;
    }

    let c = buffer[0];
    match c {
      b'\n' => {
        let t = String::from_utf8_lossy(&line);
        let trimmed_line = t.trim_end();
        if trimmed_line.len() != t.len() {
          change = true;
        }
        li.push(trimmed_line.to_owned());
        line.clear();
      }
      b'\r' => {
        change = true;
        let t = String::from_utf8_lossy(&line);
        let trimmed_line = t.trim_end();
        li.push(trimmed_line.to_owned());
        line.clear();
        if let Ok(bytes_read) = xerr::ok!(reader.read(&mut buffer)) {
          if bytes_read > 0 {
            let c = buffer[0];
            if c != b'\n' {
              // Skip the next \n
              if c == b'\r' {
                li.push("".into());
              } else {
                line.push(c);
              }
            }
          }
        }
      }
      b'\0' => {
        // 不能包含 '\0' 因为用这个做缓存的占位符
      }
      _ => {
        line.push(c);
      }
    }
  }

  if !line.is_empty() {
    let t = String::from_utf8_lossy(&line);
    let trimmed_line = t.trim_end();
    if trimmed_line.len() != t.len() {
      change = true;
    }
    li.push(trimmed_line.to_owned());
  }

  while let Some(i) = li.last() {
    if i.is_empty() {
      change = true;
      li.pop();
    } else {
      break;
    }
  }

  let txt = li.join("\n");
  (txt, change)
}

#[cfg(feature = "fs")]
pub fn fp(fp: impl AsRef<std::path::Path>) -> std::io::Result<String> {
  use std::{
    fs::File,
    io::{BufReader, Write},
  };
  let file = File::open(&fp)?;
  let mut reader = BufReader::new(file);
  let (txt, change) = read(&mut reader);
  if change {
    let mut file = File::create(fp)?;
    file.write_all(txt.as_bytes())?;
  }

  Ok(txt)
}

#[cfg(feature = "str")]
pub fn str(txt: impl AsRef<str>) -> (String, bool) {
  use std::io::Cursor;
  let txt = txt.as_ref();
  read(Cursor::new(txt))
}