Skip to content

Commit 481eddb

Browse files
committed
```
Refactor query logic and remove unused features - Replace `command` module with `query` and `query_multiple` modules - Simplify `RawServerAddress` and remove `ServerAddress` abstraction - Remove `serde` and `serde_json` dependencies and related features - Update tests to reflect new structure - Bump version to 0.8.0 ```
1 parent ed00654 commit 481eddb

File tree

6 files changed

+264
-289
lines changed

6 files changed

+264
-289
lines changed

Cargo.toml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description = "Get server addresses from QuakeWorld master servers."
44
keywords = ["masters", "quake", "quakeworld", "servers"]
55
repository = "https://github.com/quakeworld/masterstat"
66
authors = ["Viktor Persson <viktor.persson@arcsin.se>"]
7-
version = "0.7.0"
7+
version = "0.8.0"
88
edition = "2024"
99
license = "MIT"
1010
include = [
@@ -24,12 +24,8 @@ futures = "0.3.31"
2424
tokio = { version = "1.44.1", features = ["macros", "net", "rt-multi-thread", "sync", "time"] }
2525
tinyudp = "0.5.1"
2626

27-
serde = { optional = true, version = "1.0.219", features = ["derive"] }
28-
serde_json = { optional = true, version = "1.0.140" }
29-
3027
[dev-dependencies]
3128
pretty_assertions = "1.4.1"
3229

3330
[features]
3431
ci = []
35-
json = ["dep:serde", "dep:serde_json"]

src/command.rs

Lines changed: 0 additions & 199 deletions
This file was deleted.

src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
//!
33
//! Get server addresses from QuakeWorld master servers.
44
5-
mod command;
5+
mod query;
6+
mod query_multiple;
67
mod server_address;
78

8-
pub use crate::command::{server_addresses, server_addresses_from_many};
9-
pub use crate::server_address::ServerAddress;
9+
pub use crate::query::query;
10+
pub use crate::query_multiple::{MultiQueryResult, query_multiple};

src/query.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use std::{io::Cursor, time::Duration};
2+
3+
use anyhow::{Result, anyhow as e};
4+
use binrw::BinRead;
5+
6+
use crate::server_address::RawServerAddress;
7+
8+
/// Get server addresses from a single master server
9+
///
10+
/// # Example
11+
///
12+
/// ```
13+
/// use std::time::Duration;
14+
///
15+
/// async fn test() {
16+
/// let master = "master.quakeworld.nu:27000";
17+
/// let timeout = Duration::from_secs(2);
18+
/// match masterstat::query(&master, timeout).await {
19+
/// Ok(addresses) => { println!("found {} server addresses", addresses.len()) },
20+
/// Err(e) => { eprintln!("error: {}", e); }
21+
/// }
22+
/// }
23+
/// ```
24+
pub async fn query(master_address: &str, timeout: Duration) -> Result<Vec<String>> {
25+
const STATUS_MSG: [u8; 3] = [99, 10, 0];
26+
let response = tinyudp::send_and_receive(
27+
master_address,
28+
&STATUS_MSG,
29+
tinyudp::ReadOptions {
30+
timeout,
31+
buffer_size: 64 * 1024, // 64 kb
32+
},
33+
)
34+
.await?;
35+
parse_response(&response)
36+
}
37+
38+
fn parse_response(response: &[u8]) -> Result<Vec<String>> {
39+
const RESPONSE_HEADER: [u8; 6] = [255, 255, 255, 255, 100, 10];
40+
41+
if !response.starts_with(&RESPONSE_HEADER) {
42+
return Err(e!("Invalid response"));
43+
}
44+
45+
let body = &mut Cursor::new(&response[RESPONSE_HEADER.len()..]);
46+
let mut addresses = vec![];
47+
48+
while let Ok(raw_address) = RawServerAddress::read(body) {
49+
addresses.push(raw_address.to_string());
50+
}
51+
52+
Ok(addresses)
53+
}
54+
55+
#[cfg(test)]
56+
mod tests {
57+
use super::*;
58+
use pretty_assertions::assert_eq;
59+
60+
#[tokio::test]
61+
#[cfg_attr(feature = "ci", ignore)]
62+
async fn test_query() -> Result<()> {
63+
let master = "master.quakeservers.net:27000";
64+
let timeout = Duration::from_secs(2);
65+
let result = query(master, timeout).await?;
66+
assert!(!result.is_empty());
67+
Ok(())
68+
}
69+
70+
#[tokio::test]
71+
async fn test_parse_response() -> Result<()> {
72+
// invalid response header
73+
{
74+
let response = [0xff, 0xff];
75+
let result = parse_response(&response);
76+
assert_eq!(result.unwrap_err().to_string(), "Invalid response");
77+
}
78+
79+
// valid response
80+
{
81+
let response = [
82+
0xff, 0xff, 0xff, 0xff, 0x64, 0x0a, 192, 168, 1, 1, 0x75, 0x30, 192, 168, 1, 2,
83+
0x75, 0x30,
84+
];
85+
let result = parse_response(&response)?;
86+
assert_eq!(result.len(), 2);
87+
assert_eq!(result[0], "192.168.1.1:30000".to_string());
88+
assert_eq!(result[1], "192.168.1.2:30000".to_string());
89+
}
90+
91+
Ok(())
92+
}
93+
}

0 commit comments

Comments
 (0)