From 95a80c954c443c26d829de34afb1da37754c7daf Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Wed, 28 Oct 2026 19:48:02 -0400 Subject: [PATCH] LRC-LIB-FOLDERS: the sidebar reads the home folder through the engine's helper, so its menu and the rename/move guard agree --- crates/engine/src/cmd/browse.rs | 8 +------ crates/engine/src/library.rs | 31 +++++++++++++++++++++++++ crates/ui-egui/src/panels/left/local.rs | 5 ++-- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/crates/engine/src/cmd/browse.rs b/crates/engine/src/cmd/browse.rs index dd47fcaf5..23c9c06e6 100644 --- a/crates/engine/src/cmd/browse.rs +++ b/crates/engine/src/cmd/browse.rs @@ -571,15 +571,9 @@ pub(crate) fn is_protected_place(path: &Path, home: &Path) -> bool { .any(|p| key(&p) == path) } -/// The user's home folder; `None` when the host has none. -fn home_folder() -> Option { - let home = if cfg!(windows) { std::env::var_os("USERPROFILE") } else { std::env::var_os("HOME") }?; - Some(home.into()).filter(|h: &std::path::PathBuf| !h.as_os_str().is_empty()) -} - /// Refuses `from` when renaming or moving it would take the home folder or a standard place of it. fn refuse_protected_place(command: &str, from: &str) -> Result<()> { - refuse_protected_place_in(command, from, home_folder().as_deref()) + refuse_protected_place_in(command, from, crate::library::home_folder().as_deref()) } /// What `from` names once the file system has read it: made absolute against the working folder diff --git a/crates/engine/src/library.rs b/crates/engine/src/library.rs index a5071b4b3..3b99e6e55 100644 --- a/crates/engine/src/library.rs +++ b/crates/engine/src/library.rs @@ -68,6 +68,37 @@ pub fn default_dir() -> Option { Some(PathBuf::from(home).join("Pictures").join(DEFAULT_NAME)) } +/// The user's home folder (`%USERPROFILE%` on Windows, `$HOME` elsewhere); `None` when the host has +/// none or it is empty. The one place the home path is read for Local's built-in places, in the +/// engine's rename/move guard and in the sidebar. +pub fn home_folder() -> Option { + home_folder_from(cfg!(windows), |name| std::env::var_os(name)) +} + +fn home_folder_from(windows: bool, var: impl Fn(&str) -> Option) -> Option { + var(if windows { "USERPROFILE" } else { "HOME" }).filter(|h| !h.is_empty()).map(PathBuf::from) +} + +#[cfg(test)] +mod home_tests { + use super::home_folder_from; + use std::ffi::OsString; + + #[test] + fn windows_reads_userprofile_and_the_rest_read_home() { + let env = |name: &str| match name { + "HOME" => Some(OsString::from("/home/example")), + "USERPROFILE" => Some(OsString::from("C:\\Users\\example")), + _ => None, + }; + assert_eq!(home_folder_from(true, env).as_deref(), Some(std::path::Path::new("C:\\Users\\example"))); + assert_eq!(home_folder_from(false, env).as_deref(), Some(std::path::Path::new("/home/example"))); + // only the other platform's variable is set: no home, never a guess from it + assert_eq!(home_folder_from(true, |n| (n == "HOME").then(|| OsString::from("/home/example"))), None); + assert_eq!(home_folder_from(false, |_| Some(OsString::new())), None); + } +} + pub struct Library { /// The library directory, or a descriptive pseudo-path when the library isn't on disk. pub dir: PathBuf, diff --git a/crates/ui-egui/src/panels/left/local.rs b/crates/ui-egui/src/panels/left/local.rs index 3963fe0ee..5e43ab420 100644 --- a/crates/ui-egui/src/panels/left/local.rs +++ b/crates/ui-egui/src/panels/left/local.rs @@ -51,9 +51,10 @@ pub(super) fn local_now(app: &mut LightcraftApp, ui: &egui::Ui) -> Option