use std::path::{Path, PathBuf};
pub(crate) fn pick_import_file(start: &Path) -> Option<PathBuf> {
let groups = crate::authoring::IMPORT_EXTENSION_GROUPS;
let all: Vec<&str> = groups
.iter()
.flat_map(|(_, exts)| exts.iter().copied())
.collect();
let mut dialog = rfd::FileDialog::new()
.set_title("Import a file")
.set_directory(start)
.add_filter("All supported", &all);
for (name, exts) in groups {
dialog = dialog.add_filter(*name, exts);
}
dialog.pick_file()
}
pub(crate) fn project_path(picked: &Path, root: &Path) -> String {
let picked = canonical_or_self(picked);
let root = canonical_or_self(root);
let rel = picked.strip_prefix(&root).unwrap_or(&picked);
to_slash(rel)
}
fn canonical_or_self(p: &Path) -> PathBuf {
p.canonicalize().unwrap_or_else(|_| p.to_path_buf())
}
fn to_slash(p: &Path) -> String {
let s = p.to_string_lossy().into_owned();
if cfg!(windows) {
s.replace('\\', "/")
} else {
s
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn project_path_relativizes_inside_the_project() {
let root = tempfile::tempdir().unwrap();
let assets = root.path().join("assets");
std::fs::create_dir_all(&assets).unwrap();
let file = assets.join("crate.glb");
std::fs::write(&file, b"glb").unwrap();
assert_eq!(project_path(&file, root.path()), "assets/crate.glb");
}
#[test]
fn project_path_keeps_outside_files_absolute() {
let root = tempfile::tempdir().unwrap();
let other = tempfile::tempdir().unwrap();
let file = other.path().join("far.glb");
std::fs::write(&file, b"glb").unwrap();
let text = project_path(&file, root.path());
assert!(
std::path::Path::new(&text).is_absolute(),
"an outside file stays absolute: {text}"
);
assert!(text.ends_with("far.glb"));
}
#[test]
fn project_path_matches_through_symlinks_and_dot_segments() {
let root = tempfile::tempdir().unwrap();
let file = root.path().join("scene.glb");
std::fs::write(&file, b"glb").unwrap();
let dotted = root.path().join(".").join("scene.glb");
assert_eq!(project_path(&dotted, root.path()), "scene.glb");
let link_home = tempfile::tempdir().unwrap();
let link = link_home.path().join("link");
#[cfg(unix)]
{
std::os::unix::fs::symlink(root.path(), &link).unwrap();
assert_eq!(
project_path(&link.join("scene.glb"), root.path()),
"scene.glb",
"a symlinked path resolves to the same project file"
);
}
#[cfg(not(unix))]
let _ = link;
}
#[test]
fn project_path_falls_back_when_a_path_cannot_resolve() {
let root = tempfile::tempdir().unwrap();
let missing = root.path().join("gone.glb");
let text = project_path(&missing, root.path());
assert!(text.ends_with("gone.glb"), "got: {text}");
}
}