pub mod api;
pub mod auth;
pub mod cdn;
pub mod graphql;
pub mod install;
pub mod oauth;
pub mod updates;
pub use api::NexusApi;
const DEFAULT_BASE_URL: &str = "https://api.nexusmods.com/v1";
const DEFAULT_GRAPHQL_URL: &str = "https://api.nexusmods.com/v2/graphql";
#[must_use]
pub fn base_url() -> String {
std::env::var("MODDE_NEXUS_BASE_URL").unwrap_or_else(|_| DEFAULT_BASE_URL.to_string())
}
#[must_use]
pub fn graphql_url() -> String {
std::env::var("MODDE_NEXUS_GRAPHQL_URL").unwrap_or_else(|_| DEFAULT_GRAPHQL_URL.to_string())
}
use std::collections::HashMap;
use std::path::Path;
use anyhow::Result;
use reqwest::Client;
use tracing::debug;
use modde_core::manifest::wabbajack::DownloadDirective;
use crate::common::simple_download;
use crate::error::{SourceError, SourceResult};
use crate::traits::{DownloadHandle, DownloadSource, ProgressCallback, VerifiedFile};
pub struct NexusSource {
client: Client,
api_key: String,
}
impl NexusSource {
pub fn new(client: Client) -> Result<Self> {
let api_key = auth::load_api_key()?;
Ok(Self { client, api_key })
}
#[must_use]
pub fn with_api_key(client: Client, api_key: String) -> Self {
Self { client, api_key }
}
}
impl DownloadSource for NexusSource {
fn can_handle(&self, directive: &DownloadDirective) -> bool {
matches!(directive, DownloadDirective::Nexus { .. })
}
async fn resolve(&self, directive: &DownloadDirective) -> SourceResult<DownloadHandle> {
let DownloadDirective::Nexus {
game_id,
mod_id,
file_id,
hash,
} = directive
else {
return Err(SourceError::other(anyhow::anyhow!("not a Nexus directive")));
};
let download_url = cdn::generate_download_link(
&self.client,
&self.api_key,
game_id.as_str(),
*mod_id,
*file_id,
)
.await?;
debug!(url = %download_url, "resolved Nexus CDN download URL");
Ok(DownloadHandle {
url: download_url,
candidate_urls: Vec::new(),
headers: HashMap::new(),
expected_hash: *hash,
size_hint: None,
})
}
async fn download_with_progress(
&self,
handle: DownloadHandle,
dest: &Path,
progress: ProgressCallback,
) -> SourceResult<VerifiedFile> {
simple_download(&self.client, &handle, dest, &progress).await
}
}