mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-28 20:07:05 -06:00
A linked smart object's path comes from the document. Every place that follows it now goes through one resolver (photocraft_io::doc_path, built on craft-docpath): a relative path is taken from the document's folder, and only local files or files on the share the document itself is on are opened. Paths to other computers, device paths and URLs are treated like a missing file: the layer keeps its last rendering. - Link watch: such links are not stamped, and no background look starts when no link of the document is followed. - Update Modified Content and other re-renders read only followed files; the error names why a link is not followed. - Package lists such links as missing instead of copying them. - Reveal in Finder for a linked layer shows only a followed file; the document's own Reveal is unchanged. The macOS invocation ends options with `--`, and Explorer is not asked to select a path containing a comma or a quotation mark. - Place Linked stores a relative path as an absolute one. The document path travels to smart-object renders in a thread-local scope entered by Session::edit, command dispatch and background jobs. Co-authored-by: David Mann <daaavid.mann@gmail.com>
59 lines
2.4 KiB
Rust
59 lines
2.4 KiB
Rust
//! Paths stored in a document reach the file system only through [`resolve`] (built on
|
|
//! [`photocraft_io::doc_path::resolve`]): linked smart objects, the Displace map and Color Lookup
|
|
//! file a smart filter re-reads, data-set images and video footage. A refused path is handled like
|
|
//! a missing file: the layer keeps its last rendering, and commands that need the file say why.
|
|
//!
|
|
//! Smart-object rendering has no `Session` argument, so the path of the document being worked on
|
|
//! travels in a thread-local scope, entered where commands run ([`crate::Session::edit`], command
|
|
//! dispatch, background jobs). Outside a scope there is no document: relative paths are refused
|
|
//! and only local absolute paths are followed.
|
|
|
|
use std::cell::RefCell;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
pub use photocraft_io::doc_path::NotFollowed;
|
|
|
|
thread_local! {
|
|
static DOCUMENT: RefCell<Option<PathBuf>> = const { RefCell::new(None) };
|
|
}
|
|
|
|
/// Restores the previous document path when dropped.
|
|
pub(crate) struct DocumentScope(Option<PathBuf>);
|
|
|
|
impl Drop for DocumentScope {
|
|
fn drop(&mut self) {
|
|
let previous = self.0.take();
|
|
DOCUMENT.with(|d| *d.borrow_mut() = previous);
|
|
}
|
|
}
|
|
|
|
/// Resolve stored paths against `document` (the path of the document being worked on, `None`
|
|
/// for one without a file) until the returned scope is dropped.
|
|
pub(crate) fn scope(document: Option<&Path>) -> DocumentScope {
|
|
let next = document.filter(|d| !d.as_os_str().is_empty()).map(Path::to_path_buf);
|
|
DocumentScope(DOCUMENT.with(|d| d.replace(next)))
|
|
}
|
|
|
|
/// The document path of the current scope (to carry it to another thread).
|
|
pub(crate) fn current() -> Option<PathBuf> {
|
|
DOCUMENT.with(|d| d.borrow().clone())
|
|
}
|
|
|
|
/// The file to open for `stored`, a path stored in the document of the current scope.
|
|
pub fn resolve(stored: &str) -> Result<PathBuf, NotFollowed> {
|
|
resolve_in(stored, current().as_deref())
|
|
}
|
|
|
|
/// The file to open for `stored`, a path stored in the document saved at `document`.
|
|
pub fn resolve_in(stored: &str, document: Option<&Path>) -> Result<PathBuf, NotFollowed> {
|
|
photocraft_io::doc_path::resolve(stored, document)
|
|
}
|
|
|
|
/// The message for a stored path that is not followed.
|
|
pub(crate) fn refusal(what: &str, stored: &str, why: NotFollowed) -> crate::EngineError {
|
|
crate::EngineError::Other(format!("{what} {stored} is not opened: {why}"))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|