jag_surface/lib.rs
1//! jag-surface: Canvas-style API on top of jag-draw.
2#![allow(
3 clippy::collapsible_if,
4 clippy::collapsible_match,
5 clippy::too_many_arguments,
6 clippy::type_complexity
7)]
8
9mod canvas;
10pub mod shapes;
11mod surface;
12
13pub use canvas::{Canvas, ImageFitMode, RawImageDraw, ScrimDraw};
14pub use surface::{CachedFrameData, JagSurface, get_last_raw_image_rect};
15
16/// Resolve an asset path by checking multiple locations:
17/// 1. Absolute path (as-is)
18/// 2. `JAG_ASSETS_ROOT` override (if set) – supports a single
19/// directory or a path-list (e.g. "dir1:dir2") using platform
20/// `PATH` semantics.
21/// 3. Relative to current directory
22/// 4. In macOS app bundle Resources directory
23/// 5. In app bundle Resources with just filename
24pub fn resolve_asset_path(source: &std::path::Path) -> std::path::PathBuf {
25 // If absolute path exists, use it
26 if source.is_absolute() && source.exists() {
27 return source.to_path_buf();
28 }
29
30 // Try JAG_ASSETS_ROOT override. This allows launchers (e.g. WAID dev)
31 // to point Jag at one or more workspaces where shared assets like
32 // `images/` and `fonts/` live, even when the current directory is
33 // different from the Jag binary's source tree.
34 //
35 // The value may be a single directory or a platform-specific
36 // path list (e.g. "dir1:dir2" on Unix).
37 if let Ok(root_list) = std::env::var("JAG_ASSETS_ROOT") {
38 for base in std::env::split_paths(&root_list) {
39 let candidate = base.join(source);
40 if candidate.exists() {
41 return candidate;
42 }
43
44 if let Some(filename) = source.file_name() {
45 let candidate = base.join(filename);
46 if candidate.exists() {
47 return candidate;
48 }
49 }
50 }
51 }
52
53 // Try relative to current directory
54 if source.exists() {
55 return source.to_path_buf();
56 }
57
58 // Try in macOS app bundle Resources directory
59 if let Ok(exe_path) = std::env::current_exe() {
60 // exe is at App.app/Contents/MacOS/binary
61 // resources are at App.app/Contents/Resources/
62 if let Some(contents_dir) = exe_path.parent().and_then(|p| p.parent()) {
63 let resources_path = contents_dir.join("Resources").join(source);
64 if resources_path.exists() {
65 return resources_path;
66 }
67 // Also try without the leading directory (e.g., "images/foo.png" -> "foo.png")
68 if let Some(filename) = source.file_name() {
69 let resources_path = contents_dir.join("Resources").join(filename);
70 if resources_path.exists() {
71 return resources_path;
72 }
73 }
74 }
75 }
76
77 // Return original path (caller will handle non-existent case)
78 source.to_path_buf()
79}