//! Typed event description for bulk ingestion. //! //! `Event` is the new public event shape (spec Section 4). Replaces //! the nested `Vec>>`, `Vec>`, `Vec>>` //! that the old `add_events_with_prior` took. use smallvec::SmallVec; use crate::{gaussian::Gaussian, outcome::Outcome, time::Time}; /// A single match at time `time` involving some number of teams. #[derive(Clone, Debug)] pub struct Event { pub time: T, pub teams: SmallVec<[Team; 4]>, pub outcome: Outcome, } /// A team: list of members competing together. #[derive(Clone, Debug)] pub struct Team { pub members: SmallVec<[Member; 4]>, } impl Team { pub fn new() -> Self { Self { members: SmallVec::new(), } } pub fn with_members>>(members: I) -> Self { Self { members: members.into_iter().collect(), } } } impl Default for Team { fn default() -> Self { Self::new() } } /// One member of a team, identified by user key `K`. /// /// `weight` defaults to 1.0; a per-event `prior` can override the competitor's /// current skill estimate for this event only. #[derive(Clone, Debug)] pub struct Member { pub key: K, pub weight: f64, pub prior: Option, } impl Member { pub fn new(key: K) -> Self { Self { key, weight: 1.0, prior: None, } } pub fn with_weight(mut self, weight: f64) -> Self { self.weight = weight; self } pub fn with_prior(mut self, prior: Gaussian) -> Self { self.prior = Some(prior); self } } /// Convenience: a member is a user key with default weight 1.0 and no prior. impl From for Member { fn from(key: K) -> Self { Self::new(key) } } #[cfg(test)] mod tests { use super::*; use crate::Outcome; #[test] fn member_new_has_unit_weight_no_prior() { let m = Member::new("alice"); assert_eq!(m.key, "alice"); assert_eq!(m.weight, 1.0); assert!(m.prior.is_none()); } #[test] fn member_builder_methods_chain() { let m = Member::new("alice") .with_weight(0.5) .with_prior(Gaussian::from_ms(20.0, 5.0)); assert_eq!(m.weight, 0.5); assert!(m.prior.is_some()); } #[test] fn member_from_key() { let m: Member<&str> = "bob".into(); assert_eq!(m.key, "bob"); assert_eq!(m.weight, 1.0); } #[test] fn team_with_members_collects() { let t: Team<&str> = Team::with_members([Member::new("a"), Member::new("b")]); assert_eq!(t.members.len(), 2); } #[test] fn event_construction() { use smallvec::smallvec; let e: Event = Event { time: 1, teams: smallvec![ Team::with_members([Member::new("a")]), Team::with_members([Member::new("b")]), ], outcome: Outcome::winner(0, 2), }; assert_eq!(e.teams.len(), 2); assert_eq!(e.time, 1); } }