Skip to content

Commit 03c54d1

Browse files
authored
docs: better docs (#18)
1 parent e06e1d9 commit 03c54d1

34 files changed

+612
-77
lines changed

CONTRIBUTING.md

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Tooling
2+
We use the basic Rust tooling. Clippy, rustmft, etc.
3+
4+
If you have any recommendations regarding a `rustfmt.toml`, please let us know/make a PR.
5+
6+
# Contributing
7+
If you have a great idea for a feature, let us know [on the Discord](https://discord.gg/jGZxH9f).
8+
9+
Alternatively, you can open an issue.
10+
11+
# Project structure
12+
To save you some time, here's a brief explanation of how this project is structured:
13+
14+
There are 2 modules for the "major" things you might want to do, that is querying the [worldstate](https://docs.warframestat.us) and the [market](https://warframe.market/api_docs) (with the `market` feature).
15+
16+
The `worldstate` module is much more developed. This is due to the market API getting a V2 soon.
17+
18+
Since the `market` module is rather small and easy to understand, we'll talk about the `worldstate` module.
19+
20+
## Worldstate module
21+
All the models are defined via a function-like macro in the `worldstate/models` folder.
22+
23+
### The `model_builder!` macro
24+
For example, let's look at the definition for `Cetus`:
25+
```rs
26+
model_builder! {
27+
:"The Information about cetus"
28+
Cetus: "/cetusCycle",
29+
rt = obj,
30+
timed = true;
31+
32+
:"The id of the cycle"
33+
pub id: String,
34+
35+
:"The state of Cetus (day/night)"
36+
pub state: CetusState,
37+
}
38+
```
39+
Doc strings are made using the `:"doc here"` syntax. Followed by the `TypeName: "/endpoint_url"`. Said endpoints get concatenated via
40+
```rs
41+
concat!("https://api.warframestat.us/pc", $endpoint, "/?language=en")
42+
```
43+
at compile time. This prevents unnecessary allocations. Of course, this doesn't work when you want to query in another language.
44+
45+
When a type has this optional `: "/endpoint"`, it will implement the `Endpoint` trait like so:
46+
47+
```rs
48+
impl Endpoint for $struct_name {
49+
fn endpoint_en() -> &'static str {
50+
concat!("https://api.warframestat.us/pc", $endpoint, "/?language=en")
51+
}
52+
53+
#[cfg(feature = "multilangual")]
54+
fn endpoint(language: Language) -> String {
55+
format!(
56+
"https://api.warframestat.us/pc{}/?language={}",
57+
$endpoint,
58+
String::from(language)
59+
)
60+
}
61+
}
62+
```
63+
64+
This is followed by an `rt = obj/arr`, which tells the model in which format it is returned in.
65+
For example, there are no more than 1 `Cetus` at a time, so the API responds with a single `Cetus` object, hence `rt = obj`. `Fissure`s on the other hand have multiple active at a time, so the API responds with an array of those fissures, hence on fissures it's `rt = arr`.
66+
67+
Next is `timed = true`. This is some trickery, because models who have this set to true will get 2 fields: `activation` and `expiry`, and will additionally implement the `TimedEvent` trait.
68+
69+
### Putting it all together
70+
To understand this, lets look at the `Queryable` trait first:
71+
```rs
72+
pub trait Queryable: Endpoint {
73+
type Return: DeserializeOwned;
74+
fn query(
75+
request_executor: &reqwest::Client,
76+
) -> impl std::future::Future<Output = Result<Self::Return, ApiError>> + Send {
77+
async {
78+
Ok(request_executor
79+
.get(Self::endpoint_en())
80+
.send()
81+
.await?
82+
.json::<Self::Return>()
83+
.await?)
84+
}
85+
}
86+
87+
#[cfg(feature = "multilangual")]
88+
fn query_with_language(
89+
...
90+
}
91+
```
92+
93+
if a model has the endpoint signature (`: "/endpoint"`), the `Queryable` trait will be implemented by the macro.
94+
Based on the `rt`, the `type Return` will either be `Self`, or `Vec<Self>`.
95+
96+
Now, all the `Client`'s `fetch` does:
97+
```rs
98+
impl Client {
99+
pub async fn fetch<T>(&self) -> Result<T::Return, ApiError>
100+
where
101+
T: Queryable,
102+
{
103+
<T as Queryable>::query(&self.session).await
104+
}
105+
}
106+
```
107+
108+
This means, depending on the type queried, you get a `Vec<Model>`, or a single `Model`.
109+
110+
E.g.
111+
```rs
112+
let fissures: Vec<Fissure> = client.fetch<Fissure>().await?;
113+
let cetus: Cetus = client.fetch<Cetus>().await?;
114+
```
115+
116+
If you have any questions, feel free to ask on the discord, or open an issue.

Cargo.toml

+11-10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ license = "MIT"
1313
[features]
1414
default = ["worldstate"]
1515

16+
full = ["worldstate_full", "market_full"]
1617
worldstate = []
1718
multilangual = ["worldstate"]
1819
worldstate_listeners = ["worldstate"]
@@ -22,14 +23,14 @@ market_cache = ["market", "dep:moka"]
2223
market_full = ["market", "market_cache"]
2324

2425
[dependencies]
25-
tokio = { version = "1.34.0", features = ["full"] }
26-
reqwest = { version = "0.12.5", features = ["json"] }
27-
chrono = { version = "0.4.31", features = ["serde", "clock"] }
28-
serde = { version = "1.0.190", features = ["derive"] }
29-
serde_json = { version = "1.0.108" }
30-
serde_repr = "0.1.18"
26+
tokio = { version = "1.39.3", features = ["full"] }
27+
reqwest = { version = "0.12.7", features = ["json"] }
28+
chrono = { version = "0.4.38", features = ["serde", "clock"] }
29+
serde = { version = "1.0.209", features = ["derive"] }
30+
serde_json = { version = "1.0.127" }
31+
serde_repr = "0.1.19"
3132
futures = "0.3.30"
32-
log = "0.4.20"
33-
env_logger = "0.11.1"
34-
thiserror = "1.0.61"
35-
moka = { version = "0.12.7", optional = true, features = ["future"] }
33+
log = "0.4.22"
34+
env_logger = "0.11.5"
35+
thiserror = "1.0.63"
36+
moka = { version = "0.12.8", optional = true, features = ["future"] }

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Use this crate if you want to make a Warframe-related rust project that is async
88
To install, simply run `cargo add warframe`.
99

1010
### Example
11-
```rs
11+
```rust,no_run
1212
use warframe::worldstate::prelude::*;
1313
1414
#[tokio::main]
@@ -31,7 +31,7 @@ async fn main() -> Result<(), ApiError> {
3131
```
3232

3333
## Contributing
34-
Contributions are more than welcome. To contribute simply fork this repository and make a PR.
34+
See [CONTRIBUTING](CONTRIBUTING.md)
3535

3636
### Commitlint
3737

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![doc = include_str!("../README.md")]
22

33
#[cfg(feature = "worldstate")]
4+
#[forbid(missing_docs)]
45
pub mod worldstate;
56

67
#[cfg(feature = "market")]

src/market/client.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Provides a client that acts as the baseline for interacting with the market API
2+
3+
#[allow(unused_imports)]
14
use std::{sync::Arc, time::Duration};
25

36
use super::{
@@ -12,13 +15,19 @@ use super::{
1215

1316
#[cfg(feature = "market_cache")]
1417
#[derive(Debug, Clone, PartialEq, PartialOrd)]
18+
#[doc = "A cached value"]
1519
pub enum CacheValue {
20+
/// StatisticItem
1621
StatisticItem(Arc<StatisticItem>),
22+
/// ItemInfo
1723
ItemInfo(Arc<ItemInfo>),
24+
/// Items
1825
Items(Arc<Vec<Item>>),
26+
/// Orders
1927
Orders(Arc<Vec<Order>>),
2028
}
2129

30+
/// The client
2231
#[derive(Debug, Clone)]
2332
#[cfg_attr(not(feature = "market_cache"), derive(Default))]
2433
pub struct Client {
@@ -28,6 +37,7 @@ pub struct Client {
2837
}
2938

3039
impl Client {
40+
/// Creates a new [Client]
3141
pub fn new() -> Self {
3242
Default::default()
3343
}
@@ -62,7 +72,9 @@ impl Client {
6272
.await?;
6373

6474
if response.status().is_success() {
65-
let json_result = response.json::<ItemInfoPayload>().await?;
75+
let json_result = response
76+
.json::<crate::market::models::ItemInfoPayload>()
77+
.await?;
6678
Ok(json_result.payload.item)
6779
} else {
6880
Err(response.status().into())
@@ -78,7 +90,9 @@ impl Client {
7890
.await?;
7991

8092
if response.status().is_success() {
81-
let json_result = response.json::<ItemsPayload>().await?;
93+
let json_result = response
94+
.json::<crate::market::models::ItemsPayload>()
95+
.await?;
8296
Ok(json_result.payload.items)
8397
} else {
8498
Err(response.status().into())
@@ -96,14 +110,17 @@ impl Client {
96110
.await?;
97111

98112
if response.status().is_success() {
99-
let json_result = response.json::<OrderPayload>().await?;
113+
let json_result = response
114+
.json::<crate::market::models::OrderPayload>()
115+
.await?;
100116
Ok(json_result.payload.orders)
101117
} else {
102118
Err(response.status().into())
103119
}
104120
}
105121
}
106122

123+
/// The cached version of the client
107124
#[cfg(feature = "market_cache")]
108125
pub mod cached {
109126
use {
@@ -117,13 +134,17 @@ pub mod cached {
117134
pub use moka;
118135
use reqwest::Response;
119136

137+
/// Whether an item has been gotten via a cache hit or freshly fetched.
120138
pub enum FetchResult {
139+
/// Cache hit
121140
Cached(CacheValue),
141+
/// Fetched
122142
Fetched(Result<Response, ApiError>),
123143
}
124144

125145
#[cfg(feature = "market_cache")]
126146
impl Client {
147+
/// Creates a new client with a custom cache
127148
pub fn new_with_cache(cache: Cache<String, CacheValue>) -> Self {
128149
Self {
129150
session: Default::default(),

src/market/error.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
//! This module defines error types
2+
3+
/// The market's error type
14
#[derive(Debug, thiserror::Error)]
25
pub enum ApiError {
6+
/// An error from the sent request
37
#[error("Couldn't send request: {0}")]
48
FaultyRequest(#[from] reqwest::Error),
59

10+
/// An error that occurs when the deserialization of serde_json fails
611
#[error("Couldn't deserialize json body: {0}")]
712
FailedDeserialization(#[from] serde_json::Error),
813

14+
/// Any error directly from the API (status code only)
915
#[error("Error response from the API: {0}")]
1016
ApiError(reqwest::StatusCode),
1117
}

src/market/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//! Implementation for the market module, used to interact with the warframe.market API
2+
13
pub mod client;
24
pub mod error;
35
pub mod models;

src/market/models/item.rs

+5
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@ pub(crate) struct Items {
1212

1313
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)]
1414
pub struct Item {
15+
/// thumb
1516
pub thumb: String,
1617

18+
/// item_name
1719
pub item_name: String,
1820

21+
/// url_name
1922
pub url_name: String,
2023

24+
/// id
2125
pub id: String,
2226

2327
#[serde(default)]
28+
/// vaulted
2429
pub vaulted: bool,
2530
}
2631

src/market/models/item_info.rs

+6
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,22 @@ pub struct ItemInSet {
4747
pub info: LanguageItem,
4848
}
4949

50+
/// A Language Item
5051
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)]
5152
pub struct LanguageItem {
53+
/// item_name
5254
pub item_name: String,
5355

56+
/// description
5457
pub description: String,
5558

59+
/// wiki_link
5660
pub wiki_link: String,
5761

62+
/// thumb
5863
pub thumb: String,
5964

65+
/// icon
6066
pub icon: String,
6167
// drop: Vec<Option<serde_json::Value>>, // seems to be empty all the time
6268
}

src/market/models/mod.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
pub mod item;
2-
pub mod item_info;
3-
pub mod orders;
4-
pub mod statistic_item;
1+
pub(crate) mod item;
2+
pub(crate) mod item_info;
3+
pub(crate) mod orders;
4+
pub(crate) mod statistic_item;
5+
6+
pub use item::*;
7+
pub use item_info::*;
8+
pub use orders::*;
9+
pub use statistic_item::*;

src/market/models/statistic_item.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use {
2+
super::OrderType,
23
chrono::{DateTime, Utc},
34
serde::{Deserialize, Serialize},
45
};
@@ -84,13 +85,6 @@ pub struct StatisticsLive48Hour {
8485
pub id: String,
8586
}
8687

87-
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)]
88-
#[serde(rename_all = "snake_case")]
89-
pub enum OrderType {
90-
Buy,
91-
Sell,
92-
}
93-
9488
#[cfg(test)]
9589
mod test {
9690
use crate::market::{client::Client, error::ApiError};

0 commit comments

Comments
 (0)