use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize)]
pub struct StalkerCredentials {
pub base_url: String,
pub mac_address: String,
#[serde(default)]
pub timezone: Option<String>,
}
impl std::fmt::Debug for StalkerCredentials {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StalkerCredentials")
.field("base_url", &self.base_url)
.field("mac_address", &"[REDACTED]")
.finish()
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerChannel {
pub id: String,
pub name: String,
pub number: Option<u32>,
pub cmd: String,
pub tv_genre_id: Option<String>,
pub logo: Option<String>,
pub epg_channel_id: Option<String>,
#[serde(default)]
pub has_archive: bool,
#[serde(default)]
pub archive_days: u32,
#[serde(default)]
pub is_censored: bool,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerVodItem {
pub id: String,
pub name: String,
pub cmd: String,
pub category_id: Option<String>,
pub logo: Option<String>,
pub description: Option<String>,
pub year: Option<String>,
pub genre: Option<String>,
pub rating: Option<String>,
pub director: Option<String>,
pub cast: Option<String>,
pub duration: Option<String>,
pub tmdb_id: Option<i64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerSeriesItem {
pub id: String,
pub name: String,
pub category_id: Option<String>,
pub logo: Option<String>,
pub description: Option<String>,
pub year: Option<String>,
pub genre: Option<String>,
pub rating: Option<String>,
pub director: Option<String>,
pub cast: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerSeason {
pub id: String,
pub name: String,
pub movie_id: String,
pub logo: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerEpisode {
pub id: String,
pub name: String,
pub movie_id: String,
pub season_id: String,
pub episode_number: Option<u32>,
pub cmd: String,
pub logo: Option<String>,
pub description: Option<String>,
pub duration: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerEpgEntry {
pub name: String,
pub start_timestamp: Option<i64>,
pub end_timestamp: Option<i64>,
pub description: Option<String>,
pub category: Option<String>,
pub duration: Option<i64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerCategory {
pub id: String,
pub title: String,
#[serde(default)]
pub is_adult: bool,
}
#[derive(Debug, Clone)]
pub struct PaginatedResult<T> {
pub items: Vec<T>,
pub total_items: u32,
pub max_page_items: u32,
}
impl<T> PaginatedResult<T> {
pub fn total_pages(&self) -> u32 {
if self.max_page_items == 0 {
return 1;
}
self.total_items.div_ceil(self.max_page_items)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerAccountInfo {
pub login: Option<String>,
pub mac: Option<String>,
pub status: Option<String>,
pub expiration: Option<String>,
pub subscribed_till: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerSeriesDetail {
pub series: StalkerSeriesItem,
pub seasons: Vec<StalkerSeason>,
pub episodes: std::collections::HashMap<String, Vec<StalkerEpisode>>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StalkerProfile {
pub timezone: Option<String>,
pub locale: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn credentials_debug_redacts_mac() {
let creds = StalkerCredentials {
base_url: "http://example.com".into(),
mac_address: "00:1A:79:AB:CD:EF".into(),
timezone: None,
};
let debug = format!("{creds:?}");
assert!(debug.contains("[REDACTED]"));
assert!(!debug.contains("00:1A:79"));
}
#[test]
fn paginated_result_total_pages() {
let result: PaginatedResult<()> = PaginatedResult {
items: vec![],
total_items: 25,
max_page_items: 10,
};
assert_eq!(result.total_pages(), 3);
}
#[test]
fn paginated_result_exact_division() {
let result: PaginatedResult<()> = PaginatedResult {
items: vec![],
total_items: 20,
max_page_items: 10,
};
assert_eq!(result.total_pages(), 2);
}
#[test]
fn paginated_result_zero_max_page_items() {
let result: PaginatedResult<()> = PaginatedResult {
items: vec![],
total_items: 5,
max_page_items: 0,
};
assert_eq!(result.total_pages(), 1);
}
}