1use serde::{Deserialize, Serialize};
4
5pub type FetchResult<T> = Result<T, FetchError>;
7
8#[derive(Debug, Clone, thiserror::Error)]
10pub enum FetchError {
11 #[error("Network error: {0}")]
12 Network(String),
13
14 #[error("HTTP request failed: {0}")]
15 Http(String),
16
17 #[error("JSON parsing error: {0}")]
18 Json(String),
19
20 #[error("Channel not found: {0}")]
21 ChannelNotFound(String),
22
23 #[error("Rate limit exceeded")]
24 RateLimit,
25
26 #[error("Invalid response format")]
27 InvalidResponse,
28
29 #[error("Timeout error")]
30 Timeout,
31
32 #[error("Authentication required")]
33 AuthenticationRequired,
34}
35
36#[derive(Debug, Clone)]
38pub struct ClientConfig {
39 pub user_agent: Option<String>,
41 pub timeout_seconds: u64,
43 pub max_retries: u32,
45 pub retry_delay_ms: u64,
47 pub enable_logging: bool,
49 pub custom_headers: Vec<(String, String)>,
51}
52
53impl Default for ClientConfig {
54 fn default() -> Self {
55 Self {
56 user_agent: Some(crate::fetch::useragent::get_latest_chrome_user_agent().to_string()),
57 timeout_seconds: 30,
58 max_retries: 3,
59 retry_delay_ms: 1000,
60 enable_logging: false,
61 custom_headers: Vec::new(),
62 }
63 }
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ChannelInfo {
69 pub id: u64,
71 pub slug: String,
73 pub title: Option<String>,
75 pub followers_count: Option<u64>,
77 pub subscribers_count: Option<u64>,
79 pub is_live: bool,
81 pub viewers_count: Option<u64>,
83 pub category: Option<String>,
85 pub tags: Option<Vec<String>>,
87 pub language: Option<String>,
89 pub user: Option<UserInfo>,
91 pub chatroom: Option<ChatroomInfo>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct UserInfo {
98 pub id: u64,
100 pub username: String,
102 pub display_name: Option<String>,
104 pub avatar_url: Option<String>,
106 pub bio: Option<String>,
108 pub created_at: Option<String>,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct ChatroomInfo {
115 pub id: u64,
117 pub channel_id: u64,
119 pub name: String,
121 pub chatroom_type: Option<String>,
123 pub slow_mode: Option<u32>,
125}
126
127#[derive(Debug, Clone)]
129pub struct StrategyConfig {
130 pub random_delay_range: (u64, u64),
132}
133
134impl Default for StrategyConfig {
135 fn default() -> Self {
136 Self {
137 random_delay_range: (100, 500),
138 }
139 }
140}
141
142#[derive(Debug, Clone)]
144pub struct RequestContext {
145 pub channel_name: String,
147 pub strategy: String,
149 pub attempt: u32,
151 pub timestamp: chrono::DateTime<chrono::Utc>,
153 pub user_agent: String,
155}
156
157impl RequestContext {
158 pub fn new(channel_name: &str, strategy: &str) -> Self {
159 Self {
160 channel_name: channel_name.to_string(),
161 strategy: strategy.to_string(),
162 attempt: 1,
163 timestamp: chrono::Utc::now(),
164 user_agent: "unknown".to_string(),
165 }
166 }
167}
168
169#[derive(Debug, Clone, Default)]
171pub struct FetchMetrics {
172 pub total_requests: u64,
174 pub successful_requests: u64,
176 pub failed_requests: u64,
178 pub avg_response_time_ms: f64,
180 pub cache_hits: u64,
182 pub rate_limit_hits: u64,
184}
185
186impl FetchMetrics {
187 pub fn success_rate(&self) -> f64 {
188 if self.total_requests == 0 {
189 0.0
190 } else {
191 self.successful_requests as f64 / self.total_requests as f64 * 100.0
192 }
193 }
194
195 pub fn record_success(&mut self, response_time_ms: u64) {
196 self.total_requests += 1;
197 self.successful_requests += 1;
198 self.update_avg_response_time(response_time_ms);
199 }
200
201 pub fn record_failure(&mut self) {
202 self.total_requests += 1;
203 self.failed_requests += 1;
204 }
205
206 fn update_avg_response_time(&mut self, response_time_ms: u64) {
207 if self.total_requests == 1 {
208 self.avg_response_time_ms = response_time_ms as f64;
209 } else {
210 self.avg_response_time_ms = (self.avg_response_time_ms * (self.total_requests - 1) as f64 + response_time_ms as f64) / self.total_requests as f64;
211 }
212 }
213}