Implement Nexus integration #54

Merged
lucas merged 15 commits from feat/nexus into master 2023-03-15 21:43:37 +01:00
3 changed files with 81 additions and 9 deletions
Showing only changes of commit e434535d96 - Show all commits

View file

@ -27,12 +27,25 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result<Mod
.wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?;
let data = Cursor::new(data); let data = Cursor::new(data);
let nexus = info let nexus = if let Some((_, id, _, _)) = info
.path .path
.file_name() .file_name()
.and_then(|s| s.to_str()) .and_then(|s| s.to_str())
.and_then(NexusApi::parse_file_name) .and_then(NexusApi::parse_file_name)
.map(|(_, id, version, _)| NexusInfo { id, version }); {
if !state.nexus_api_key.is_empty() {
let api = NexusApi::new(state.nexus_api_key.to_string())?;
let mod_info = api
.mods_id(id)
.await
.wrap_err_with(|| format!("Failed to query mod {} from Nexus", id))?;
Some(NexusInfo::from(mod_info))
} else {
None
}
} else {
None
};
let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?;
@ -375,13 +388,8 @@ async fn check_mod_update(info: Arc<ModInfo>, api: Arc<NexusApi>) -> Result<Opti
.await .await
.wrap_err_with(|| format!("Failed to query mod {} from Nexus", nexus.id))?; .wrap_err_with(|| format!("Failed to query mod {} from Nexus", nexus.id))?;
let updated_nexus = NexusInfo {
id: nexus.id,
version: updated_info.version,
};
let mut info = Arc::unwrap_or_clone(info); let mut info = Arc::unwrap_or_clone(info);
info.nexus = Some(updated_nexus); info.nexus = Some(NexusInfo::from(updated_info));
Ok(Some(info)) Ok(Some(info))
} }

View file

@ -4,6 +4,7 @@ use std::sync::Arc;
use druid::im::{HashMap, Vector}; use druid::im::{HashMap, Vector};
use druid::{Data, ImageBuf, Lens, WindowHandle, WindowId}; use druid::{Data, ImageBuf, Lens, WindowHandle, WindowId};
use dtmt_shared::ModConfig; use dtmt_shared::ModConfig;
use nexusmods::Mod as NexusMod;
use super::SelectedModLens; use super::SelectedModLens;
@ -72,6 +73,21 @@ impl From<dtmt_shared::ModDependency> for ModDependency {
pub(crate) struct NexusInfo { pub(crate) struct NexusInfo {
pub id: u64, pub id: u64,
pub version: String, pub version: String,
pub author: String,
pub summary: String,
pub description: Arc<String>,
}
impl From<NexusMod> for NexusInfo {
fn from(value: NexusMod) -> Self {
Self {
id: value.mod_id,
version: value.version,
author: value.author,
summary: value.summary,
description: Arc::new(value.description),
}
}
} }
#[derive(Clone, Data, Debug, Lens)] #[derive(Clone, Data, Debug, Lens)]

View file

@ -3,7 +3,7 @@ use std::sync::Arc;
use druid::im::Vector; use druid::im::Vector;
use druid::{Data, Lens}; use druid::{Data, Lens};
use super::{ModInfo, State}; use super::{ModInfo, NexusInfo, State};
pub(crate) struct SelectedModLens; pub(crate) struct SelectedModLens;
@ -73,3 +73,51 @@ impl<T: Data> Lens<Vector<T>, Vector<(usize, T)>> for IndexedVectorLens {
ret ret
} }
} }
/// A Lens that first checks a key in a mod's `NexusInfo`, then falls back to
/// the regular one.
pub(crate) struct NexusInfoLens<T, L, R>
where
L: Lens<NexusInfo, T>,
R: Lens<ModInfo, T>,
{
value: L,
fallback: R,
_marker: std::marker::PhantomData<T>,
}
impl<T: Data, L, R> NexusInfoLens<T, L, R>
where
L: Lens<NexusInfo, T>,
R: Lens<ModInfo, T>,
{
pub fn new(value: L, fallback: R) -> Self {
Self {
value,
fallback,
_marker: std::marker::PhantomData,
}
}
}
impl<T: Data, L, R> Lens<ModInfo, T> for NexusInfoLens<T, L, R>
where
L: Lens<NexusInfo, T>,
R: Lens<ModInfo, T>,
{
fn with<V, F: FnOnce(&T) -> V>(&self, data: &ModInfo, f: F) -> V {
if let Some(nexus) = &data.nexus {
self.value.with(nexus, f)
} else {
self.fallback.with(data, f)
}
}
fn with_mut<V, F: FnOnce(&mut T) -> V>(&self, data: &mut ModInfo, f: F) -> V {
if let Some(nexus) = &mut data.nexus {
self.value.with_mut(nexus, f)
} else {
self.fallback.with_mut(data, f)
}
}
}