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    pub last_update: DateTime<Utc>,
100    /// Markets offered by this bookmaker.
101    pub markets: Vec<MarketOdds>,
102    /// Optional deep link to the bookmaker's page.
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub link: Option<String>,
105    /// Optional site ID.
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub sid: Option<String>,
108}
109
110/// Odds for a specific market from a bookmaker.
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct MarketOdds {
113    /// Market key (e.g., "h2h", "spreads", "totals").
114    pub key: String,
115    /// When the market odds were last updated.
116    pub last_update: Option<DateTime<Utc>>,
117    /// Outcomes/selections for this market.
118    pub outcomes: Vec<Outcome>,
119}
120
121/// An outcome/selection within a market.
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct Outcome {
124    /// Name of the outcome (team name, "Over", "Under", etc.).
125    pub name: String,
126    /// The odds price.
127    pub price: f64,
128    /// Point spread or total line (for spreads/totals markets).
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub point: Option<f64>,
131    /// Description for player props or alternate markets.
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub description: Option<String>,
134    /// Bet link URL if available.
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub link: Option<String>,
137    /// DFS multiplier if requested.
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub multiplier: Option<f64>,
140}
141
142/// A participant (team or player) in a sport.
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct Participant {
145    /// Unique identifier for the participant.
146    pub id: String,
147    /// Full name of the team or player.
148    pub full_name: String,
149}
150
151/// Bookmaker with available markets (without odds).
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct BookmakerMarkets {
154    /// Unique key for the bookmaker.
155    pub key: String,
156    /// Human-readable bookmaker title.
157    pub title: String,
158    /// List of available market keys.
159    pub markets: Vec<String>,
160}
161
162/// Event with bookmaker market availability.
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct EventMarkets {
165    /// Unique identifier for the event.
166    pub id: String,
167    /// Sport key this event belongs to.
168    pub sport_key: String,
169    /// Human-readable sport title.
170    pub sport_title: String,
171    /// When the event starts.
172    pub commence_time: DateTime<Utc>,
173    /// Home team name (if applicable).
174    pub home_team: Option<String>,
175    /// Away team name (if applicable).
176    pub away_team: Option<String>,
177    /// Bookmakers with their available markets.
178    pub bookmakers: Vec<BookmakerMarkets>,
179}
180
181/// Wrapper for historical API responses.
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct HistoricalResponse<T> {
184    /// The timestamp of the historical snapshot.
185    pub timestamp: DateTime<Utc>,
186    /// Previous available timestamp.
187    pub previous_timestamp: Option<DateTime<Utc>>,
188    /// Next available timestamp.
189    pub next_timestamp: Option<DateTime<Utc>>,
190    /// The actual data.
191    pub data: T,
192}
193
194/// API usage information from response headers.
195#[derive(Debug, Clone, Default)]
196pub struct UsageInfo {
197    /// Remaining API requests in the current period.
198    pub requests_remaining: Option<u32>,
199    /// Total requests used in the current period.
200    pub requests_used: Option<u32>,
201    /// Cost of the last request.
202    pub requests_last: Option<u32>,
203}
204
205impl UsageInfo {
206    /// Parse usage info from response headers.
207    pub(crate) fn from_headers(headers: &reqwest::header::HeaderMap) -> Self {
208        Self {
209            requests_remaining: headers
210                .get("x-requests-remaining")
211                .and_then(|v| v.to_str().ok())
212                .and_then(|v| v.parse().ok()),
213            requests_used: headers
214                .get("x-requests-used")
215                .and_then(|v| v.to_str().ok())
216                .and_then(|v| v.parse().ok()),
217            requests_last: headers
218                .get("x-requests-last")
219                .and_then(|v| v.to_str().ok())
220                .and_then(|v| v.parse().ok()),
221        }
222    }
223}
224
225/// A response with data and API usage information.
226#[derive(Debug, Clone)]
227pub struct Response<T> {
228    /// The response data.
229    pub data: T,
230    /// API usage information.
231    pub usage: UsageInfo,
232}
233
234impl<T> Response<T> {
235    /// Create a new response with data and usage info.
236    pub fn new(data: T, usage: UsageInfo) -> Self {
237        Self { data, usage }
238    }
239
240    /// Map the data to a new type.
241    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Response<U> {
242        Response {
243            data: f(self.data),
244            usage: self.usage,
245        }
246    }
247}