feat(nexusmods): Implement additional endpoints
This commit is contained in:
parent
5ca1ca3506
commit
2452f9b4ab
3 changed files with 154 additions and 56 deletions
|
@ -13,7 +13,7 @@ serde_json = "1.0.94"
|
|||
thiserror = "1.0.39"
|
||||
time = { version = "0.3.20", features = ["serde"] }
|
||||
tracing = "0.1.37"
|
||||
url = "2.3.1"
|
||||
url = { version = "2.3.1", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.26.0", features = ["rt", "macros"] }
|
||||
|
|
|
@ -2,15 +2,15 @@ use std::convert::Infallible;
|
|||
|
||||
use lazy_static::lazy_static;
|
||||
use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue};
|
||||
use reqwest::{Client, Url};
|
||||
use serde::ser::SerializeTuple;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use reqwest::{Client, RequestBuilder, Url};
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
mod types;
|
||||
pub use types::*;
|
||||
|
||||
// TODO: Add OS information
|
||||
const USER_AGENT: &str = concat!("DTMM/", env!("CARGO_PKG_VERSION"));
|
||||
const GAME_ID: &str = "warhammer40kdarktide";
|
||||
|
||||
lazy_static! {
|
||||
static ref BASE_URL: Url = Url::parse("https://api.nexusmods.com/v1/").unwrap();
|
||||
|
@ -37,44 +37,6 @@ pub enum Error {
|
|||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct UpdateInfo {
|
||||
pub mod_id: u64,
|
||||
#[serde(with = "time::serde::timestamp")]
|
||||
pub latest_file_update: OffsetDateTime,
|
||||
#[serde(with = "time::serde::timestamp")]
|
||||
pub latest_mod_activity: OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum UpdatePeriod {
|
||||
Day,
|
||||
Week,
|
||||
Month,
|
||||
}
|
||||
|
||||
impl Default for UpdatePeriod {
|
||||
fn default() -> Self {
|
||||
Self::Week
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for UpdatePeriod {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut tup = serializer.serialize_tuple(2)?;
|
||||
tup.serialize_element("period")?;
|
||||
tup.serialize_element(match self {
|
||||
Self::Day => "1d",
|
||||
Self::Week => "1w",
|
||||
Self::Month => "1m",
|
||||
})?;
|
||||
tup.end()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Api {
|
||||
client: Client,
|
||||
}
|
||||
|
@ -93,22 +55,37 @@ impl Api {
|
|||
Ok(Self { client })
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn send<T>(&self, req: RequestBuilder) -> Result<T>
|
||||
where
|
||||
T: for<'a> Deserialize<'a>,
|
||||
{
|
||||
let res = req.send().await?.error_for_status()?;
|
||||
tracing::trace!(?res);
|
||||
|
||||
let json = res.text().await?;
|
||||
serde_json::from_str(&json).map_err(|error| Error::Deserialize { json, error })
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub async fn user_validate(&self) -> Result<User> {
|
||||
let url = BASE_URL.join("users/validate.json")?;
|
||||
let req = self.client.get(url);
|
||||
self.send(req).await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub async fn mods_updated(&self, period: UpdatePeriod) -> Result<Vec<UpdateInfo>> {
|
||||
let url = BASE_URL_GAME.join("mods/updated.json")?;
|
||||
let req = self.client.get(url).query(&[period]);
|
||||
self.send(req).await
|
||||
}
|
||||
|
||||
let res = self
|
||||
.client
|
||||
.get(url)
|
||||
.query(&[period])
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
tracing::trace!(?res);
|
||||
let json = res.text().await?;
|
||||
|
||||
serde_json::from_str(&json).map_err(|error| Error::Deserialize { json, error })
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub async fn mods_id(&self, id: u64) -> Result<Mod> {
|
||||
let url = BASE_URL_GAME.join(&format!("mods/{}.json", id))?;
|
||||
let req = self.client.get(url);
|
||||
self.send(req).await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,4 +106,23 @@ mod test {
|
|||
.await
|
||||
.expect("failed to query 'mods_updated'");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_validate() {
|
||||
let client = make_api();
|
||||
client
|
||||
.user_validate()
|
||||
.await
|
||||
.expect("failed to query 'user_validate'");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mods_id() {
|
||||
let client = make_api();
|
||||
let dmf_id = 8;
|
||||
client
|
||||
.mods_id(dmf_id)
|
||||
.await
|
||||
.expect("failed to query 'mods_id'");
|
||||
}
|
||||
}
|
||||
|
|
102
lib/nexusmods/src/types.rs
Normal file
102
lib/nexusmods/src/types.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use reqwest::Url;
|
||||
use serde::ser::SerializeTuple;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct User {
|
||||
pub user_id: u64,
|
||||
pub name: String,
|
||||
pub profile_url: Url,
|
||||
// pub is_premium: bool,
|
||||
// pub is_supporter: bool,
|
||||
// pub email: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ModStatus {
|
||||
Published,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize)]
|
||||
pub enum EndorseStatus {
|
||||
Undecided,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ModEndorsement {
|
||||
pub endorse_status: EndorseStatus,
|
||||
#[serde(with = "time::serde::timestamp::option")]
|
||||
pub timestamp: Option<OffsetDateTime>,
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Mod {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub summary: String,
|
||||
pub picture_url: Url,
|
||||
pub uid: u64,
|
||||
pub mod_id: u64,
|
||||
pub category_id: u64,
|
||||
pub version: String,
|
||||
#[serde(with = "time::serde::timestamp")]
|
||||
pub created_timestamp: OffsetDateTime,
|
||||
// created_time: OffsetDateTime,
|
||||
#[serde(with = "time::serde::timestamp")]
|
||||
pub updated_timestamp: OffsetDateTime,
|
||||
// updated_time: OffsetDateTime,
|
||||
pub author: String,
|
||||
pub uploaded_by: String,
|
||||
pub uploaded_users_profile_url: Url,
|
||||
pub status: ModStatus,
|
||||
pub available: bool,
|
||||
pub endorsement: ModEndorsement,
|
||||
// pub mod_downloads: u64,
|
||||
// pub mod_unique_downloads: u64,
|
||||
// pub game_id: u64,
|
||||
// pub allow_rating: bool,
|
||||
// pub domain_name: String,
|
||||
// pub endorsement_count: u64,
|
||||
// pub contains_adult_content: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct UpdateInfo {
|
||||
pub mod_id: u64,
|
||||
#[serde(with = "time::serde::timestamp")]
|
||||
pub latest_file_update: OffsetDateTime,
|
||||
#[serde(with = "time::serde::timestamp")]
|
||||
pub latest_mod_activity: OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum UpdatePeriod {
|
||||
Day,
|
||||
Week,
|
||||
Month,
|
||||
}
|
||||
|
||||
impl Default for UpdatePeriod {
|
||||
fn default() -> Self {
|
||||
Self::Week
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for UpdatePeriod {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut tup = serializer.serialize_tuple(2)?;
|
||||
tup.serialize_element("period")?;
|
||||
tup.serialize_element(match self {
|
||||
Self::Day => "1d",
|
||||
Self::Week => "1w",
|
||||
Self::Month => "1m",
|
||||
})?;
|
||||
tup.end()
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue