1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::{collections::BTreeMap, sync::Arc, time::SystemTime};
5
6use crate::errors::RtcResult;
7
8pub type DynProvider = dyn StatsProvider + Send + Sync + 'static;
9
10#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
11pub struct StatsId(String);
12
13impl StatsId {
14 pub fn new(value: impl Into<String>) -> Self {
15 Self(value.into())
16 }
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
20pub enum StatsKind {
21 InboundRtp,
22 OutboundRtp,
23 RemoteInboundRtp,
24 RemoteOutboundRtp,
25 Transport,
26 IceCandidatePair,
27 DataChannel,
28 MediaSource,
29 MediaSink,
30 Custom(String),
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct StatsEntry {
35 pub id: StatsId,
36 pub kind: StatsKind,
37 pub timestamp: SystemTime,
38 pub values: BTreeMap<String, Value>,
39}
40
41impl StatsEntry {
42 pub fn new(id: StatsId, kind: StatsKind) -> Self {
43 Self {
44 id,
45 kind,
46 timestamp: SystemTime::now(),
47 values: BTreeMap::new(),
48 }
49 }
50
51 pub fn with_value(mut self, key: impl Into<String>, value: Value) -> Self {
52 self.values.insert(key.into(), value);
53 self
54 }
55}
56
57impl std::fmt::Display for StatsEntry {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 write!(f, "[{:?}/{}]", self.kind, self.id.0)?;
60 for (k, v) in &self.values {
61 if let Some(s) = v.as_str() {
63 write!(f, " {}={}", k, s)?;
64 } else {
65 write!(f, " {}={}", k, v)?;
66 }
67 }
68 Ok(())
69 }
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct StatsReport {
74 pub collected_at: SystemTime,
75 pub entries: Vec<StatsEntry>,
76}
77
78impl StatsReport {
79 pub fn new(entries: Vec<StatsEntry>) -> Self {
80 Self {
81 collected_at: SystemTime::now(),
82 entries,
83 }
84 }
85
86 pub fn merge(mut self, mut other: StatsReport) -> Self {
87 self.entries.append(&mut other.entries);
88 self.collected_at = self.collected_at.max(other.collected_at);
89 self
90 }
91}
92
93impl std::fmt::Display for StatsReport {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 write!(f, "StatsReport(len={})", self.entries.len())?;
96 for entry in &self.entries {
97 write!(f, " {}", entry)?;
98 }
99 Ok(())
100 }
101}
102
103#[async_trait]
104pub trait StatsProvider: Send + Sync {
105 async fn collect(&self) -> RtcResult<Vec<StatsEntry>>;
106}
107
108pub async fn gather_once(providers: &[Arc<DynProvider>]) -> RtcResult<StatsReport> {
109 let mut entries = Vec::new();
110 for provider in providers {
111 entries.extend(provider.collect().await?);
112 }
113 Ok(StatsReport::new(entries))
114}