simple_fs/span/
read_span.rs

1use crate::{Error, Result, SPath, open_file};
2use std::fs::File;
3use std::io::{self, ErrorKind};
4
5#[cfg(unix)]
6use std::os::unix::fs::FileExt as _;
7#[cfg(windows)]
8use std::os::windows::fs::FileExt as _;
9
10/// Read a (start,end) half-open span and return a string.
11pub fn read_span(path: impl AsRef<SPath>, start: usize, end: usize) -> Result<String> {
12	let len = end.checked_sub(start).ok_or(Error::SpanInvalidStartAfterEnd)?;
13
14	let path = path.as_ref();
15	let file = open_file(path)?;
16
17	let res = read_exact_at(&file, start as u64, len).map_err(|err| Error::FileCantRead((path, err).into()))?;
18
19	let txt = String::from_utf8(res).map_err(|_| Error::SpanInvalidUtf8)?;
20
21	Ok(txt)
22}
23
24// region:    --- Support
25
26/// Read exactly `len` bytes starting at absolute file offset `offset` into a Vec.
27fn read_exact_at(file: &File, offset: u64, len: usize) -> io::Result<Vec<u8>> {
28	let mut buf = vec![0u8; len];
29	let mut filled = 0usize;
30
31	while filled < len {
32		#[cfg(unix)]
33		let n = file.read_at(&mut buf[filled..], offset + filled as u64)?;
34		#[cfg(windows)]
35		let n = file.seek_read(&mut buf[filled..], offset + filled as u64)?;
36
37		if n == 0 {
38			return Err(io::Error::new(
39				ErrorKind::UnexpectedEof,
40				"span exceeds file size (hit EOF)",
41			));
42		}
43		filled += n;
44	}
45	Ok(buf)
46}
47
48// endregion: --- Support