1use crate::errors::UpError;
4use crate::UP_BUNDLE_ID;
5use camino::Utf8Path;
6use camino::Utf8PathBuf;
7use color_eyre::eyre::eyre;
8use color_eyre::eyre::Context;
9use color_eyre::Result;
10use std::fs;
11use std::fs::File;
12use std::fs::OpenOptions;
13use std::os::unix::fs::OpenOptionsExt;
14use tracing::trace;
15use tracing::warn;
16
17const EMPTY_HOME_DIR: &str = "/var/empty";
23
24pub fn home_dir() -> Result<Utf8PathBuf> {
26 let home_dir = dirs::home_dir()
27 .ok_or_else(|| eyre!("Expected to be able to calculate the user's home directory."))?;
28 let home_dir = Utf8PathBuf::try_from(home_dir)?;
29 if home_dir == EMPTY_HOME_DIR {
30 warn!(
31 "User home directory appears to be set to {EMPTY_HOME_DIR}. This is likely to cause \
32 issues with program execution."
33 );
34 }
35 Ok(home_dir)
36}
37
38pub fn log_dir() -> Result<Utf8PathBuf> {
40 Ok(home_dir()?.join("Library/Logs").join(UP_BUNDLE_ID))
41}
42
43pub(crate) fn parent(path: &Utf8Path) -> Result<&Utf8Path> {
45 path.parent()
46 .ok_or_else(|| eyre!("Didn't expect path {path} to be the root directory."))
47}
48
49pub fn to_utf8_path(path: &std::path::Path) -> Result<&Utf8Path> {
52 Utf8Path::from_path(path).ok_or_else(|| eyre!("Invalid UTF-8 in path {path:?}"))
53}
54
55pub fn remove_broken_symlink(path: &Utf8Path) -> Result<(), UpError> {
63 warn!(
64 "Removing existing broken symlink.\n Path: {path}\n Dest: {dest}",
65 dest = &path.read_link_utf8().map_err(|e| UpError::IoError {
66 path: path.to_owned(),
67 source: e
68 })?
69 );
70 fs::remove_file(path).map_err(|e| UpError::DeleteError {
71 path: path.to_owned(),
72 source: e,
73 })?;
74
75 Ok(())
76}
77
78pub fn create(file_path: &Utf8Path, mode: Option<u32>) -> Result<File> {
81 trace!("Ensuring file exists: {file_path}");
82 create_dir_all(parent(file_path)?)?;
83 let mode = mode.unwrap_or(0o666);
84
85 let file = OpenOptions::new()
86 .write(true)
87 .create(true)
88 .mode(mode)
90 .open(file_path)
91 .wrap_err_with(|| eyre!("Failed to create file at {file_path}"))?;
92
93 Ok(file)
94}
95
96pub fn create_dir_all(path: impl AsRef<Utf8Path>) -> Result<()> {
98 let path = path.as_ref();
99 trace!("Ensuring that directory path exists: {path}");
100 fs::create_dir_all(path).wrap_err_with(|| eyre!("Failed to create directory {path}"))
101}
102
103pub(crate) fn write(path: impl AsRef<Utf8Path>, contents: impl AsRef<[u8]>) -> Result<()> {
105 let path = path.as_ref();
106 trace!("Writing data to {path}");
107 fs::write(path, contents).wrap_err_with(|| eyre!("Failed to write to file {path}"))
108}