twitch_highway 0.3.0

Twitch API
Documentation
use chrono::{DateTime, FixedOffset};
use serde::{Deserialize, Serialize};

use crate::types::{BroadcasterId, Outcome, PredictionId, Title};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Prediction {
    pub id: PredictionId,
    pub broadcaster_id: BroadcasterId,
    pub broadcaster_name: String,
    pub broadcaster_login: String,
    pub title: String,
    pub winning_outcome_id: Option<String>,
    pub outcomes: Vec<Outcome>,
    pub prediction_window: u64,
    pub status: PredictionStatus,
    pub created_at: DateTime<FixedOffset>,
    pub ended_at: Option<DateTime<FixedOffset>>,
    pub locked_at: Option<DateTime<FixedOffset>>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PredictionStatus {
    ACTIVE,
    CANCELED,
    LOCKED,
    RESOLVED,
}

impl PredictionStatus {
    pub fn as_str(&self) -> &str {
        match self {
            Self::ACTIVE => "ACTIVE",
            Self::CANCELED => "CANCELED",
            Self::LOCKED => "LOCKED",
            Self::RESOLVED => "RESOLVED",
        }
    }
}

impl AsRef<str> for PredictionStatus {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

#[derive(Serialize)]
pub(crate) struct CreatePredictionBody<'a> {
    pub broadcaster_id: &'a BroadcasterId,
    pub title: &'a str,
    pub outcomes: &'a [Title],
    pub prediction_window: u64,
}

#[cfg(test)]
mod tests {
    use crate::{
        predictions::{Prediction, PredictionStatus},
        types::OutocmeColor,
    };

    #[test]
    fn prediction_status_enum() {
        let statuses = vec![
            (PredictionStatus::ACTIVE, "ACTIVE"),
            (PredictionStatus::CANCELED, "CANCELED"),
            (PredictionStatus::LOCKED, "LOCKED"),
            (PredictionStatus::RESOLVED, "RESOLVED"),
        ];

        for (status, expected_str) in statuses {
            assert_eq!(status.as_str(), expected_str);
            assert_eq!(status.as_ref(), expected_str);

            let serialized = serde_json::to_string(&status).unwrap();
            assert_eq!(serialized, format!("\"{}\"", expected_str));

            let deserialized: PredictionStatus = serde_json::from_str(&serialized).unwrap();
            assert_eq!(deserialized.as_str(), expected_str);
        }
    }

    #[test]
    fn prediction_serialization() {
        use serde_json::json;

        let json_data = json!({
            "id": "test_pred_123",
            "broadcaster_id": "broadcaster456",
            "broadcaster_name": "TestStreamer",
            "broadcaster_login": "teststreamer",
            "title": "Test Prediction",
            "winning_outcome_id": null,
            "outcomes": [
                {
                    "id": "outcome1",
                    "title": "Yes",
                    "users": 10,
                    "channel_points": 5000,
                    "top_predictors": [
                        {
                            "user_id": "user123",
                            "user_name": "TopUser",
                            "user_login": "topuser",
                            "channel_points_used": 1000,
                            "channel_points_won": 2000
                        },
                    ],
                    "color": "BLUE"
                }
            ],
            "prediction_window": 600,
            "status": "ACTIVE",
            "created_at": "2024-01-15T10:30:00Z",
            "ended_at": null,
            "locked_at": null
        });

        let prediction: Prediction = serde_json::from_value(json_data).unwrap();

        assert_eq!(prediction.id.as_str(), "test_pred_123");
        assert_eq!(prediction.broadcaster_id.as_str(), "broadcaster456");
        assert_eq!(prediction.title, "Test Prediction");
        assert_eq!(prediction.outcomes.len(), 1);
        assert_eq!(prediction.prediction_window, 600);
        assert!(matches!(prediction.status, PredictionStatus::ACTIVE));
        assert!(prediction.winning_outcome_id.is_none());
        assert!(prediction.ended_at.is_none());

        let outcome = &prediction.outcomes[0];
        assert_eq!(outcome.title, "Yes");
        assert_eq!(outcome.users, 10);
        assert_eq!(outcome.channel_points, 5000);
        assert!(outcome.top_predictors.is_some());
        assert!(matches!(outcome.color, OutocmeColor::BLUE));

        let top_predictor = outcome.top_predictors.as_ref().unwrap();
        assert_eq!(top_predictor[0].user_id.as_str(), "user123");
        assert_eq!(top_predictor[0].channel_points_used, 1000);
        assert_eq!(top_predictor[0].channel_points_won, Some(2000));
    }
}