mirror of
https://github.com/pesde-pkg/pesde.git
synced 2025-04-09 05:10:54 +01:00
200 lines
7.5 KiB
Rust
200 lines
7.5 KiB
Rust
use crate::{
|
|
linking::generator::get_file_types,
|
|
lockfile::DownloadedGraph,
|
|
manifest::target::Target,
|
|
names::PackageNames,
|
|
scripts::{execute_script, ScriptName},
|
|
source::{PackageRef, VersionId},
|
|
Project, PACKAGES_CONTAINER_NAME,
|
|
};
|
|
use std::{collections::BTreeMap, fs::create_dir_all};
|
|
|
|
pub mod generator;
|
|
|
|
impl Project {
|
|
pub fn link_dependencies(&self, graph: &DownloadedGraph) -> Result<(), errors::LinkingError> {
|
|
let manifest = self.deser_manifest()?;
|
|
|
|
let mut package_types = BTreeMap::<&PackageNames, BTreeMap<&VersionId, Vec<String>>>::new();
|
|
|
|
for (name, versions) in graph {
|
|
for (version_id, node) in versions {
|
|
let Some(lib_file) = node.target.lib_path() else {
|
|
continue;
|
|
};
|
|
|
|
let container_folder = node.node.container_folder(
|
|
&self
|
|
.path()
|
|
.join(node.node.base_folder(manifest.target.kind(), true))
|
|
.join(PACKAGES_CONTAINER_NAME),
|
|
name,
|
|
version_id.version(),
|
|
);
|
|
|
|
let lib_file = lib_file.to_path(&container_folder);
|
|
|
|
let contents = match std::fs::read_to_string(&lib_file) {
|
|
Ok(contents) => contents,
|
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
|
return Err(errors::LinkingError::LibFileNotFound(
|
|
lib_file.display().to_string(),
|
|
));
|
|
}
|
|
Err(e) => return Err(e.into()),
|
|
};
|
|
|
|
let types = match get_file_types(&contents) {
|
|
Ok(types) => types,
|
|
Err(e) => {
|
|
return Err(errors::LinkingError::FullMoon(
|
|
lib_file.display().to_string(),
|
|
e,
|
|
))
|
|
}
|
|
};
|
|
|
|
log::debug!("{name}@{version_id} has {} exported types", types.len());
|
|
|
|
package_types
|
|
.entry(name)
|
|
.or_default()
|
|
.insert(version_id, types);
|
|
|
|
#[cfg(feature = "roblox")]
|
|
if let Target::Roblox { build_files, .. } = &node.target {
|
|
let script_name = ScriptName::RobloxSyncConfigGenerator.to_string();
|
|
|
|
let Some(script_path) = manifest.scripts.get(&script_name) else {
|
|
log::warn!("not having a `{script_name}` script in the manifest might cause issues with Roblox linking");
|
|
continue;
|
|
};
|
|
|
|
execute_script(
|
|
Some(&script_name),
|
|
&script_path.to_path(self.path()),
|
|
build_files,
|
|
&container_folder,
|
|
false,
|
|
)
|
|
.map_err(|e| {
|
|
errors::LinkingError::GenerateRobloxSyncConfig(
|
|
container_folder.display().to_string(),
|
|
e,
|
|
)
|
|
})?;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (name, versions) in graph {
|
|
for (version_id, node) in versions {
|
|
let base_folder = self.path().join(
|
|
self.path()
|
|
.join(node.node.base_folder(manifest.target.kind(), true)),
|
|
);
|
|
create_dir_all(&base_folder)?;
|
|
let base_folder = base_folder.canonicalize()?;
|
|
let packages_container_folder = base_folder.join(PACKAGES_CONTAINER_NAME);
|
|
|
|
let container_folder = node.node.container_folder(
|
|
&packages_container_folder,
|
|
name,
|
|
version_id.version(),
|
|
);
|
|
|
|
if let Some((alias, types)) = package_types
|
|
.get(name)
|
|
.and_then(|v| v.get(version_id))
|
|
.and_then(|types| node.node.direct.as_ref().map(|(alias, _)| (alias, types)))
|
|
{
|
|
let module = generator::generate_linking_module(
|
|
&generator::get_require_path(
|
|
&node.target,
|
|
&base_folder,
|
|
&container_folder,
|
|
node.node.pkg_ref.use_new_structure(),
|
|
)?,
|
|
types,
|
|
);
|
|
|
|
std::fs::write(base_folder.join(format!("{alias}.luau")), module)?;
|
|
}
|
|
|
|
for (dependency_name, (dependency_version_id, dependency_alias)) in
|
|
&node.node.dependencies
|
|
{
|
|
let Some(dependency_node) = graph
|
|
.get(dependency_name)
|
|
.and_then(|v| v.get(dependency_version_id))
|
|
else {
|
|
return Err(errors::LinkingError::DependencyNotFound(
|
|
dependency_name.to_string(),
|
|
dependency_version_id.to_string(),
|
|
));
|
|
};
|
|
|
|
let dependency_container_folder = dependency_node.node.container_folder(
|
|
&packages_container_folder,
|
|
dependency_name,
|
|
dependency_version_id.version(),
|
|
);
|
|
|
|
let linker_folder = container_folder
|
|
.join(dependency_node.node.base_folder(node.target.kind(), false));
|
|
create_dir_all(&linker_folder)?;
|
|
let linker_folder = linker_folder.canonicalize()?;
|
|
|
|
let linker_file = linker_folder.join(format!("{dependency_alias}.luau"));
|
|
|
|
let module = generator::generate_linking_module(
|
|
&generator::get_require_path(
|
|
&dependency_node.target,
|
|
&linker_file,
|
|
&dependency_container_folder,
|
|
node.node.pkg_ref.use_new_structure(),
|
|
)?,
|
|
package_types
|
|
.get(dependency_name)
|
|
.and_then(|v| v.get(dependency_version_id))
|
|
.unwrap(),
|
|
);
|
|
|
|
std::fs::write(linker_file, module)?;
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub mod errors {
|
|
use thiserror::Error;
|
|
|
|
#[derive(Debug, Error)]
|
|
#[non_exhaustive]
|
|
pub enum LinkingError {
|
|
#[error("error deserializing project manifest")]
|
|
Manifest(#[from] crate::errors::ManifestReadError),
|
|
|
|
#[error("error interacting with filesystem")]
|
|
Io(#[from] std::io::Error),
|
|
|
|
#[error("dependency not found: {0}@{1}")]
|
|
DependencyNotFound(String, String),
|
|
|
|
#[error("library file at {0} not found")]
|
|
LibFileNotFound(String),
|
|
|
|
#[error("error parsing Luau script at {0}")]
|
|
FullMoon(String, Vec<full_moon::Error>),
|
|
|
|
#[error("error generating require path")]
|
|
GetRequirePath(#[from] crate::linking::generator::errors::GetRequirePathError),
|
|
|
|
#[cfg(feature = "roblox")]
|
|
#[error("error generating roblox sync config for {0}")]
|
|
GenerateRobloxSyncConfig(String, #[source] std::io::Error),
|
|
}
|
|
}
|