mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-14 19:27:01 -04:00
Library: a library's own file names its folder; any other file is a clear error
`lightcraft-cli run --library <dir>/catalog.snap` failed with the OS's "File exists" (creating a folder over the file). Session::open_library now opens the folder that holds catalog.snap / catalog.log / catalog.lock, and says "is a file; a library is a folder" for any other file. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
0ba118de92
commit
457c899160
1 file changed
+59
-1
@@ -32,6 +32,32 @@ use crate::{EngineError, LibrarySource, Result, Selection, Session};
|
||||
/// Library directory name inside the user's Pictures folder.
|
||||
pub const DEFAULT_NAME: &str = "LightCraft Library";
|
||||
|
||||
/// The library folder `path` names. A library is a folder; naming one of its own files
|
||||
/// (`catalog.snap`, `catalog.log`, `catalog.lock`) means the folder that holds it. Any other
|
||||
/// existing file is an error that says so, instead of the OS's "File exists" from creating the
|
||||
/// folder.
|
||||
pub fn resolve_dir(path: &Path) -> Result<PathBuf> {
|
||||
if !path.is_file() {
|
||||
return Ok(path.to_path_buf());
|
||||
}
|
||||
let own = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.is_some_and(|n| [lightcraft_catalog::journal::SNAPSHOT, lightcraft_catalog::journal::LOG, lightcraft_catalog::lock::LOCK].contains(&n));
|
||||
if !own {
|
||||
return Err(EngineError::Other(format!("{} is a file; a library is a folder", path.display())));
|
||||
}
|
||||
Ok(match path.parent().filter(|p| !p.as_os_str().is_empty()) {
|
||||
Some(dir) => dir.to_path_buf(),
|
||||
None => PathBuf::from("."),
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether `dir` already holds a library (its catalog files).
|
||||
pub fn is_library(dir: &Path) -> bool {
|
||||
dir.join(lightcraft_catalog::journal::SNAPSHOT).is_file() || dir.join(lightcraft_catalog::journal::LOG).is_file()
|
||||
}
|
||||
|
||||
/// The default library location: `$LIGHTCRAFT_LIBRARY` if set, else `~/Pictures/LightCraft Library`
|
||||
/// (`%USERPROFILE%\Pictures\LightCraft Library` on Windows).
|
||||
pub fn default_dir() -> Option<PathBuf> {
|
||||
@@ -253,7 +279,7 @@ impl Session {
|
||||
/// has it open, this fails with [`EngineError::LibraryInUse`] and nothing is read or changed.
|
||||
/// Reopening the library this session already has open keeps its lock.
|
||||
pub fn open_library(&mut self, dir: impl AsRef<Path>, seed_demo: bool) -> Result<&LoadReport> {
|
||||
let dir = dir.as_ref().to_path_buf();
|
||||
let dir = resolve_dir(dir.as_ref())?;
|
||||
let open = || FsStore::open(&dir).map_err(|e| EngineError::Other(format!("can't open library {}: {e}", dir.display())));
|
||||
let stores = LibraryStores { catalog: Box::new(open()?), files: Box::new(open()?), on_disk: true, dir: dir.clone() };
|
||||
// the library open in this session (same directory): hand its lock over
|
||||
@@ -655,3 +681,35 @@ fn unlocked_warning(lock: Option<&LibraryLock>) -> Option<String> {
|
||||
.to_string()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod resolve_tests {
|
||||
use super::*;
|
||||
|
||||
/// `--library <dir>/catalog.snap` failed with the OS's "File exists" (creating a folder over
|
||||
/// the file); it opens the library that file belongs to.
|
||||
#[test]
|
||||
fn a_librarys_own_file_opens_its_folder_and_any_other_file_is_a_clear_error() {
|
||||
let dir = std::env::temp_dir().join(format!("lc-resolve-lib-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let mut s = Session::new().with_fs();
|
||||
s.open_library(&dir, false).unwrap();
|
||||
s.close_library().unwrap();
|
||||
drop(s);
|
||||
assert!(is_library(&dir));
|
||||
let snap = dir.join(lightcraft_catalog::journal::SNAPSHOT);
|
||||
assert_eq!(resolve_dir(&snap).unwrap(), dir);
|
||||
let mut s = Session::new().with_fs();
|
||||
s.open_library(&snap, false).unwrap();
|
||||
assert_eq!(s.library.as_ref().map(|l| l.dir.clone()), Some(dir.clone()));
|
||||
drop(s);
|
||||
|
||||
let other = dir.join("notes.txt");
|
||||
std::fs::write(&other, b"x").unwrap();
|
||||
let e = Session::new().with_fs().open_library(&other, false).err().unwrap().to_string();
|
||||
assert!(e.contains("is a file"), "{e}");
|
||||
assert!(!e.contains("File exists"), "{e}");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user