diff --git a/crates/fj-host/src/lib.rs b/crates/fj-host/src/lib.rs index f71ad9d42..f94b23459 100644 --- a/crates/fj-host/src/lib.rs +++ b/crates/fj-host/src/lib.rs @@ -27,7 +27,6 @@ use std::{ thread, }; -use cargo_metadata::Metadata; use notify::Watcher as _; use thiserror::Error; @@ -41,7 +40,8 @@ pub struct Model { } impl Model { - /// Initialize the model from a path + /// Initialize the model using the path to its crate (i.e. the folder + /// containing `Cargo.toml`). /// /// Optionally, the target directory where plugin files are compiled to can /// be provided. If it is not provided, the target directory is assumed to @@ -50,33 +50,28 @@ impl Model { path: PathBuf, target_dir: Option, ) -> io::Result { - let name = { - // Can't panic. It only would, if the path ends with "..", and we - // are canonicalizing it here to prevent that. - let canonical = path.canonicalize()?; - let file_name = canonical - .file_name() - .expect("Expected path to be canonical"); - - file_name.to_string_lossy().replace('-', "_") - }; + let crate_dir = path.canonicalize()?; + + let metadata = cargo_metadata::MetadataCommand::new() + .current_dir(&crate_dir) + .exec() + .map_err(metadata_error_to_io)?; - let src_path = path.join("src"); + let pkg = package_associated_with_directory(&metadata, &crate_dir)?; + let src_path = crate_dir.join("src"); let lib_path = { + let name = pkg.name.replace('-', "_"); let file = HostPlatform::lib_file_name(&name); let target_dir = target_dir - .or_else(|| cargo_metadata_target_dir(&path)) - .unwrap_or_else(|| path.join("target")); + .unwrap_or_else(|| metadata.target_directory.clone().into()); target_dir.join("debug").join(file) }; - let manifest_path = path.join("Cargo.toml"); - Ok(Self { src_path, lib_path, - manifest_path, + manifest_path: pkg.manifest_path.as_std_path().to_path_buf(), }) } @@ -211,6 +206,33 @@ impl Model { } } +fn metadata_error_to_io(e: cargo_metadata::Error) -> std::io::Error { + match e { + cargo_metadata::Error::Io(io) => io, + _ => std::io::Error::new(io::ErrorKind::Other, e), + } +} + +fn package_associated_with_directory<'m>( + metadata: &'m cargo_metadata::Metadata, + dir: &Path, +) -> Result<&'m cargo_metadata::Package, io::Error> { + for pkg in metadata.workspace_packages() { + let crate_dir = pkg + .manifest_path + .parent() + .and_then(|p| p.canonicalize().ok()); + + if crate_dir.as_deref() == Some(dir) { + return Ok(pkg); + } + } + + let msg = format!("\"{}\" doesn't point to a crate", dir.display()); + + Err(io::Error::new(io::ErrorKind::Other, msg)) +} + /// Watches a model for changes, reloading it continually pub struct Watcher { _watcher: Box, @@ -288,14 +310,3 @@ pub enum Error { } type ModelFn = unsafe extern "C" fn(args: &Parameters) -> fj::Shape; - -fn cargo_metadata_target_dir(model: &Path) -> Option { - let Metadata { - target_directory, .. - } = cargo_metadata::MetadataCommand::new() - .current_dir(model) - .exec() - .ok()?; - - Some(target_directory.into()) -}