diff --git a/torn-api/Cargo.toml b/torn-api/Cargo.toml index 4907380..539328e 100644 --- a/torn-api/Cargo.toml +++ b/torn-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "torn-api" -version = "0.5.12" +version = "0.5.13" edition = "2021" authors = ["Pyrit [2111649]"] license = "MIT" diff --git a/torn-api/src/de_util.rs b/torn-api/src/de_util.rs index 4be10e6..3f3983b 100644 --- a/torn-api/src/de_util.rs +++ b/torn-api/src/de_util.rs @@ -1,5 +1,7 @@ #![allow(unused)] +use std::collections::BTreeMap; + use chrono::{DateTime, NaiveDateTime, Utc}; use serde::de::{Deserialize, Deserializer, Error, Unexpected, Visitor}; @@ -92,6 +94,47 @@ where deserializer.deserialize_any(DumbVisitor) } +pub(crate) fn datetime_map<'de, D>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, +{ + #[derive(serde::Deserialize)] + struct UnixTimestamp( + #[serde(with = "chrono::serde::ts_seconds")] chrono::DateTime, + ); + + struct MapVisitor; + + impl<'de> Visitor<'de> for MapVisitor { + type Value = BTreeMap>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "map of unix timestamps") + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut result = BTreeMap::new(); + while let Some(key) = map.next_key::<&'de str>()? { + let id = key + .parse() + .map_err(|_e| A::Error::invalid_value(Unexpected::Str(key), &"integer"))?; + + let ts: UnixTimestamp = map.next_value()?; + result.insert(id, ts.0); + } + + Ok(result) + } + } + + deserializer.deserialize_map(MapVisitor) +} + #[cfg(feature = "decimal")] pub(crate) fn string_or_decimal<'de, D>(deserializer: D) -> Result where diff --git a/torn-api/src/faction.rs b/torn-api/src/faction.rs index 707b9a9..9ae8b47 100644 --- a/torn-api/src/faction.rs +++ b/torn-api/src/faction.rs @@ -35,6 +35,21 @@ pub struct Member<'a> { pub last_action: LastAction, } +#[derive(Debug, Clone, Deserialize)] +pub struct FactionTerritoryWar<'a> { + pub territory: &'a str, + pub assaulting_faction: i32, + pub defending_faction: i32, + pub score: i32, + pub required_score: i32, + + #[serde(with = "chrono::serde::ts_seconds")] + pub start_time: DateTime, + + #[serde(with = "chrono::serde::ts_seconds")] + pub end_time: DateTime, +} + #[derive(Debug, Clone, Deserialize)] pub struct Basic<'a> { #[serde(rename = "ID")] @@ -49,6 +64,12 @@ pub struct Basic<'a> { #[serde(borrow)] pub members: BTreeMap>, + + #[serde(deserialize_with = "de_util::datetime_map")] + pub peace: BTreeMap>, + + #[serde(borrow)] + pub territory_wars: Vec>, } #[derive(Debug, Clone, Copy, Deserialize)] diff --git a/torn-key-pool/src/postgres.rs b/torn-key-pool/src/postgres.rs index 2d19401..561ad3f 100644 --- a/torn-key-pool/src/postgres.rs +++ b/torn-key-pool/src/postgres.rs @@ -852,6 +852,45 @@ pub(crate) mod test { } } + #[test] + async fn test_concurrent_spread() { + let storage = Arc::new(setup().await.0); + + for i in 0..24 { + storage + .store_key(1, format!("{}", i), vec![Domain::All]) + .await + .unwrap(); + } + + for _ in 0..10 { + let mut set = tokio::task::JoinSet::new(); + + for _ in 0..50 { + let storage = storage.clone(); + set.spawn(async move { + storage.acquire_key(Domain::All).await.unwrap(); + }); + } + + for _ in 0..50 { + set.join_next().await.unwrap().unwrap(); + } + + let keys = storage.read_user_keys(1).await.unwrap(); + + assert_eq!(keys.len(), 25); + + for key in keys { + assert_eq!(key.uses, 2); + } + + sqlx::query("update api_keys set uses=0") + .execute(&storage.pool) + .await + .unwrap(); + } + } // HACK: this test is time sensitive and will fail if runs at the top of the minute #[test] async fn test_concurrent_many() {