1use crate::Error;
2#[cfg(not(windows))]
3use crate::output_parent;
4#[cfg(windows)]
5use crate::windows::write_atomic;
6#[cfg(not(windows))]
7use std::io::Write;
8use std::{fs::File, io::Read, path::Path};
9
10pub fn read_bounded(path: &Path, limit: usize) -> Result<Vec<u8>, Error> {
11 let file = File::open(path).map_err(|cause| Error::Context {
12 message: format!("open {}", path.display()),
13 cause: Box::new(Error::Io(cause)),
14 })?;
15 let mut bytes = Vec::new();
16 read_into(file, limit, &mut bytes)?;
17 Ok(bytes)
18}
19
20pub(crate) fn read_into(input: impl Read, limit: usize, bytes: &mut Vec<u8>) -> Result<(), Error> {
21 let bound = u64::try_from(limit)
22 .map_err(Error::Integer)?
23 .checked_add(1)
24 .ok_or(Error::LimitOverflow)?;
25 input.take(bound).read_to_end(bytes)?;
26 if bytes.len() > limit {
27 return Err(Error::TooLarge { limit });
28 }
29 Ok(())
30}
31
32pub fn write_new(path: &Path, bytes: &[u8]) -> Result<(), Error> {
33 write_atomic(path, bytes, false)
34}
35
36pub fn write_replace(path: &Path, bytes: &[u8]) -> Result<(), Error> {
37 write_atomic(path, bytes, true)
38}
39
40#[cfg(not(windows))]
41fn write_atomic(path: &Path, bytes: &[u8], replace: bool) -> Result<(), Error> {
42 let parent = output_parent(path);
43 let mut temporary = tempfile::NamedTempFile::new_in(parent)?;
44 temporary.write_all(bytes)?;
45 temporary.as_file().sync_all()?;
46 if replace {
47 temporary.persist(path).map_err(Error::Persist)?;
48 } else {
49 temporary
50 .persist_noclobber(path)
51 .map_err(|cause| Error::Context {
52 message: format!("create {} without overwrite", path.display()),
53 cause: Box::new(Error::Persist(cause)),
54 })?;
55 }
56 sync_directory(parent)?;
57 Ok(())
58}
59
60#[cfg(not(windows))]
61pub fn sync_directory(path: &Path) -> Result<(), std::io::Error> {
62 File::open(path)?.sync_all()
63}