Initial commit
This commit is contained in:
parent
3ee50f9aff
commit
bd6dbebb31
20 changed files with 2644 additions and 0 deletions
10
logs_tf/Cargo.toml
Normal file
10
logs_tf/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "logs_tf"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.38"
|
||||
json = "0.12.4"
|
||||
reqwest = { version = "0.12.9", features = ["blocking"] }
|
||||
ucore = { path = "../core" }
|
||||
83
logs_tf/src/lib.rs
Normal file
83
logs_tf/src/lib.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
pub mod log;
|
||||
pub mod performance;
|
||||
pub mod query_error;
|
||||
pub mod search_params;
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use json::JsonValue;
|
||||
pub use log::*;
|
||||
pub use query_error::*;
|
||||
use reqwest::blocking as reqwest;
|
||||
|
||||
use self::search_params::SearchParams;
|
||||
|
||||
const LOGS_TF_API_BASE: &str = "https://logs.tf/api/v1/log";
|
||||
|
||||
/// Function that tries to execute something that returns a result. If it does
|
||||
/// not work the first time, it will keep trying num_retries times until it
|
||||
/// either returns Ok() or all the tries have been used up.
|
||||
pub(self) fn keep_trying<A, R, E>(action: A, num_retries: u8) -> Result<R, E>
|
||||
where
|
||||
A: Fn() -> Result<R, E>,
|
||||
{
|
||||
let mut num_tries = 0;
|
||||
loop {
|
||||
let res = action();
|
||||
num_tries += 1;
|
||||
|
||||
if res.is_ok() || num_tries > num_retries + 1 {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sleep for a little time before making a request to logs.tf. The API is very
|
||||
/// sensitive to quickly making queries to it and will respond with invalid
|
||||
/// responses otherwise.
|
||||
pub(self) fn log_delay() { thread::sleep(Duration::from_millis(500)) }
|
||||
|
||||
/// Checks for the `"success": true` field in the json value, which is always
|
||||
/// set by logs.tf. If `"success": false` is set, it will parse the error and
|
||||
/// return a `QueryError`.
|
||||
fn check_json_success(json: &JsonValue) -> QueryResult<()>
|
||||
{
|
||||
let success = json["success"].as_bool().unwrap();
|
||||
|
||||
if success {
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
let error = json["error"].as_str().unwrap();
|
||||
Err(QueryError::Unsuccessful(error.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
fn search_logs_once(search_params: &SearchParams) -> QueryResult<Vec<LogMetadata>>
|
||||
{
|
||||
log_delay();
|
||||
|
||||
let request = reqwest::Client::builder().build()?.get(LOGS_TF_API_BASE);
|
||||
let request = search_params.add_params_to_request(request);
|
||||
|
||||
let response = request.send()?;
|
||||
let json = json::parse(&(response.text()?)).unwrap();
|
||||
check_json_success(&json)?;
|
||||
|
||||
Ok(json["logs"]
|
||||
.members()
|
||||
.map(|meta| LogMetadata::from_json(meta))
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Query logs.tf for logs with the given parameters. Takes a number of retries.
|
||||
/// Should the first query fail, this is the number of tries it will take until
|
||||
/// it gives up querying.
|
||||
///
|
||||
/// # Returns
|
||||
/// The metadata of all logs that fit the search parameters
|
||||
pub fn search_logs(search_params: SearchParams, num_retries: u8) -> QueryResult<Vec<LogMetadata>>
|
||||
{
|
||||
keep_trying(|| search_logs_once(&search_params), num_retries)
|
||||
}
|
||||
127
logs_tf/src/log.rs
Normal file
127
logs_tf/src/log.rs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use json::JsonValue;
|
||||
use ucore::SteamID;
|
||||
|
||||
use super::{keep_trying, QueryResult, LOGS_TF_API_BASE};
|
||||
use crate::performance::{Performance, Score};
|
||||
|
||||
pub struct LogMetadata
|
||||
{
|
||||
pub id: u32,
|
||||
pub date_time: DateTime<Utc>,
|
||||
pub map: String,
|
||||
pub num_players: u8,
|
||||
}
|
||||
|
||||
pub struct Log
|
||||
{
|
||||
meta: LogMetadata,
|
||||
performances: HashMap<SteamID, Vec<Performance>>,
|
||||
duration_secs: u32,
|
||||
}
|
||||
|
||||
impl LogMetadata
|
||||
{
|
||||
pub fn from_json(json: &JsonValue) -> Self
|
||||
{
|
||||
Self {
|
||||
id: json["id"].as_u32().unwrap(),
|
||||
date_time: DateTime::from_timestamp(json["date"].as_i64().unwrap(), 0)
|
||||
.expect("Failed to parse datetime"),
|
||||
map: json["map"].as_str().unwrap().to_owned(),
|
||||
num_players: json["players"].as_u8().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Log
|
||||
{
|
||||
pub(crate) fn new(meta: LogMetadata, duration_secs: u32) -> Self
|
||||
{
|
||||
Self {
|
||||
meta,
|
||||
performances: HashMap::new(),
|
||||
duration_secs,
|
||||
}
|
||||
}
|
||||
|
||||
fn download_once(id: u32) -> QueryResult<Self>
|
||||
{
|
||||
let log = reqwest::blocking::get(format!("{}/{}", LOGS_TF_API_BASE, id))?
|
||||
.text()
|
||||
.expect("Unable to read response body");
|
||||
|
||||
let json = json::parse(&log)?;
|
||||
super::check_json_success(&json)?;
|
||||
|
||||
Ok(Self::from_json(id, &json))
|
||||
}
|
||||
|
||||
/// Download the log with the given id from logs.tf and turn it into a
|
||||
/// format that can be processed by a rating system easily.
|
||||
pub fn download(id: u32, num_retries: u8) -> QueryResult<Self>
|
||||
{
|
||||
keep_trying(|| Self::download_once(id), num_retries)
|
||||
}
|
||||
|
||||
/// Parse the json information as found on logs.tf into a format easily
|
||||
/// digestible by the rating system.
|
||||
// XXX: Check presumed logs.tf json for any format deviances
|
||||
pub fn from_json(id: u32, json: &JsonValue) -> Self
|
||||
{
|
||||
let info = &json["info"];
|
||||
let duration_secs = info["total_length"]
|
||||
.as_u32()
|
||||
.expect("Duration is not an unsigned int");
|
||||
let map = info["map"]
|
||||
.as_str()
|
||||
.expect("Unable to read map of log")
|
||||
.to_owned();
|
||||
let timestamp = info["date"]
|
||||
.as_u32()
|
||||
.expect("Unable to read date as Unix timestamp") as i64;
|
||||
let date_time = DateTime::from_timestamp(timestamp, 0).expect("Invalid timestamp");
|
||||
let num_players = json["names"].members().len() as u8;
|
||||
|
||||
let meta = LogMetadata {
|
||||
id,
|
||||
date_time,
|
||||
map,
|
||||
num_players,
|
||||
};
|
||||
|
||||
let score = Score::from_json(json);
|
||||
|
||||
let mut performances = HashMap::new();
|
||||
for (player_id, stats) in json["players"].entries() {
|
||||
let player_id =
|
||||
SteamID::from_str(player_id).expect("Player id is not a valid steam id");
|
||||
|
||||
let player_performances = Performance::extract_all_from_json(&score, stats);
|
||||
performances.insert(player_id, player_performances);
|
||||
}
|
||||
|
||||
Self {
|
||||
meta,
|
||||
performances,
|
||||
duration_secs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta(&self) -> &LogMetadata { &self.meta }
|
||||
pub fn duration_secs(&self) -> u32 { self.duration_secs }
|
||||
pub fn performances(&self) -> &HashMap<SteamID, Vec<Performance>> { &self.performances }
|
||||
|
||||
pub(crate) fn add_performance(&mut self, player: SteamID, performance: Performance)
|
||||
{
|
||||
match self.performances.get_mut(&player) {
|
||||
Some(perfs) => perfs.push(performance),
|
||||
None => {
|
||||
self.performances.insert(player, vec![performance]);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
80
logs_tf/src/performance/dm_performance.rs
Normal file
80
logs_tf/src/performance/dm_performance.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use json::JsonValue;
|
||||
use ucore::Class;
|
||||
|
||||
use super::Performance;
|
||||
|
||||
pub struct DMPerformance
|
||||
{
|
||||
pub class: Class,
|
||||
pub kills: u8,
|
||||
pub assists: u8,
|
||||
pub deaths: u8,
|
||||
pub damage: u32,
|
||||
pub time_played_secs: u32,
|
||||
}
|
||||
|
||||
impl DMPerformance
|
||||
{
|
||||
pub fn extract_all_from_json(json: &JsonValue) -> Vec<Self>
|
||||
{
|
||||
json["class_stats"]
|
||||
.members()
|
||||
.map(|class_stats| Self {
|
||||
class: Class::from_str(class_stats["type"].as_str().unwrap()).unwrap(),
|
||||
kills: class_stats["kills"].as_u8().unwrap(),
|
||||
assists: class_stats["assists"].as_u8().unwrap(),
|
||||
deaths: class_stats["deaths"].as_u8().unwrap(),
|
||||
damage: class_stats["dmg"].as_u32().unwrap(),
|
||||
time_played_secs: class_stats["total_time"].as_u32().unwrap(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DMPerformance> for Performance
|
||||
{
|
||||
fn from(value: DMPerformance) -> Self { Self::DM(value) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests
|
||||
{
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn extract_all_from_json()
|
||||
{
|
||||
let mut json = String::new();
|
||||
File::open("test_data/log_3094861.json")
|
||||
.expect("Unable to open test file")
|
||||
.read_to_string(&mut json)
|
||||
.expect("Unable to read file to string");
|
||||
let json = json::parse(&json).expect("Unable to parse json");
|
||||
|
||||
let perfs = DMPerformance::extract_all_from_json(&json["players"]["[U:1:886717065]"]);
|
||||
|
||||
assert_eq!(perfs.len(), 3);
|
||||
let scout_perf = &perfs[0];
|
||||
let engi_perf = &perfs[1];
|
||||
let _pyro_perf = &perfs[2];
|
||||
|
||||
assert_eq!(scout_perf.class, Class::Scout);
|
||||
assert_eq!(scout_perf.kills, 19);
|
||||
assert_eq!(scout_perf.assists, 14);
|
||||
assert_eq!(scout_perf.deaths, 16);
|
||||
assert_eq!(scout_perf.damage, 6671);
|
||||
assert_eq!(scout_perf.time_played_secs, 1618);
|
||||
|
||||
assert_eq!(engi_perf.class, Class::Engineer);
|
||||
assert_eq!(engi_perf.kills, 0);
|
||||
assert_eq!(engi_perf.assists, 2);
|
||||
assert_eq!(engi_perf.deaths, 0);
|
||||
assert_eq!(engi_perf.damage, 293);
|
||||
assert_eq!(engi_perf.time_played_secs, 99);
|
||||
}
|
||||
}
|
||||
76
logs_tf/src/performance/medic_performance.rs
Normal file
76
logs_tf/src/performance/medic_performance.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use json::JsonValue;
|
||||
use ucore::Class;
|
||||
|
||||
use super::Performance;
|
||||
|
||||
pub struct MedicPerformance
|
||||
{
|
||||
pub healing: u32,
|
||||
pub average_uber_length_secs: f32,
|
||||
pub num_ubers: u8,
|
||||
pub num_drops: u8,
|
||||
pub deaths: u8,
|
||||
pub time_played_secs: u32,
|
||||
}
|
||||
|
||||
impl MedicPerformance
|
||||
{
|
||||
pub fn extract_from_json(json: &JsonValue) -> Option<Self>
|
||||
{
|
||||
let class_stats = json["class_stats"].members().find(|class_stats| {
|
||||
Class::from_str(class_stats["type"].as_str().unwrap()).unwrap() == Class::Medic
|
||||
});
|
||||
|
||||
if !json.has_key("medicstats") || class_stats.is_none() {
|
||||
return None;
|
||||
}
|
||||
let class_stats = class_stats.unwrap();
|
||||
|
||||
Some(Self {
|
||||
healing: json["heal"].as_u32().unwrap_or(0),
|
||||
average_uber_length_secs: json["medicstats"]["avg_uber_length"]
|
||||
.as_f32()
|
||||
.unwrap_or(0.0),
|
||||
num_ubers: json["ubers"].as_u8().unwrap_or(0),
|
||||
num_drops: json["drops"].as_u8().unwrap_or(0),
|
||||
deaths: class_stats["deaths"].as_u8().unwrap_or(0),
|
||||
time_played_secs: class_stats["total_time"].as_u32().unwrap_or(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MedicPerformance> for Performance
|
||||
{
|
||||
fn from(value: MedicPerformance) -> Self { Self::Med(value) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests
|
||||
{
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn extract_from_json()
|
||||
{
|
||||
let mut json = String::new();
|
||||
File::open("test_data/log_3094861.json")
|
||||
.expect("Unable to open test file")
|
||||
.read_to_string(&mut json)
|
||||
.expect("Unable to read file to string");
|
||||
let json = json::parse(&json).expect("Unable to parse json");
|
||||
|
||||
let stats = MedicPerformance::extract_from_json(&json["players"]["[U:1:71020853]"])
|
||||
.expect("Unable to find medic performance");
|
||||
assert_eq!(stats.healing, 22732);
|
||||
assert_eq!(stats.average_uber_length_secs, 6.875);
|
||||
assert_eq!(stats.num_ubers, 12);
|
||||
assert_eq!(stats.num_drops, 0);
|
||||
assert_eq!(stats.deaths, 10);
|
||||
assert_eq!(stats.time_played_secs, 1738);
|
||||
}
|
||||
}
|
||||
44
logs_tf/src/performance/mod.rs
Normal file
44
logs_tf/src/performance/mod.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
pub mod dm_performance;
|
||||
pub mod medic_performance;
|
||||
pub mod overall_performance;
|
||||
pub mod score;
|
||||
|
||||
use dm_performance::DMPerformance;
|
||||
use json::JsonValue;
|
||||
use medic_performance::MedicPerformance;
|
||||
use overall_performance::OverallPerformance;
|
||||
|
||||
pub use self::score::Score;
|
||||
|
||||
/// A `Performance` contains what a player has done in the course of a game. It
|
||||
/// contains either a generic performance, where data is not available on a per
|
||||
/// class basis and the specific performance with information of that class,
|
||||
/// being either a DM class or the medic.
|
||||
pub enum Performance
|
||||
{
|
||||
Overall(OverallPerformance),
|
||||
DM(DMPerformance),
|
||||
Med(MedicPerformance),
|
||||
}
|
||||
|
||||
impl Performance
|
||||
{
|
||||
pub fn extract_all_from_json(score: &Score, json: &JsonValue) -> Vec<Performance>
|
||||
{
|
||||
let overall_performance = OverallPerformance::from_json(score, json);
|
||||
let dm_performances = DMPerformance::extract_all_from_json(json);
|
||||
let med_performance = MedicPerformance::extract_from_json(json);
|
||||
|
||||
let mut performances = vec![overall_performance.into()];
|
||||
|
||||
for dm_perf in dm_performances {
|
||||
performances.push(dm_perf.into());
|
||||
}
|
||||
|
||||
if let Some(med_performance) = med_performance {
|
||||
performances.push(med_performance.into());
|
||||
}
|
||||
|
||||
performances
|
||||
}
|
||||
}
|
||||
53
logs_tf/src/performance/overall_performance.rs
Normal file
53
logs_tf/src/performance/overall_performance.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use json::JsonValue;
|
||||
|
||||
use super::score::{Score, Team};
|
||||
use super::Performance;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OverallPerformance
|
||||
{
|
||||
pub won_rounds: u8,
|
||||
pub num_rounds: u8,
|
||||
pub damage: u32,
|
||||
pub damage_taken: u32,
|
||||
pub kills: u8,
|
||||
pub deaths: u8,
|
||||
pub num_medkits: u16,
|
||||
pub medkits_hp: u32,
|
||||
}
|
||||
|
||||
impl OverallPerformance
|
||||
{
|
||||
pub fn from_json(score: &Score, json: &JsonValue) -> Self
|
||||
{
|
||||
let team = Team::from_str(json["team"].as_str().unwrap()).unwrap();
|
||||
let won_rounds = score.get_score(team);
|
||||
let lost_rounds = score.get_score(team.other());
|
||||
let num_rounds = won_rounds + lost_rounds;
|
||||
|
||||
let damage = json["dmg"].as_u32().unwrap_or(0);
|
||||
let damage_taken = json["dt"].as_u32().unwrap_or(0);
|
||||
let kills = json["kills"].as_u8().unwrap_or(0);
|
||||
let deaths = json["deaths"].as_u8().unwrap_or(0);
|
||||
let num_medkits = json["medkits"].as_u16().unwrap_or(0);
|
||||
let medkits_hp = json["medkits_hp"].as_u32().unwrap_or(0);
|
||||
|
||||
Self {
|
||||
won_rounds,
|
||||
num_rounds,
|
||||
damage,
|
||||
damage_taken,
|
||||
kills,
|
||||
deaths,
|
||||
num_medkits,
|
||||
medkits_hp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OverallPerformance> for Performance
|
||||
{
|
||||
fn from(value: OverallPerformance) -> Self { Self::Overall(value) }
|
||||
}
|
||||
62
logs_tf/src/performance/score.rs
Normal file
62
logs_tf/src/performance/score.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use json::JsonValue;
|
||||
|
||||
pub struct Score
|
||||
{
|
||||
red: u8,
|
||||
blue: u8,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Team
|
||||
{
|
||||
Red,
|
||||
Blue,
|
||||
}
|
||||
|
||||
impl Score
|
||||
{
|
||||
pub fn new(red: u8, blue: u8) -> Self { Self { red, blue } }
|
||||
|
||||
pub fn from_json(json: &JsonValue) -> Self
|
||||
{
|
||||
let red = json["teams"]["Red"]["score"].as_u8().unwrap();
|
||||
let blue = json["teams"]["Blue"]["score"].as_u8().unwrap();
|
||||
|
||||
Self { red, blue }
|
||||
}
|
||||
|
||||
pub fn get_score(&self, team: Team) -> u8
|
||||
{
|
||||
match team {
|
||||
Team::Red => self.red,
|
||||
Team::Blue => self.blue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Team
|
||||
{
|
||||
pub fn other(self) -> Self
|
||||
{
|
||||
match self {
|
||||
Self::Red => Self::Blue,
|
||||
Self::Blue => Self::Red,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Team
|
||||
{
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err>
|
||||
{
|
||||
match s.trim().to_lowercase().as_str() {
|
||||
"red" => Ok(Self::Red),
|
||||
"blue" => Ok(Self::Blue),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
56
logs_tf/src/query_error.rs
Normal file
56
logs_tf/src/query_error.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
use json::JsonError;
|
||||
use reqwest::Error as HttpError;
|
||||
|
||||
/// Any error that may occur when querying data from logs.tf
|
||||
#[derive(Debug)]
|
||||
pub enum QueryError
|
||||
{
|
||||
/// An error that can occur when the connection to logs.tf is unstable or
|
||||
/// the service is down.
|
||||
HttpResponse(HttpError),
|
||||
/// If for whatever reason an invalid Json file is returned by logs.tf or it
|
||||
/// is corrupted.
|
||||
JsonParseError(JsonError),
|
||||
/// The Json object returned always contains `"success": true` or
|
||||
/// `"success": false` to let the other party know if the query succeeded.
|
||||
/// If it is false, this error is returned.
|
||||
Unsuccessful(String),
|
||||
}
|
||||
|
||||
pub type QueryResult<T> = Result<T, QueryError>;
|
||||
|
||||
impl From<HttpError> for QueryError
|
||||
{
|
||||
fn from(e: HttpError) -> Self { Self::HttpResponse(e) }
|
||||
}
|
||||
impl From<JsonError> for QueryError
|
||||
{
|
||||
fn from(e: JsonError) -> Self { Self::JsonParseError(e) }
|
||||
}
|
||||
|
||||
impl fmt::Display for QueryError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
match self {
|
||||
Self::HttpResponse(http_e) => {
|
||||
write!(f, "An error occured contacting logs.tf: {}", http_e)
|
||||
},
|
||||
Self::JsonParseError(json_e) => {
|
||||
write!(f, "logs.tf did not return valid json: {}", json_e)
|
||||
},
|
||||
Self::Unsuccessful(e) => {
|
||||
write!(
|
||||
f,
|
||||
"logs.tf could not successfully complete the query: {}",
|
||||
e
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for QueryError {}
|
||||
79
logs_tf/src/search_params.rs
Normal file
79
logs_tf/src/search_params.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use std::cmp;
|
||||
|
||||
use reqwest::blocking::RequestBuilder;
|
||||
use ucore::SteamID;
|
||||
|
||||
pub struct SearchParams
|
||||
{
|
||||
pub player_id: Option<SteamID>,
|
||||
pub title: Option<String>,
|
||||
pub limit: Option<u16>,
|
||||
}
|
||||
|
||||
impl SearchParams
|
||||
{
|
||||
pub fn player_id(id: SteamID) -> Self
|
||||
{
|
||||
Self {
|
||||
player_id: Some(id),
|
||||
title: None,
|
||||
limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_title(title: String) -> Self
|
||||
{
|
||||
Self {
|
||||
player_id: None,
|
||||
title: Some(title),
|
||||
limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn limit(limit: u16) -> Self
|
||||
{
|
||||
let limit = cmp::min(limit, 10000);
|
||||
|
||||
Self {
|
||||
player_id: None,
|
||||
title: None,
|
||||
limit: Some(limit),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_player_id(mut self, id: SteamID) -> Self
|
||||
{
|
||||
self.player_id.replace(id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_log_title(mut self, title: String) -> Self
|
||||
{
|
||||
self.title.replace(title);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_limit(mut self, limit: u16) -> Self
|
||||
{
|
||||
self.limit.replace(cmp::min(limit, 10000));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_params_to_request(&self, request_builder: RequestBuilder) -> RequestBuilder
|
||||
{
|
||||
let request_builder = match &self.player_id {
|
||||
Some(id) => request_builder.query(&[("player", &id.to_id64_string())]),
|
||||
None => request_builder,
|
||||
};
|
||||
|
||||
let request_builder = match &self.title {
|
||||
Some(name) => request_builder.query(&[("title", &name)]),
|
||||
None => request_builder,
|
||||
};
|
||||
|
||||
match &self.limit {
|
||||
Some(limit) => request_builder.query(&[("limit", &limit.to_string())]),
|
||||
None => request_builder,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue