Skip to main content

the_odds_api/
models.rs

1//! Data models for API responses.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6/// A sport available in the API.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Sport {
9    /// Unique identifier for the sport (e.g., "americanfootball_nfl").
10    pub key: String,
11    /// Group/category the sport belongs to (e.g., "American Football").
12    pub group: String,
13    /// Human-readable title (e.g., "NFL").
14    pub title: String,
15    /// Description of the sport.
16    pub description: String,
17    /// Whether the sport is currently in season.
18    pub active: bool,
19    /// Whether the sport has outright/futures markets.
20    pub has_outrights: bool,
21}
22
23/// A sporting event.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct Event {
26    /// Unique identifier for the event.
27    pub id: String,
28    /// Sport key this event belongs to.
29    pub sport_key: String,
30    /// Human-readable sport title.
31    pub sport_title: String,
32    /// When the event starts.
33    pub commence_time: DateTime<Utc>,
34    /// Home team name (if applicable).
35    pub home_team: Option<String>,
36    /// Away team name (if applicable).
37    pub away_team: Option<String>,
38}
39
40/// An event with odds from bookmakers.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct EventOdds {
43    /// Unique identifier for the event.
44    pub id: String,
45    /// Sport key this event belongs to.
46    pub sport_key: String,
47    /// Human-readable sport title.
48    pub sport_title: String,
49    /// When the event starts.
50    pub commence_time: DateTime<Utc>,
51    /// Home team name (if applicable).
52    pub home_team: Option<String>,
53    /// Away team name (if applicable).
54    pub away_team: Option<String>,
55    /// List of bookmakers with their odds.
56    pub bookmakers: Vec<Bookmaker>,
57}
58
59/// An event with scores.
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct EventScore {
62    /// Unique identifier for the event.
63    pub id: String,
64    /// Sport key this event belongs to.
65    pub sport_key: String,
66    /// Human-readable sport title.
67    pub sport_title: String,
68    /// When the event starts.
69    pub commence_time: DateTime<Utc>,
70    /// Home team name (if applicable).
71    pub home_team: Option<String>,
72    /// Away team name (if applicable).
73    pub away_team: Option<String>,
74    /// Whether the event has been completed.
75    pub completed: bool,
76    /// Current scores for each team.
77    pub scores: Option<Vec<Score>>,
78    /// Last update time for scores.
79    pub last_update: Option<DateTime<Utc>>,
80}
81
82/// Score for a team in an event.
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct Score {
85    /// Team name.
86    pub name: String,
87    /// Current score (as string to handle various formats).
88    pub score: String,
89}
90
91/// A bookmaker with odds for various markets.
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct Bookmaker {
94    /// Unique key for the bookmaker.
95    pub key: String,
96    /// Human-readable bookmaker title.
97    pub title: String,
98    /// When the odds were last updated.
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub last_update: Option<DateTime<Utc>>,
101    /// Markets offered by this bookmaker.
102    pub markets: Vec<MarketOdds>,
103    /// Optional deep link to the bookmaker's page.
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub link: Option<String>,
106    /// Optional site ID.
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub sid: Option<String>,
109}
110
111/// Odds for a specific market from a bookmaker.
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct MarketOdds {
114    /// Market key (e.g., "h2h", "spreads", "totals").
115    pub key: String,
116    /// When the market odds were last updated.
117    pub last_update: Option<DateTime<Utc>>,
118    /// Outcomes/selections for this market.
119    pub outcomes: Vec<Outcome>,
120}
121
122/// An outcome/selection within a market.
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct Outcome {
125    /// Name of the outcome (team name, "Over", "Under", etc.).
126    pub name: String,
127    /// The odds price.
128    pub price: f64,
129    /// Point spread or total line (for spreads/totals markets).
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub point: Option<f64>,
132    /// Description for player props or alternate markets.
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub description: Option<String>,
135    /// Bet link URL if available.
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub link: Option<String>,
138    /// DFS multiplier if requested.
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub multiplier: Option<f64>,
141}
142
143/// A participant (team or player) in a sport.
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct Participant {
146    /// Unique identifier for the participant.
147    pub id: String,
148    /// Full name of the team or player.
149    pub full_name: String,
150}
151
152/// A market entry with its key and last update time (without odds).
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct MarketInfo {
155    /// Market key (e.g., "h2h", "spreads", "totals").
156    pub key: String,
157    /// When the market was last updated.
158    pub last_update: Option<DateTime<Utc>>,
159}
160
161/// Bookmaker with available markets (without odds).
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct BookmakerMarkets {
164    /// Unique key for the bookmaker.
165    pub key: String,
166    /// Human-readable bookmaker title.
167    pub title: String,
168    /// List of available markets.
169    pub markets: Vec<MarketInfo>,
170}
171
172/// Event with bookmaker market availability.
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct EventMarkets {
175    /// Unique identifier for the event.
176    pub id: String,
177    /// Sport key this event belongs to.
178    pub sport_key: String,
179    /// Human-readable sport title.
180    pub sport_title: String,
181    /// When the event starts.
182    pub commence_time: DateTime<Utc>,
183    /// Home team name (if applicable).
184    pub home_team: Option<String>,
185    /// Away team name (if applicable).
186    pub away_team: Option<String>,
187    /// Bookmakers with their available markets.
188    pub bookmakers: Vec<BookmakerMarkets>,
189}
190
191/// Wrapper for historical API responses.
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct HistoricalResponse<T> {
194    /// The timestamp of the historical snapshot.
195    pub timestamp: DateTime<Utc>,
196    /// Previous available timestamp.
197    pub previous_timestamp: Option<DateTime<Utc>>,
198    /// Next available timestamp.
199    pub next_timestamp: Option<DateTime<Utc>>,
200    /// The actual data.
201    pub data: T,
202}
203
204/// API usage information from response headers.
205#[derive(Debug, Clone, Default)]
206pub struct UsageInfo {
207    /// Remaining API requests in the current period.
208    pub requests_remaining: Option<u32>,
209    /// Total requests used in the current period.
210    pub requests_used: Option<u32>,
211    /// Cost of the last request.
212    pub requests_last: Option<u32>,
213}
214
215impl UsageInfo {
216    /// Parse usage info from response headers.
217    pub(crate) fn from_headers(headers: &reqwest::header::HeaderMap) -> Self {
218        Self {
219            requests_remaining: headers
220                .get("x-requests-remaining")
221                .and_then(|v| v.to_str().ok())
222                .and_then(|v| v.parse().ok()),
223            requests_used: headers
224                .get("x-requests-used")
225                .and_then(|v| v.to_str().ok())
226                .and_then(|v| v.parse().ok()),
227            requests_last: headers
228                .get("x-requests-last")
229                .and_then(|v| v.to_str().ok())
230                .and_then(|v| v.parse().ok()),
231        }
232    }
233}
234
235/// A response with data and API usage information.
236#[derive(Debug, Clone)]
237pub struct Response<T> {
238    /// The response data.
239    pub data: T,
240    /// API usage information.
241    pub usage: UsageInfo,
242}
243
244impl<T> Response<T> {
245    /// Create a new response with data and usage info.
246    pub fn new(data: T, usage: UsageInfo) -> Self {
247        Self { data, usage }
248    }
249
250    /// Map the data to a new type.
251    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Response<U> {
252        Response {
253            data: f(self.data),
254            usage: self.usage,
255        }
256    }
257}