zebra_chain/common.rs
1//! Common functions used in Zebra.
2
3use std::{
4 ffi::OsString,
5 fs,
6 io::{self, Write},
7 path::PathBuf,
8};
9
10use tempfile::PersistError;
11
12/// Returns Zebra's default cache directory path.
13pub fn default_cache_dir() -> PathBuf {
14 dirs::cache_dir()
15 .unwrap_or_else(|| std::env::current_dir().unwrap().join("cache"))
16 .join("zebra")
17}
18
19/// Accepts a target file path and a byte-slice.
20///
21/// Atomically writes the byte-slice to a file to avoid corrupting the file if Zebra
22/// panics, crashes, or exits while the file is being written, or if multiple Zebra instances
23/// try to read and write the same file.
24///
25/// Returns the provided file path if successful.
26///
27/// # Concurrency
28///
29/// This function blocks on filesystem operations and should be called in a blocking task
30/// when calling from an async environment.
31///
32/// # Panics
33///
34/// If the provided `file_path` is a directory path.
35pub fn atomic_write(
36 file_path: PathBuf,
37 data: &[u8],
38) -> io::Result<Result<PathBuf, PersistError<fs::File>>> {
39 // Get the file's parent directory, or use Zebra's default cache directory
40 let file_dir = file_path
41 .parent()
42 .map(|p| p.to_owned())
43 .unwrap_or_else(default_cache_dir);
44
45 // Create the directory if needed.
46 fs::create_dir_all(&file_dir)?;
47
48 // Give the temporary file a similar name to the permanent file,
49 // but hide it in directory listings.
50 let mut tmp_file_prefix: OsString = ".tmp.".into();
51 tmp_file_prefix.push(
52 file_path
53 .file_name()
54 .expect("file path must have a file name"),
55 );
56
57 // Create the temporary file in the same directory as the permanent file,
58 // so atomic filesystem operations are possible.
59 let mut tmp_file = tempfile::Builder::new()
60 .prefix(&tmp_file_prefix)
61 .tempfile_in(file_dir)?;
62
63 tmp_file.write_all(data)?;
64
65 // Atomically write the temp file to `file_path`.
66 let persist_result = tmp_file
67 .persist(&file_path)
68 // Drops the temp file and returns the file path.
69 .map(|_| file_path);
70 Ok(persist_result)
71}