use a `LinesBytes` struct and test it

pull/1962/head
amrbashir 9 months ago
parent a74539f994
commit ffb52e8ec2
No known key found for this signature in database
GPG Key ID: BBD7A47A2003FF33

@ -419,30 +419,20 @@ pub async fn read_text_file_lines_next<R: Runtime>(
let lines = resource_table.get::<StdLinesResource>(rid)?; let lines = resource_table.get::<StdLinesResource>(rid)?;
let ret = StdLinesResource::with_lock(&lines, |lines| -> CommandResult<Vec<u8>> { let ret = StdLinesResource::with_lock(&lines, |lines| -> CommandResult<Vec<u8>> {
let mut buf = Vec::new(); // This is an optimization to include wether we finished iteration or not (1 or 0)
// This is an optimization over `BufReader::lines` so we can use `tauri::ipc::Response`
// and also include wether we finished iteration or not (1 or 0)
// at the end of returned vector so we can use `tauri::ipc::Response` // at the end of returned vector so we can use `tauri::ipc::Response`
// and avoid serialization overhead of separate values. // and avoid serialization overhead of separate values.
match lines.read_until(b'\n', &mut buf) { match lines.next() {
Ok(0) => { Some(Ok(mut bytes)) => {
resource_table.close(rid)?; bytes.push(false as u8);
buf.push(true as u8); Ok(bytes)
}
// retain same behavior as `BufReader::lines` and `Lines` iterator
Err(_) | Ok(_) => {
if buf.last() == Some(&b'\n') {
buf.pop();
if buf.last() == Some(&b'\r') {
buf.pop();
} }
} Some(Err(_)) => Ok(vec![false as u8]),
buf.push(false as u8); None => {
resource_table.close(rid)?;
Ok(vec![true as u8])
} }
} }
Ok(buf)
}); });
ret.map(tauri::ipc::Response::new) ret.map(tauri::ipc::Response::new)
@ -1030,14 +1020,38 @@ impl StdFileResource {
impl Resource for StdFileResource {} impl Resource for StdFileResource {}
struct StdLinesResource(Mutex<BufReader<File>>); /// Same as [std::io::Lines] but with bytes
struct LinesBytes<T: BufRead>(T);
impl<B: BufRead> Iterator for LinesBytes<B> {
type Item = std::io::Result<Vec<u8>>;
fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
let mut buf = Vec::new();
match self.0.read_until(b'\n', &mut buf) {
Ok(0) => None,
Ok(_n) => {
if buf.last() == Some(&b'\n') {
buf.pop();
if buf.last() == Some(&b'\r') {
buf.pop();
}
}
Some(Ok(buf))
}
Err(e) => Some(Err(e)),
}
}
}
struct StdLinesResource(Mutex<LinesBytes<BufReader<File>>>);
impl StdLinesResource { impl StdLinesResource {
fn new(lines: BufReader<File>) -> Self { fn new(lines: BufReader<File>) -> Self {
Self(Mutex::new(lines)) Self(Mutex::new(LinesBytes(lines)))
} }
fn with_lock<R, F: FnMut(&mut BufReader<File>) -> R>(&self, mut f: F) -> R { fn with_lock<R, F: FnMut(&mut LinesBytes<BufReader<File>>) -> R>(&self, mut f: F) -> R {
let mut lines = self.0.lock().unwrap(); let mut lines = self.0.lock().unwrap();
f(&mut lines) f(&mut lines)
} }
@ -1136,7 +1150,12 @@ fn get_stat(metadata: std::fs::Metadata) -> FileInfo {
} }
} }
#[cfg(test)]
mod test { mod test {
use std::io::{BufRead, BufReader};
use super::LinesBytes;
#[test] #[test]
fn safe_file_path_parse() { fn safe_file_path_parse() {
use super::SafeFilePath; use super::SafeFilePath;
@ -1150,4 +1169,22 @@ mod test {
Ok(SafeFilePath::Url(_)) Ok(SafeFilePath::Url(_))
)); ));
} }
#[test]
fn test_lines_bytes() {
let base = String::from("line 1\nline2\nline 3\nline 4");
let bytes = base.as_bytes();
let string1 = base.lines().collect::<String>();
let string2 = BufReader::new(bytes).lines().flatten().collect::<String>();
let string3 = LinesBytes(BufReader::new(bytes))
.flatten()
.map(|s| String::from_utf8(s))
.flatten()
.collect::<String>();
assert_eq!(string1, string2);
assert_eq!(string1, string3);
assert_eq!(string2, string3);
}
} }

Loading…
Cancel
Save