Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/commands/qmod/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use qpm_qmod::models::mod_json::ModJson;

use crate::models::mod_json::{ModJsonExtensions, PreProcessingData};
use crate::models::package::{PackageConfigExtensions, SharedPackageConfigExtensions};
use crate::repository::{self, Repository};

use qpm_package::models::dependency::SharedPackageConfig;

Expand Down Expand Up @@ -42,14 +43,16 @@ pub(crate) fn execute_qmod_manifest_operation(
) -> Result<()> {
let package = PackageConfig::read(".")?;
let shared_package = SharedPackageConfig::read(".")?;
let repo = repository::useful_default_new(build_parameters.offline)?;

let new_json = generate_qmod_manifest(&package, shared_package, build_parameters)?;
let new_json = generate_qmod_manifest(&repo, &package, shared_package, build_parameters)?;
// Write mod.json
new_json.write(&PathBuf::from(ModJson::get_result_name()))?;
Ok(())
}

pub(crate) fn generate_qmod_manifest(
repo: &impl Repository,
package: &PackageConfig,
shared_package: SharedPackageConfig,
build_parameters: ManifestQmodOperationArgs,
Expand All @@ -61,7 +64,7 @@ pub(crate) fn generate_qmod_manifest(
let binary = shared_package
.config
.info
.get_so_name()
.get_so_name2()
.file_name()
.map(|s| s.to_string_lossy().to_string());

Expand All @@ -72,7 +75,7 @@ pub(crate) fn generate_qmod_manifest(
binary,
};
let mut existing_json = ModJson::read_and_preprocess(preprocess_data)?;
let template_mod_json: ModJson = shared_package.to_mod_json();
let template_mod_json: ModJson = shared_package.to_mod_json(repo)?;
let legacy_0_1_0 = package.matches_version(&VersionReq::parse("^0.1.0")?);
existing_json = ModJson::merge_modjson(existing_json, template_mod_json, legacy_0_1_0);
if let Some(excluded) = build_parameters.exclude_libs {
Expand Down
3 changes: 3 additions & 0 deletions src/commands/qmod/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::commands::qmod::manifest::{generate_qmod_manifest, ManifestQmodOperat
use crate::commands::scripts;
use crate::models::mod_json::ModJsonExtensions;
use crate::models::package::PackageConfigExtensions;
use crate::repository;
use crate::terminal::colors::QPMColor;

use qpm_package::models::dependency::SharedPackageConfig;
Expand Down Expand Up @@ -63,8 +64,10 @@ pub(crate) fn execute_qmod_zip_operation(build_parameters: ZipQmodOperationArgs)
"No mod.template.json found in the current directory, set it up please :) Hint: use \"qmod create\"");
let package = PackageConfig::read(".")?;
let shared_package = SharedPackageConfig::read(".")?;
let repo = repository::useful_default_new(build_parameters.offline)?;

let new_manifest = generate_qmod_manifest(
&repo,
&package,
shared_package,
ManifestQmodOperationArgs {
Expand Down
17 changes: 12 additions & 5 deletions src/commands/restore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{env, fs::File, io::Read, path::Path};
use clap::Args;

use color_eyre::{
eyre::{bail, eyre, ContextCompat, Result},
eyre::{bail, eyre, Context, ContextCompat, Result},
Section,
};
use itertools::Itertools;
Expand Down Expand Up @@ -82,21 +82,28 @@ impl Command for RestoreCommand {

// update config
shared_package.config = package;
// make additional data use cached data

// https://discord.com/channels/994470435100033074/994630741235347566/1265083186157715538
// make additional data update (for local installs)
shared_package
.restored_dependencies
.iter_mut()
.try_for_each(|d| -> color_eyre::Result<()> {
let package = repo
let package: SharedPackageConfig = repo
.get_package(&d.dependency.id, &d.version)
.ok()
.flatten()
.with_context(|| {
format!(
"Unable to fetch {}:{}",
d.dependency.id.dependency_id_color(),
d.version.version_id_color()
)
})?
.wrap_err_with(|| {
format!(
"Package {}:{} does not exist",
d.dependency.id.dependency_id_color(),
d.version.version_id_color()
)
})?;
d.dependency.additional_data = package.config.info.additional_data;
Ok(())
Expand Down
122 changes: 67 additions & 55 deletions src/models/package.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::{collections::HashSet, fs::File, io::BufReader, path::Path};
use std::{collections::HashMap, fs::File, io::BufReader, path::Path};

use color_eyre::{eyre::Context, owo_colors::OwoColorize, Result, Section};
use itertools::Itertools;
use qpm_package::{
extensions::package_metadata::PackageMetadataExtensions,
models::{
dependency::{Dependency, SharedDependency, SharedPackageConfig},
package::PackageConfig,
package::{PackageConfig, PackageDependency},
},
};
use qpm_qmod::models::mod_json::{ModDependency, ModJson};
Expand Down Expand Up @@ -40,7 +40,7 @@ pub trait SharedPackageConfigExtensions: Sized {
repository: &impl Repository,
) -> Result<(Self, Vec<SharedPackageConfig>)>;

fn to_mod_json(self) -> ModJson;
fn to_mod_json(self, repo: &impl Repository) -> color_eyre::Result<ModJson>;

fn try_write_toolchain(&self, repo: &impl Repository) -> Result<()>;
}
Expand Down Expand Up @@ -167,109 +167,115 @@ impl SharedPackageConfigExtensions for SharedPackageConfig {
))
}

fn to_mod_json(self) -> ModJson {
fn to_mod_json(self, repo: &impl Repository) -> color_eyre::Result<ModJson> {
// Self {
// id: dep.id,
// version_range: dep.version_range,
// mod_link: dep.additional_data.mod_link,
// }

let local_deps = &self.config.dependencies;
// List of dependencies we are directly referencing in qpm.json
let direct_dependencies: HashMap<String, &PackageDependency> = self
.config
.dependencies
.iter()
.map(|f| (f.id.clone(), f))
.collect();

// Only bundle mods that are not specifically excluded in qpm.json or if they're not header-only
let restored_deps: Vec<_> = self
let restored_dependencies_inflated: HashMap<String, SharedPackageConfig> = self
.restored_dependencies
.iter()
.filter(|dep| {
let local_dep_opt = local_deps
.iter()
.find(|local_dep| local_dep.id == dep.dependency.id);
.into_iter()
.map(|shared_dep| -> color_eyre::Result<_> {
let dep_package =
repo.get_package_checked(&shared_dep.dependency.id, &shared_dep.version)?;
Ok((shared_dep.dependency.id.clone(), dep_package))
})
.try_collect()?;

if let Some(local_dep) = local_dep_opt {
// Only bundle mods that are not specifically excluded in qpm.json or if they're not header-only
let restored_deps: Vec<_> = restored_dependencies_inflated
.values()
.filter(|dep_package| {
if let Some(local_dep) = direct_dependencies.get(&dep_package.config.info.id) {
// if force included/excluded, return early
if let Some(force_included) = local_dep.additional_data.include_qmod {
return force_included;
}
}

// or if header only is false
dep.dependency.additional_data.mod_link.is_some()
|| !dep.dependency.additional_data.headers_only.unwrap_or(false)
// if a qmod, we need to depend on it
dep_package.config.info.additional_data.mod_link.is_some()
// if not header only, we link to it and bundle it later
|| !dep_package
.config
.info
.additional_data
.headers_only
.unwrap_or(false)
})
.collect();

// List of dependencies we are directly referencing in qpm.json
let direct_dependencies: HashSet<String> = self
.config
.dependencies
.iter()
.map(|f| f.id.clone())
.collect();

// downloadable mods links n stuff
// mods that are header-only but provide qmods can be added as deps
// Must be directly referenced in qpm.json
let mods: Vec<ModDependency> = local_deps
.iter()
let mods: Vec<ModDependency> = direct_dependencies
.values()
// Removes any dependency without a qmod link
.filter_map(|dep| {
let shared_dep = restored_deps.iter().find(|d| d.dependency.id == dep.id)?;
if shared_dep.dependency.additional_data.mod_link.is_some() {
return Some((shared_dep, dep));
let shared_dep = restored_dependencies_inflated.get(&dep.id)?;
if shared_dep.config.info.additional_data.mod_link.is_some() {
let mod_dependency = ModDependency {
version_range: dep.version_range.clone(),
id: dep.id.clone(),
mod_link: shared_dep.config.info.additional_data.mod_link.clone(),
required: dep.additional_data.required,
};
return Some(mod_dependency);
}

None
})
.map(|(shared_dep, dep)| ModDependency {
version_range: dep.version_range.clone(),
id: dep.id.clone(),
mod_link: shared_dep.dependency.additional_data.mod_link.clone(),
required: dep.additional_data.required,
})
.sorted_by(|a, b| a.id.cmp(&b.id))
.collect();

// The rest of the mods to handle are not qmods, they are .so or .a mods
// actual direct lib deps
let libs: Vec<String> = self
.restored_dependencies
let libs: Vec<String> = restored_deps
.iter()
// We could just query the bmbf core mods list on GH?
// https://github.com/BMBF/resources/blob/master/com.beatgames.beatsaber/core-mods.json
// but really the only lib that never is copied over is the modloader, the rest is either a downloaded qmod or just a copied lib
// even core mods should technically be added via download
.filter(|lib| {
// find the actual dependency for the include qmod value
let local_dep_opt = local_deps
.iter()
.find(|local_dep| local_dep.id == lib.dependency.id);

// if set, use it later

let include_qmod = local_dep_opt
.and_then(|local_dep| local_dep.additional_data.include_qmod.as_ref());
let include_qmod = direct_dependencies
.get(&lib.config.info.id)
.and_then(|dep| dep.additional_data.include_qmod);

// Must be directly referenced in qpm.json
direct_dependencies.contains(&lib.dependency.id) &&
direct_dependencies.contains_key(&lib.config.info.id) &&

// keep if header only is false, or if not defined
!lib.dependency.additional_data.headers_only.unwrap_or(false) &&
!lib.config.info.additional_data.headers_only.unwrap_or(false) &&

// Modloader should never be included
lib.dependency.id != "modloader" &&
lib.config.info.id != "modloader" &&

// don't include static deps
!lib.dependency.additional_data.static_linking.unwrap_or(false) &&
!lib.config.info.additional_data.static_linking.unwrap_or(false) &&

// it's marked to be included, defaults to including ( same as dependencies with qmods )
include_qmod.copied().unwrap_or(true) &&
include_qmod.unwrap_or(true) &&

// Only keep libs that aren't downloadable
!mods.iter().any(|dep| lib.dependency.id == dep.id)
!mods.iter().any(|dep| lib.config.info.id == dep.id)
})
.map(|dep| dep.get_so_name().to_str().unwrap().to_string())
.map(|dep| dep.config.info.get_so_name2().to_str().unwrap().to_string())
.sorted()
.collect();

ModJson {
let json = ModJson {
name: self.config.info.name.clone(),
id: self.config.info.id.clone(),
porter: None,
Expand All @@ -280,11 +286,17 @@ impl SharedPackageConfigExtensions for SharedPackageConfig {
cover_image: None,
is_library: None,
dependencies: mods,
// TODO: Change
late_mod_files: vec![self.config.info.get_so_name().to_str().unwrap().to_string()],
late_mod_files: vec![self
.config
.info
.get_so_name2()
.to_str()
.unwrap()
.to_string()],
library_files: libs,
..Default::default()
}
};
Ok(json)
}

fn try_write_toolchain(&self, repo: &impl Repository) -> Result<()> {
Expand Down
25 changes: 24 additions & 1 deletion src/repository/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use color_eyre::Result;
use color_eyre::{
eyre::{Context, ContextCompat},
Result,
};
use itertools::Itertools;
use semver::Version;

use qpm_package::models::{
backend::PackageVersion, dependency::SharedPackageConfig, package::PackageConfig,
};

use crate::terminal::colors::QPMColor;

use self::{
local::FileRepository, memcached::MemcachedRepository, multi::MultiDependencyRepository,
qpackages::QPMRepository,
Expand All @@ -20,6 +25,24 @@ pub trait Repository {
fn get_package_names(&self) -> Result<Vec<String>>;
fn get_package_versions(&self, id: &str) -> Result<Option<Vec<PackageVersion>>>;
fn get_package(&self, id: &str, version: &Version) -> Result<Option<SharedPackageConfig>>;

fn get_package_checked(&self, id: &str, version: &Version) -> Result<SharedPackageConfig> {
self.get_package(id, version)
.with_context(|| {
format!(
"Unable to fetch {}:{}",
id.dependency_id_color(),
version.version_id_color()
)
})?
.wrap_err_with(|| {
format!(
"Package not found: {}:{}",
id.dependency_id_color(),
version.version_id_color()
)
})
}
// add to the db cache
// this just stores the shared config itself, not the package
fn add_to_db_cache(&mut self, config: SharedPackageConfig, permanent: bool) -> Result<()>;
Expand Down
Loading