1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
//! Contains DTOs for [RTCPeerConnection] metrics.
//!
//! [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection

#![allow(clippy::module_name_repetitions)]

use std::{
    hash::{Hash, Hasher},
    time::{Duration, SystemTime, SystemTimeError},
};

use derive_more::{Display, From};
use serde::{Deserialize, Serialize};

/// Enum with which you can try to deserialize some known enum and if it
/// isn't known, then unknown data will be stored as [`String`] in the
/// [`NonExhaustive::Unknown`] variant.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(untagged)]
pub enum NonExhaustive<T> {
    /// Will store known enum variant if it successfully deserialized.
    Known(T),

    /// Will store unknown enum variant with it's data as [`String`].
    Unknown(String),
}

/// Unique ID that is associated with the object that was inspected to produce
/// [`RtcStat`] object.
///
/// Two [`RtcStat`]s objects, extracted from two different [RTCStatsReport]
/// objects, MUST have the same ID if they were produced by inspecting the same
/// underlying object.
///
/// [RTCStatsReport]: https://w3.org/TR/webrtc#dom-rtcstatsreport
#[derive(
    Clone, Debug, Deserialize, Display, Eq, From, Hash, PartialEq, Serialize,
)]
#[from(forward)]
pub struct StatId(pub String);

/// Represents the [stats object] constructed by inspecting a specific
/// [monitored object].
///
/// [Full doc on W3C][1].
///
/// [stats object]: https://w3.org/TR/webrtc-stats/#dfn-stats-object
/// [monitored object]: https://w3.org/TR/webrtc-stats/#dfn-monitored-object
/// [1]: https://w3.org/TR/webrtc#rtcstats-dictionary
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
pub struct RtcStat {
    /// Unique ID that is associated with the object that was inspected to
    /// produce this [RTCStats] object.
    ///
    /// [RTCStats]: https://w3.org/TR/webrtc#dom-rtcstats
    pub id: StatId,

    /// Timestamp associated with this object.
    ///
    /// The time is relative to the UNIX epoch (Jan 1, 1970, UTC).
    ///
    /// For statistics that came from a remote source (e.g., from received RTCP
    /// packets), timestamp represents the time at which the information
    /// arrived at the local endpoint. The remote timestamp can be found in an
    /// additional field in an [`RtcStat`]-derived dictionary, if applicable.
    pub timestamp: HighResTimeStamp,

    /// Actual stats of this [`RtcStat`].
    ///
    /// All possible stats are described in the [`RtcStatsType`] enum.
    #[serde(flatten)]
    pub stats: RtcStatsType,
}

/// All known types of [`RtcStat`]s.
///
/// [List of all RTCStats types on W3C][1].
///
/// [1]: https://w3.org/TR/webrtc-stats/#rtctatstype-%2A
/// [`RtcStat`]: super::RtcStat
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum RtcStatsType {
    /// Statistics for a codec that is currently used by [RTP] streams
    /// being sent or received by [RTCPeerConnection] object.
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    #[cfg(feature = "extended-stats")]
    Codec(Box<RtcCodecStats>),

    /// Statistics for an inbound [RTP] stream that is currently received
    /// with [RTCPeerConnection] object.
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    InboundRtp(Box<RtcInboundRtpStreamStats>),

    /// Statistics for an outbound [RTP] stream that is currently sent with
    /// [RTCPeerConnection] object.
    ///
    /// When there are multiple [RTP] streams connected to the same sender,
    /// such as when using simulcast or RTX, there will be one
    /// [`RtcOutboundRtpStreamStats`] per RTP stream, with distinct values
    /// of the `ssrc` attribute, and all these senders will have a
    /// reference to the same "sender" object (of type
    /// [RTCAudioSenderStats][1] or [RTCVideoSenderStats][2]) and
    /// "track" object (of type
    /// [RTCSenderAudioTrackAttachmentStats][3] or
    /// [RTCSenderVideoTrackAttachmentStats][4]).
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    /// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcaudiosenderstats
    /// [2]: https://w3.org/TR/webrtc-stats/#dom-rtcvideosenderstats
    /// [3]: https://tinyurl.com/sefa5z4
    /// [4]: https://tinyurl.com/rkuvpl4
    OutboundRtp(Box<RtcOutboundRtpStreamStats>),

    /// Statistics for the remote endpoint's inbound [RTP] stream
    /// corresponding to an outbound stream that is currently sent with
    /// [RTCPeerConnection] object.
    ///
    /// It is measured at the remote endpoint and reported in a RTCP
    /// Receiver Report (RR) or RTCP Extended Report (XR).
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    RemoteInboundRtp(Box<RtcRemoteInboundRtpStreamStats>),

    /// Statistics for the remote endpoint's outbound [RTP] stream
    /// corresponding to an inbound stream that is currently received with
    /// [RTCPeerConnection] object.
    ///
    /// It is measured at the remote endpoint and reported in an RTCP
    /// Sender Report (SR).
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    RemoteOutboundRtp(Box<RtcRemoteOutboundRtpStreamStats>),

    /// Statistics for the media produced by a [MediaStreamTrack][1] that
    /// is currently attached to an [RTCRtpSender]. This reflects
    /// the media that is fed to the encoder after [getUserMedia]
    /// constraints have been applied (i.e. not the raw media
    /// produced by the camera).
    ///
    /// [RTCRtpSender]: https://w3.org/TR/webrtc#rtcrtpsender-interface
    /// [getUserMedia]: https://tinyurl.com/sngpyr6
    /// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
    MediaSource(Box<MediaSourceStats>),

    /// Statistics for a contributing source (CSRC) that contributed to an
    /// inbound [RTP] stream.
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    #[cfg(feature = "extended-stats")]
    Csrc(Box<RtpContributingSourceStats>),

    /// Statistics related to the [RTCPeerConnection] object.
    ///
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    #[cfg(feature = "extended-stats")]
    PeerConnection(Box<RtcPeerConnectionStats>),

    /// Statistics related to each [RTCDataChannel] ID.
    ///
    /// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
    #[cfg(feature = "extended-stats")]
    DataChannel(Box<DataChannelStats>),

    /// Contains statistics related to a specific [MediaStream].
    ///
    /// This is now obsolete.
    ///
    /// [MediaStream]: https://w3.org/TR/mediacapture-streams#mediastream
    #[cfg(feature = "extended-stats")]
    Stream(Box<MediaStreamStats>),

    /// Statistics related to a specific [MediaStreamTrack][1]'s attachment
    /// to an [RTCRtpSender] and the corresponding media-level
    /// metrics.
    ///
    /// [RTCRtpSender]: https://w3.org/TR/webrtc#rtcrtpsender-interface
    /// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
    Track(Box<TrackStats>),

    /// Statistics related to a specific [RTCRtpTransceiver].
    ///
    /// [RTCRtpTransceiver]: https://w3.org/TR/webrtc#dom-rtcrtptransceiver
    #[cfg(feature = "extended-stats")]
    Transceiver(Box<RtcRtpTransceiverStats>),

    /// Statistics related to a specific [RTCRtpSender] and the
    /// corresponding media-level metrics.
    ///
    /// [RTCRtpSender]: https://w3.org/TR/webrtc#rtcrtpsender-interface
    #[cfg(feature = "extended-stats")]
    Sender(Box<SenderStatsKind>),

    /// Statistics related to a specific [RTCRtpReceiver] and the
    /// corresponding media-level metrics.
    ///
    /// [RTCRtpReceiver]: https://w3.org/TR/webrtc#dom-rtcrtpreceiver
    #[cfg(feature = "extended-stats")]
    Receiver(Box<ReceiverStatsKind>),

    /// Transport statistics related to the [RTCPeerConnection] object.
    ///
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    Transport(Box<RtcTransportStats>),

    /// SCTP transport statistics related to an [RTCSctpTransport] object.
    ///
    /// [RTCSctpTransport]: https://w3.org/TR/webrtc#dom-rtcsctptransport
    SctpTransport(Box<RtcSctpTransportStats>),

    /// ICE candidate pair statistics related to the [RTCIceTransport]
    /// objects.
    ///
    /// A candidate pair that is not the current pair for a transport is
    /// [deleted][1] when the [RTCIceTransport] does an ICE restart, at the
    /// time the state changes to `new`.
    ///
    /// The candidate pair that is the current pair for a transport is
    /// deleted after an ICE restart when the [RTCIceTransport]
    /// switches to using a candidate pair generated from the new
    /// candidates; this time doesn't correspond to any other
    /// externally observable event.
    ///
    /// [RTCIceTransport]: https://w3.org/TR/webrtc#dom-rtcicetransport
    /// [1]: https://w3.org/TR/webrtc-stats/#dfn-deleted
    CandidatePair(Box<RtcIceCandidatePairStats>),

    /// ICE local candidate statistics related to the [RTCIceTransport]
    /// objects.
    ///
    /// A local candidate is [deleted][1] when the [RTCIceTransport] does
    /// an ICE restart, and the candidate is no longer a member of
    /// any non-deleted candidate pair.
    ///
    /// [RTCIceTransport]: https://w3.org/TR/webrtc#dom-rtcicetransport
    /// [1]: https://w3.org/TR/webrtc-stats/#dfn-deleted
    LocalCandidate(Box<RtcIceCandidateStats>),

    /// ICE remote candidate statistics related to the [RTCIceTransport]
    /// objects.
    ///
    /// A remote candidate is [deleted][1] when the [RTCIceTransport] does
    /// an ICE restart, and the candidate is no longer a member of
    /// any non-deleted candidate pair.
    ///
    /// [RTCIceTransport]: https://w3.org/TR/webrtc#dom-rtcicetransport
    /// [1]: https://w3.org/TR/webrtc-stats/#dfn-deleted
    RemoteCandidate(Box<RtcIceCandidateStats>),

    /// Information about a certificate used by [RTCIceTransport].
    ///
    /// [RTCIceTransport]: https://w3.org/TR/webrtc#dom-rtcicetransport
    #[cfg(feature = "extended-stats")]
    Certificate(Box<RtcCertificateStats>),

    /// Information about the connection to an ICE server (e.g. STUN or
    /// TURN).
    #[cfg(feature = "extended-stats")]
    IceServer(Box<RtcIceServerStats>),

    /// Disabled or unknown variants of stats will be deserialized as
    /// [`RtcStatsType::Other`].
    #[serde(other)]
    Other,
}

#[cfg(feature = "extended-stats")]
/// Contains statistics related to a specific [MediaStream].
///
/// This is now obsolete.
///
/// [`RtcStatsType::Stream`] variant.
///
/// [Full doc on W3C][1].
///
/// [MediaStream]: https://w3.org/TR/mediacapture-streams#mediastream
/// [1]: https://w3.org/TR/webrtc-stats/#idl-def-rtcmediastreamstats
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MediaStreamStats {
    /// [`stream.id`][1] property.
    ///
    /// [1]: https://w3.org/TR/mediacapture-streams#dom-mediastream-id
    pub stream_identifier: String,

    /// ID of the stats object, not the `track.id`.
    pub track_ids: Vec<StatId>,
}

#[cfg(feature = "extended-stats")]
/// Statistics related to each [RTCDataChannel] ID.
///
/// [`RtcStatsType::DataChannel`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
/// [1]: https://w3.org/TR/webrtc-stats/#dcstats-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DataChannelStats {
    /// [`label`][1] value of the [RTCDataChannel] object.
    ///
    /// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
    /// [1]: https://w3.org/TR/webrtc#dom-datachannel-label
    pub label: Option<String>,

    /// [`protocol`][1] value of the [RTCDataChannel] object.
    ///
    /// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
    /// [1]: https://w3.org/TR/webrtc#dom-datachannel-protocol
    pub protocol: Option<Protocol>,

    /// [`id`][1] attribute of the [RTCDataChannel] object.
    ///
    /// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
    /// [1]: https://w3.org/TR/webrtc#dom-rtcdatachannel-id
    pub data_channel_identifier: Option<u64>,

    /// [Stats object reference][1] for the transport used to carry
    /// [RTCDataChannel].
    ///
    /// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
    /// [1]: https://w3.org/TR/webrtc-stats/#dfn-stats-object-reference
    pub transport_id: Option<String>,

    /// [`readyState`][1] value of the [RTCDataChannel] object.
    ///
    /// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
    /// [1]: https://w3.org/TR/webrtc#dom-datachannel-readystate
    pub state: Option<DataChannelState>,

    /// Total number of API `message` events sent.
    pub messages_sent: Option<u64>,

    /// Total number of payload bytes sent on this [RTCDataChannel], i.e. not
    /// including headers or padding.
    ///
    /// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
    pub bytes_sent: Option<u64>,

    /// Total number of API `message` events received.
    pub messages_received: Option<u64>,

    /// Total number of bytes received on this [RTCDataChannel], i.e. not
    /// including headers or padding.
    ///
    /// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
    pub bytes_received: Option<u64>,
}

/// Non-exhaustive version of [`KnownDataChannelState`].
pub type DataChannelState = NonExhaustive<KnownDataChannelState>;

/// State of the [RTCDataChannel]'s underlying data connection.
///
/// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum KnownDataChannelState {
    /// User agent is attempting to establish the underlying data transport.
    /// This is the initial state of [RTCDataChannel] object, whether created
    /// with [createDataChannel][1], or dispatched as a part of an
    /// [RTCDataChannelEvent].
    ///
    /// [RTCDataChannel]: https://w3.org/TR/webrtc#dom-rtcdatachannel
    /// [RTCDataChannelEvent]: https://w3.org/TR/webrtc#dom-rtcdatachannelevent
    /// [1]: https://w3.org/TR/webrtc#dom-peerconnection-createdatachannel
    Connecting,

    /// [Underlying data transport][1] is established and communication is
    /// possible.
    ///
    /// [1]: https://w3.org/TR/webrtc#dfn-data-transport
    Open,

    /// [`procedure`][2] to close down the [underlying data transport][1] has
    /// started.
    ///
    /// [1]: https://w3.org/TR/webrtc#dfn-data-transport
    /// [2]: https://w3.org/TR/webrtc#data-transport-closing-procedure
    Closing,

    /// [Underlying data transport][1] has been [`closed`][2] or could not be
    /// established.
    ///
    /// [1]: https://w3.org/TR/webrtc#dfn-data-transport
    /// [2]: https://w3.org/TR/webrtc#dom-rtcdatachannelstate-closed
    Closed,
}

#[cfg(feature = "extended-stats")]
/// Stats for the [RTCPeerConnection] object.
///
/// [`RtcStatsType::PeerConnection`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
/// [1]: https://w3.org/TR/webrtc-stats/#pcstats-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcPeerConnectionStats {
    /// Number of unique `DataChannel`s that have entered the `open` state
    /// during their lifetime.
    pub data_channels_opened: Option<u64>,

    /// Number of unique `DataChannel`s that have left the `open` state during
    /// their lifetime (due to being closed by either end or the underlying
    /// transport being closed). `DataChannel`s that transition from
    /// `connecting` to `closing` or `closed` without ever being `open` are not
    /// counted in this number.
    pub data_channels_closed: Option<u64>,

    /// Number of unique `DataChannel`s returned from a successful
    /// [createDataChannel][1] call on the [RTCPeerConnection].
    /// If the underlying data transport is not established, these may be in
    /// the `connecting` state.
    ///
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    /// [1]: https://w3.org/TR/webrtc#dom-peerconnection-createdatachannel
    pub data_channels_requested: Option<u64>,

    /// Number of unique `DataChannel`s signaled in a `datachannel` event on
    /// the [RTCPeerConnection].
    ///
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    pub data_channels_accepted: Option<u64>,
}

#[cfg(feature = "extended-stats")]
/// Statistics for a contributing source (CSRC) that contributed to an inbound
/// [RTP] stream.
///
/// [`RtcStatsType::Csrc`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
/// [1]: https://w3.org/TR/webrtc-stats/#contributingsourcestats-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtpContributingSourceStats {
    /// SSRC identifier of the contributing source represented by the stats
    /// object, as defined by [RFC 3550]. It is a 32-bit unsigned integer that
    /// appears in the CSRC list of any packets the relevant source contributed
    /// to.
    ///
    /// [RFC 3550]: https://tools.ietf.org/html/rfc3550
    pub contributor_ssrc: Option<u32>,

    /// ID of the [RTCInboundRtpStreamStats][1] object representing the inbound
    /// [RTP] stream that this contributing source is contributing to.
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    /// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcinboundrtpstreamstats
    pub inbound_rtp_stream_id: Option<String>,

    /// Total number of [RTP] packets that this contributing source contributed
    /// to.
    ///
    /// This value is incremented each time a packet is counted by
    /// [RTCInboundRtpStreamStats.packetsReceived][2], and the packet's CSRC
    /// list (as defined by [Section 5.1 in RFC 3550][3]) contains the SSRC
    /// identifier of this contributing source, [`contributorSsrc`].
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    /// [`contributorSsrc`]: https://tinyurl.com/tf8c7j4
    /// [2]: https://tinyurl.com/rreuf49
    /// [3]: https://tools.ietf.org/html/rfc3550#section-5.1
    pub packets_contributed_to: Option<u64>,

    /// Present if the last received RTP packet that this source contributed to
    /// contained an [RFC 6465] mixer-to-client audio level header extension.
    ///
    /// The value of [`audioLevel`] is between `0..1` (linear), where `1.0`
    /// represents `0 dBov`, `0` represents silence, and `0.5` represents
    /// approximately `6 dBSPL` change in the sound pressure level from 0
    /// dBov. The [RFC 6465] header extension contains values in the range
    /// `0..127`, in units of `-dBov`, where `127` represents silence. To
    /// convert these values to the linear `0..1` range of `audioLevel`, a
    /// value of `127` is converted to `0`, and all other values are
    /// converted using the equation:
    ///
    /// `f(rfc6465_level) = 10^(-rfc6465_level/20)`
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    /// [RFC 6465]: https://tools.ietf.org/html/rfc6465
    /// [`audioLevel`]: https://tinyurl.com/sfy699q
    pub audio_level: Option<Float>,
}

/// Statistics for the remote endpoint's outbound [RTP] stream corresponding
/// to an inbound stream that is currently received with [RTCPeerConnection]
/// object.
///
/// It is measured at the remote endpoint and reported in an RTCP Sender Report
/// (SR).
///
/// [`RtcStatsType::RemoteOutboundRtp`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
/// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
/// [1]: https://w3.org/TR/webrtc-stats/#remoteoutboundrtpstats-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcRemoteOutboundRtpStreamStats {
    /// [`localId`] is used for looking up the local
    /// [RTCInboundRtpStreamStats][1] object for the same SSRC.
    ///
    /// [`localId`]: https://tinyurl.com/vu9tb2e
    /// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcinboundrtpstreamstats
    pub local_id: Option<String>,

    /// [`remoteTimestamp`] (as [HIGHRES-TIME]) is the remote timestamp at
    /// which these statistics were sent by the remote endpoint. This
    /// differs from timestamp, which represents the time at which the
    /// statistics were generated or received by the local endpoint. The
    /// [`remoteTimestamp`], if present, is derived from the NTP timestamp
    /// in an RTCP Sender Report (SR) block, which reflects the remote
    /// endpoint's clock. That clock may not be synchronized with the local
    /// clock.
    ///
    /// [`remoteTimestamp`]: https://tinyurl.com/rzlhs87
    /// [HIGRES-TIME]: https://w3.org/TR/webrtc-stats/#bib-highres-time
    pub remote_timestamp: Option<HighResTimeStamp>,

    /// Total number of RTCP SR blocks sent for this SSRC.
    pub reports_sent: Option<u64>,
}

/// Statistics for the remote endpoint's inbound [RTP] stream corresponding
/// to an outbound stream that is currently sent with [RTCPeerConnection]
/// object.
///
/// It is measured at the remote endpoint and reported in a RTCP Receiver
/// Report (RR) or RTCP Extended Report (XR).
///
/// [`RtcStatsType::RemoteInboundRtp`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
/// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
/// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcinboundrtpstreamstats
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcRemoteInboundRtpStreamStats {
    /// [`localId`] is used for looking up the local
    /// [RTCOutboundRtpStreamStats] object for the same SSRC.
    ///
    /// [`localId`]: https://tinyurl.com/r8uhbo9
    /// [RTCOutBoundRtpStreamStats]: https://tinyurl.com/r6f5vqg
    pub local_id: Option<String>,

    /// Packet [jitter] measured in seconds for this SSRC.
    ///
    /// [jitter]: https://en.wikipedia.org/wiki/Jitter
    pub jitter: Option<Float>,

    /// Estimated round trip time for this SSRC based on the RTCP timestamps in
    /// the RTCP Receiver Report (RR) and measured in seconds. Calculated as
    /// defined in [Section 6.4.1 of RFC 3550][1]. If no RTCP Receiver Report
    /// is received with a DLSR value other than 0, the round trip time is
    /// left undefined.
    ///
    /// [1]: https://tools.ietf.org/html/rfc3550#section-6.4.1
    pub round_trip_time: Option<Float>,

    /// Fraction packet loss reported for this SSRC. Calculated as defined in
    /// [Section 6.4.1 of RFC 3550][1] and [Appendix A.3][2].
    ///
    /// [1]: https://tools.ietf.org/html/rfc3550#section-6.4.1
    /// [2]: https://tools.ietf.org/html/rfc3550#appendix-A.3
    pub fraction_lost: Option<Float>,

    /// Total number of RTCP RR blocks received for this SSRC.
    pub reports_received: Option<u64>,

    /// Total number of RTCP RR blocks received for this SSRC that contain a
    /// valid round trip time. This counter will increment if the
    /// [`roundTripTime`] is undefined.
    ///
    /// [`roundTripTime`]: https://tinyurl.com/ssg83hq
    pub round_trip_time_measurements: Option<Float>,
}

#[cfg(feature = "extended-stats")]
/// [RTCRtpTransceiverStats][1] object representing an [RTCRtpTransceiver] of an
/// [RTCPeerConnection].
///
/// It appears as soon as the monitored [RTCRtpTransceiver] object is created,
/// such as by invoking [addTransceiver][2], [addTrack][3] or
/// [setRemoteDescription][4]. [RTCRtpTransceiverStats][1] objects can only be
/// deleted if the corresponding [RTCRtpTransceiver] is removed (this can only
/// happen if a remote description is rolled back).
///
/// [`RtcStatsType::Transceiver`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
/// [RTCRtpTransceiver]: https://w3.org/TR/webrtc#dom-rtcrtptransceiver
/// [1]: https://w3.org/TR/webrtc-stats/#transceiver-dict%2A
/// [2]: https://w3.org/TR/webrtc#dom-rtcpeerconnection-addtransceiver
/// [3]: https://w3.org/TR/webrtc#dom-rtcpeerconnection-addtrack
/// [4]: https://tinyurl.com/vejym8v
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcRtpTransceiverStats {
    /// ID of the stats object representing the
    /// [RTCRtpSender associated with the RTCRtpTransceiver][1] represented by
    /// this stats object.
    ///
    /// [1]: https://w3.org/TR/webrtc#dom-rtcrtptransceiver-sender
    pub sender_id: Option<String>,

    /// ID of the stats object representing the
    /// [RTCRtpReceiver associated with the RTCRtpTransceiver][1] represented
    /// by this stats object.
    ///
    /// [1]: https://w3.org/TR/webrtc#dom-rtcrtptransceiver-receiver
    pub receiver_id: Option<String>,

    /// If the [RTCRtpTransceiver] that this stats object represents has a
    /// [`mid` value][1] that is not null, this is that value, otherwise this
    /// value is undefined.
    ///
    /// [RTCRtpTransceiver]: https://w3.org/TR/webrtc#dom-rtcrtptransceiver
    /// [1]: https://w3.org/TR/webrtc#dom-rtptransceiver-mid
    pub mid: Option<String>,
}

/// Representation of the stats corresponding to an [RTCSctpTransport].
///
/// [`RtcStatsType::SctpTransport`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTCSctpTransport]: https://w3.org/TR/webrtc#dom-rtcsctptransport
/// [1]: https://w3.org/TR/webrtc-stats/#sctptransportstats-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcSctpTransportStats {
    /// Latest smoothed round-trip time value, corresponding to
    /// [`spinfo_srtt` defined in RFC 6458][1] but converted to seconds.
    ///
    /// If there has been no round-trip time measurements yet, this value is
    /// undefined.
    ///
    /// [1]: https://tools.ietf.org/html/rfc6458#page-83
    pub smoothed_round_trip_time: Option<HighResTimeStamp>,
}

/// Representation of the stats corresponding to an [RTCDtlsTransport] and its
/// underlying [RTCIceTransport].
///
/// When RTCP multiplexing is used, one transport is used for both RTP and RTCP.
/// Otherwise, RTP and RTCP will be sent on separate transports, and
/// `rtcpTransportStatsId` can be used to pair the resulting
/// [`RtcTransportStats`] objects. Additionally, when bundling is used, a single
/// transport will be used for all [MediaStreamTrack][2]s in the bundle group.
/// If bundling is not used, different [MediaStreamTrack][2]s will use different
/// transports. RTCP multiplexing and bundling are described in [WebRTC].
///
/// [`RtcStatsType::Transport`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTCDtlsTransport]: https://w3.org/TR/webrtc#dom-rtcdtlstransport
/// [RTCIceTransport]: https://w3.org/TR/webrtc#dom-rtcicetransport
/// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
/// [WebRTC]: https://w3.org/TR/webrtc
/// [1]: https://w3.org/TR/webrtc-stats/#transportstats-dict%2A
/// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
#[serde_with::skip_serializing_none]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcTransportStats {
    /// Total number of packets sent over this transport.
    pub packets_sent: Option<u64>,

    /// Total number of packets received on this transport.
    pub packets_received: Option<u64>,

    /// Total number of payload bytes sent on this [RTCPeerConnection], i.e.
    /// not including headers or padding.
    ///
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    pub bytes_sent: Option<u64>,

    /// Total number of bytes received on this [RTCPeerConnection], i.e. not
    /// including headers or padding.
    ///
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    pub bytes_received: Option<u64>,

    /// Set to the current value of the [`role` attribute][1] of the
    /// [underlying RTCDtlsTransport's `transport`][2].
    ///
    /// [1]: https://w3.org/TR/webrtc#dom-icetransport-role
    /// [2]: https://w3.org/TR/webrtc#dom-rtcdtlstransport-icetransport
    pub ice_role: Option<IceRole>,
}

/// Variants of [ICE roles][1].
///
/// More info in the [RFC 5245].
///
/// [RFC 5245]: https://tools.ietf.org/html/rfc5245
/// [1]: https://w3.org/TR/webrtc#dom-icetransport-role
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum IceRole {
    /// Agent whose role as defined by [Section 3 in RFC 5245][1], has not yet
    /// been determined.
    ///
    /// [1]: https://tools.ietf.org/html/rfc5245#section-3
    Unknown,

    /// Controlling agent as defined by [Section 3 in RFC 5245][1].
    ///
    /// [1]: https://tools.ietf.org/html/rfc5245#section-3
    Controlling,

    /// Controlled agent as defined by [Section 3 in RFC 5245][1].
    ///
    /// [1]: https://tools.ietf.org/html/rfc5245#section-3
    Controlled,
}

#[cfg(feature = "extended-stats")]
/// Statistics related to a specific [RTCRtpSender] and the corresponding
/// media-level metrics.
///
/// [`RtcStatsType::Sender`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTCRtpSender]: https://w3.org/TR/webrtc#rtcrtpsender-interface
/// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcstatstype-sender
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum SenderStatsKind {
    /// [RTCAudioSenderStats][1] object.
    ///
    /// [1]: https://tinyurl.com/w5ow5xs
    Audio {
        /// ID of the related media source.
        media_source_id: Option<String>,
    },

    /// [RTCVideoSenderStats][1] object.
    ///
    /// [1]: https://tinyurl.com/ry39vnw
    Video {
        /// ID of the related media source.
        media_source_id: Option<String>,
    },
}

#[cfg(feature = "extended-stats")]
/// Statistics related to a specific [RTCRtpReceiver] and the corresponding
/// media-level metrics.
///
/// [`RtcStatsType::Receiver`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTCRtpReceiver]: https://w3.org/TR/webrtc#dom-rtcrtpreceiver
/// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcstatstype-receiver
#[serde_with::skip_serializing_none]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum ReceiverStatsKind {
    /// [RTCAudioReceiverStats] object.
    ///
    /// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcaudioreceiverstats
    Audio {},

    /// [RTCVideoReceiverStats] object.
    ///
    /// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcvideoreceiverstats
    Video {},
}

/// ICE candidate pair statistics related to the [RTCIceTransport] objects.
///
/// A candidate pair that is not the current pair for a transport is
/// [deleted][1] when the [RTCIceTransport] does an ICE restart, at the time
/// the state changes to `new`.
///
/// The candidate pair that is the current pair for a transport is deleted after
/// an ICE restart when the [RTCIceTransport] switches to using a candidate pair
/// generated from the new candidates; this time doesn't correspond to any other
/// externally observable event.
///
/// [`RtcStatsType::CandidatePair`] variant.
///
/// [Full doc on W3C][2].
///
/// [RTCIceTransport]: https://w3.org/TR/webrtc#dom-rtcicetransport
/// [1]: https://w3.org/TR/webrtc-stats/#dfn-deleted
/// [2]: https://w3.org/TR/webrtc-stats/#candidatepair-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcIceCandidatePairStats {
    /// State of the checklist for the local and remote candidates in a pair.
    pub state: IceCandidatePairState,

    /// Related to updating the nominated flag described in
    /// [Section 7.1.3.2.4 of RFC 5245][1].
    ///
    /// [1]: https://tools.ietf.org/html/rfc5245#section-7.1.3.2.4
    pub nominated: bool,

    /// Total number of payload bytes sent on this candidate pair, i.e. not
    /// including headers or padding.
    pub bytes_sent: u64,

    /// Total number of payload bytes received on this candidate pair, i.e. not
    /// including headers or padding.
    pub bytes_received: u64,

    /// Sum of all round trip time measurements in seconds since the beginning
    /// of the session, based on STUN connectivity check [STUN-PATH-CHAR]
    /// responses (responsesReceived), including those that reply to requests
    /// that are sent in order to verify consent [RFC 7675].
    ///
    /// The average round trip time can be computed from
    /// [`totalRoundTripTime`][1] by dividing it by [`responsesReceived`][2].
    ///
    /// [STUN-PATH-CHAR]: https://w3.org/TR/webrtc-stats/#bib-stun-path-char
    /// [RFC 7675]: https://tools.ietf.org/html/rfc7675
    /// [1]: https://tinyurl.com/tgr543a
    /// [2]: https://tinyurl.com/r3zo2um
    pub total_round_trip_time: Option<HighResTimeStamp>,

    /// Latest round trip time measured in seconds, computed from both STUN
    /// connectivity checks [STUN-PATH-CHAR], including those that are sent for
    /// consent verification [RFC 7675].
    ///
    /// [STUN-PATH-CHAR]: https://w3.org/TR/webrtc-stats/#bib-stun-path-char
    /// [RFC 7675]: https://tools.ietf.org/html/rfc7675
    pub current_round_trip_time: Option<HighResTimeStamp>,

    /// Calculated by the underlying congestion control by combining the
    /// available bitrate for all the outgoing RTP streams using this candidate
    /// pair. The bitrate measurement does not count the size of the IP or
    /// other transport layers like TCP or UDP. It is similar to the TIAS
    /// defined in [RFC 3890], i.e. it is measured in bits per second and the
    /// bitrate is calculated over a 1 second window.
    ///
    /// Implementations that do not calculate a sender-side estimate MUST leave
    /// this undefined. Additionally, the value MUST be undefined for candidate
    /// pairs that were never used. For pairs in use, the estimate is normally
    /// no lower than the bitrate for the packets sent at
    /// [`lastPacketSentTimestamp`][1], but might be higher. For candidate
    /// pairs that are not currently in use but were used before,
    /// implementations MUST return undefined.
    ///
    /// [RFC 3890]: https://tools.ietf.org/html/rfc3890
    /// [1]: https://tinyurl.com/rfc72eh
    pub available_outgoing_bitrate: Option<u64>,
}

/// Each candidate pair in the check list has a foundation and a state.
/// The foundation is the combination of the foundations of the local and
/// remote candidates in the pair.  The state is assigned once the check
/// list for each media stream has been computed.  There are five
/// potential values that the state can have.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum KnownIceCandidatePairState {
    /// Check has not been performed for this pair, and can be performed as
    /// soon as it is the highest-priority Waiting pair on the check list.
    Waiting,

    /// Check has been sent for this pair, but the transaction is in progress.
    InProgress,

    /// Check for this pair was already done and produced a successful result.
    Succeeded,

    /// Check for this pair was already done and failed, either never producing
    /// any response or producing an unrecoverable failure response.
    Failed,

    /// Check for this pair hasn't been performed, and it can't yet be
    /// performed until some other check succeeds, allowing this pair to
    /// unfreeze and move into the [`KnownIceCandidatePairState::Waiting`]
    /// state.
    Frozen,

    /// Other Candidate pair was nominated.
    ///
    /// This state is **obsolete and not spec compliant**, however, it still
    /// may be emitted by some implementations.
    Cancelled,
}

/// Non-exhaustive version of [`KnownIceCandidatePairState`].
pub type IceCandidatePairState = NonExhaustive<KnownIceCandidatePairState>;

/// Known protocols used in the WebRTC.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum KnownProtocol {
    /// [User Datagram Protocol][1].
    ///
    /// [1]: https://en.wikipedia.org/wiki/User_Datagram_Protocol
    Udp,

    /// [Transmission Control Protocol][1].
    ///
    /// [1]: https://en.wikipedia.org/wiki/Transmission_Control_Protocol
    Tcp,
}

/// Non-exhaustive version of [`KnownProtocol`].
pub type Protocol = NonExhaustive<KnownProtocol>;

/// [RTCIceCandidateType] represents the type of the ICE candidate, as
/// defined in [Section 15.1 of RFC 5245][1].
///
/// [RTCIceCandidateType]: https://w3.org/TR/webrtc#rtcicecandidatetype-enum
/// [1]: https://tools.ietf.org/html/rfc5245#section-15.1
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum KnownCandidateType {
    /// Host candidate, as defined in [Section 4.1.1.1 of RFC 5245][1].
    ///
    /// [1]: https://tools.ietf.org/html/rfc5245#section-4.1.1.1
    Host,

    /// Server reflexive candidate, as defined in
    /// [Section 4.1.1.2 of RFC 5245][1].
    ///
    /// [1]: https://tools.ietf.org/html/rfc5245#section-4.1.1.2
    Srlfx,

    /// Peer reflexive candidate, as defined in
    /// [Section 4.1.1.2 of RFC 5245][1].
    ///
    /// [1]: https://tools.ietf.org/html/rfc5245#section-4.1.1.2
    Prflx,

    /// Relay candidate, as defined in [Section 7.1.3.2.1 of RFC 5245][1].
    ///
    /// [1]: https://tools.ietf.org/html/rfc5245#section-7.1.3.2.1
    Relay,
}

/// Non-exhaustive version of [`KnownCandidateType`].
pub type CandidateType = NonExhaustive<KnownCandidateType>;

/// Fields of [`RtcStatsType::InboundRtp`] variant.
#[serde_with::skip_serializing_none]
#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(tag = "mediaType", rename_all = "camelCase")]
pub enum RtcInboundRtpStreamMediaType {
    /// Fields when `mediaType` is `audio`.
    Audio {
        /// Indicator whether the last RTP packet whose frame was delivered to
        /// the [RTCRtpReceiver]'s [MediaStreamTrack][1] for playout contained
        /// voice activity or not based on the presence of the V bit in the
        /// extension header, as defined in [RFC 6464].
        ///
        /// [RTCRtpReceiver]: https://w3.org/TR/webrtc#rtcrtpreceiver-interface
        /// [RFC 6464]: https://tools.ietf.org/html/rfc6464#page-3
        /// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
        voice_activity_flag: Option<bool>,

        /// Total number of samples that have been received on this RTP stream.
        /// This includes [`concealedSamples`].
        ///
        /// [`concealedSamples`]: https://tinyurl.com/s6c4qe4
        total_samples_received: Option<u64>,

        /// Total number of samples that are concealed samples.
        ///
        /// A concealed sample is a sample that was replaced with synthesized
        /// samples generated locally before being played out.
        /// Examples of samples that have to be concealed are samples from lost
        /// packets (reported in [`packetsLost`]) or samples from packets that
        /// arrive too late to be played out (reported in
        /// [`packetsDiscarded`]).
        ///
        /// [`packetsLost`]: https://tinyurl.com/u2gq965
        /// [`packetsDiscarded`]: https://tinyurl.com/yx7qyox3
        concealed_samples: Option<u64>,

        /// Total number of concealed samples inserted that are "silent".
        ///
        /// Playing out silent samples results in silence or comfort noise.
        /// This is a subset of [`concealedSamples`].
        ///
        /// [`concealedSamples`]: https://tinyurl.com/s6c4qe4
        silent_concealed_samples: Option<u64>,

        /// Audio level of the receiving track.
        audio_level: Option<Float>,

        /// Audio energy of the receiving track.
        total_audio_energy: Option<Float>,

        /// Audio duration of the receiving track.
        ///
        /// For audio durations of tracks attached locally, see
        /// [RTCAudioSourceStats][1] instead.
        ///
        /// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcaudiosourcestats
        total_samples_duration: Option<HighResTimeStamp>,
    },

    /// Fields when `mediaType` is `video`.
    Video {
        /// Total number of frames correctly decoded for this RTP stream, i.e.
        /// frames that would be displayed if no frames are dropped.
        frames_decoded: Option<u64>,

        /// Total number of key frames, such as key frames in VP8 [RFC 6386] or
        /// IDR-frames in H.264 [RFC 6184], successfully decoded for this RTP
        /// media stream.
        ///
        /// This is a subset of [`framesDecoded`].
        /// [`framesDecoded`] - [`keyFramesDecoded`] gives you the number of
        /// delta frames decoded.
        ///
        /// [RFC 6386]: https://w3.org/TR/webrtc-stats/#bib-rfc6386
        /// [RFC 6184]: https://w3.org/TR/webrtc-stats/#bib-rfc6184
        /// [`framesDecoded`]: https://tinyurl.com/srfwrwt
        /// [`keyFramesDecoded`]: https://tinyurl.com/qtdmhtm
        key_frames_decoded: Option<u64>,

        /// Width of the last decoded frame.
        ///
        /// Before the first frame is decoded this attribute is missing.
        frame_width: Option<u64>,

        /// Height of the last decoded frame.
        ///
        /// Before the first frame is decoded this attribute is missing.
        frame_height: Option<u64>,

        /// Sum of the interframe delays in seconds between consecutively
        /// decoded frames, recorded just after a frame has been decoded.
        total_inter_frame_delay: Option<Float>,

        /// Number of decoded frames in the last second.
        frames_per_second: Option<u64>,

        /// Bit depth per pixel of the last decoded frame.
        ///
        /// Typical values are 24, 30, or 36 bits. Before the first frame is
        /// decoded this attribute is missing.
        frame_bit_depth: Option<u64>,

        /// Total number of Full Intra Request (FIR) packets sent by this
        /// receiver.
        fir_count: Option<u64>,

        /// Total number of Picture Loss Indication (PLI) packets sent by this
        /// receiver.
        pli_count: Option<u64>,

        /// Total number of Slice Loss Indication (SLI) packets sent by this
        /// receiver.
        sli_count: Option<u64>,

        /// Number of concealment events.
        ///
        /// This counter increases every time a concealed sample is synthesized
        /// after a non-concealed sample. That is, multiple consecutive
        /// concealed samples will increase the [`concealedSamples`] count
        /// multiple times but is a single concealment event.
        ///
        /// [`concealedSamples`]: https://tinyurl.com/s6c4qe4
        concealment_events: Option<u64>,

        /// Total number of complete frames received on this RTP stream.
        ///
        /// This metric is incremented when the complete frame is received.
        frames_received: Option<u64>,
    },
}

/// Representation of the measurement metrics for the incoming [RTP] media
/// stream. The timestamp reported in the statistics object is the time at which
/// the data was sampled.
///
/// [`RtcStatsType::InboundRtp`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
/// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcinboundrtpstreamstats
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcInboundRtpStreamStats {
    /// ID of the stats object representing the receiving track.
    pub track_id: Option<String>,

    /// Fields which should be in the [`RtcStat`] based on `mediaType`.
    #[serde(flatten)]
    pub media_specific_stats: RtcInboundRtpStreamMediaType,

    /// Total number of bytes received for this SSRC.
    pub bytes_received: u64,

    /// Total number of RTP data packets received for this SSRC.
    pub packets_received: u64,

    /// Total number of RTP data packets for this SSRC that have been lost
    /// since the beginning of reception.
    ///
    /// This number is defined to be the number of packets expected less the
    /// number of packets actually received, where the number of packets
    /// received includes any which are late or duplicates. Thus, packets that
    /// arrive late are not counted as lost, and the loss __may be negative__
    /// if there are duplicates.
    pub packets_lost: Option<i64>,

    /// Packet jitter measured in seconds for this SSRC.
    pub jitter: Option<Float>,

    /// Total number of seconds that have been spent decoding the
    /// [`framesDecoded`] frames of this stream.
    ///
    /// The average decode time can be calculated by dividing this value with
    /// [`framesDecoded`]. The time it takes to decode one frame is the time
    /// passed between feeding the decoder a frame and the decoder returning
    /// decoded data for that frame.
    ///
    /// [`framesDecoded`]: https://tinyurl.com/srfwrwt
    pub total_decode_time: Option<HighResTimeStamp>,

    /// Total number of audio samples or video frames that have come out of the
    /// jitter buffer (increasing [`jitterBufferDelay`]).
    ///
    /// [`jitterBufferDelay`]: https://tinyurl.com/qvoojt5
    pub jitter_buffer_emitted_count: Option<u64>,
}

/// Statistics related to a specific [MediaStreamTrack][1]'s attachment to an
/// [RTCRtpSender] and the corresponding media-level metrics.
///
/// [`RtcStatsType::Track`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTCRtpSender]: https://w3.org/TR/webrtc#rtcrtpsender-interface
/// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
/// [2]: https://w3.org/TR/webrtc-stats/#dom-rtcstatstype-track
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TrackStats {
    /// [`id` property][1] of the track.
    ///
    /// [1]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrack-id
    pub track_identifier: String,

    /// `true` if the source is remote, for instance if it is sourced from
    /// another host via an [RTCPeerConnection]. `false` otherwise.
    ///
    /// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
    pub remote_source: Option<bool>,

    /// Reflection of the "ended" state of the track.
    pub ended: Option<bool>,

    /// Either `audio` or `video`.
    ///
    /// This reflects the [`kind` attribute][2] of the [MediaStreamTrack][1].
    ///
    /// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
    /// [2]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrack-kind
    pub kind: Option<TrackStatsKind>,
}

/// [`kind` attribute] values of the [MediaStreamTrack][1].
///
/// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
/// [2]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrack-kind
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum TrackStatsKind {
    /// Track is used for the audio content.
    Audio,

    /// Track is used for the video content.
    Video,
}

/// [`RtcStat`] fields of [`RtcStatsType::OutboundRtp`] type based on
/// `mediaType`.
#[serde_with::skip_serializing_none]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(tag = "mediaType", rename_all = "camelCase")]
pub enum RtcOutboundRtpStreamMediaType {
    /// Fields when `mediaType` is `audio`.
    Audio {
        /// Total number of samples that have been sent over this RTP stream.
        total_samples_sent: Option<u64>,

        /// Whether the last RTP packet sent contained voice activity or not
        /// based on the presence of the V bit in the extension header.
        voice_activity_flag: Option<bool>,
    },

    /// Fields when `mediaType` is `video`.
    Video {
        /// Width of the last encoded frame.
        ///
        /// The resolution of the encoded frame may be lower than the media
        /// source (see [RTCVideoSourceStats.width][1]).
        ///
        /// Before the first frame is encoded this attribute is missing.
        ///
        /// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcvideosourcestats-width
        frame_width: Option<u64>,

        /// Height of the last encoded frame.
        ///
        /// The resolution of the encoded frame may be lower than the media
        /// source (see [RTCVideoSourceStats.height][1]).
        ///
        /// Before the first frame is encoded this attribute is missing.
        ///
        /// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcvideosourcestats-height
        frame_height: Option<u64>,

        /// Number of encoded frames during the last second.
        ///
        /// This may be lower than the media source frame rate (see
        /// [RTCVideoSourceStats.framesPerSecond][1]).
        ///
        /// [1]: https://tinyurl.com/rrmkrfk
        frames_per_second: Option<u64>,
    },
}

/// Statistics for an outbound [RTP] stream that is currently sent with this
/// [RTCPeerConnection] object.
///
/// When there are multiple [RTP] streams connected to the same sender, such
/// as when using simulcast or RTX, there will be one
/// [`RtcOutboundRtpStreamStats`] per RTP stream, with distinct values of
/// the `ssrc` attribute, and all these senders will have a reference to
/// the same "sender" object (of type [RTCAudioSenderStats][1] or
/// [RTCVideoSenderStats][2]) and "track" object (of type
/// [RTCSenderAudioTrackAttachmentStats][3] or
/// [RTCSenderVideoTrackAttachmentStats][4]).
///
/// [`RtcStatsType::OutboundRtp`] variant.
///
/// [Full doc on W3C][5].
///
/// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
/// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
/// [1]: https://w3.org/TR/webrtc-stats/#dom-rtcaudiosenderstats
/// [2]: https://w3.org/TR/webrtc-stats/#dom-rtcvideosenderstats
/// [3]: https://tinyurl.com/sefa5z4
/// [4]: https://tinyurl.com/rkuvpl4
/// [5]: https://w3.org/TR/webrtc-stats/#outboundrtpstats-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcOutboundRtpStreamStats {
    /// ID of the stats object representing the current track attachment to the
    /// sender of this stream.
    pub track_id: Option<String>,

    /// Fields which should be in the [`RtcStat`] based on `mediaType`.
    #[serde(flatten)]
    pub media_type: RtcOutboundRtpStreamMediaType,

    /// Total number of bytes sent for this SSRC.
    pub bytes_sent: Option<u64>,

    /// Total number of RTP packets sent for this SSRC.
    pub packets_sent: Option<u64>,

    /// ID of the stats object representing the track currently
    /// attached to the sender of this stream.
    pub media_source_id: Option<String>,
}

/// Properties of a `candidate` in [Section 15.1 of RFC 5245][1].
/// It corresponds to a [RTCIceTransport] object.
///
/// [`RtcStatsType::LocalCandidate`] or [`RtcStatsType::RemoteCandidate`]
/// variant.
///
/// [Full doc on W3C][2].
///
/// [RTCIceTransport]: https://w3.org/TR/webrtc#dom-rtcicetransport
/// [1]: https://tools.ietf.org/html/rfc5245#section-15.1
/// [2]: https://w3.org/TR/webrtc-stats/#icecandidate-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcIceCandidateStats {
    /// Unique ID that is associated to the object that was inspected to
    /// produce the [RTCTransportStats][1] associated with this candidate.
    ///
    /// [1]: https://w3.org/TR/webrtc-stats/#transportstats-dict%2A
    pub transport_id: Option<String>,

    /// Address of the candidate, allowing for IPv4 addresses, IPv6 addresses,
    /// and fully qualified domain names (FQDNs).
    pub address: Option<String>,

    /// Port number of the candidate.
    pub port: u16,

    /// Valid values for transport is one of `udp` and `tcp`.
    pub protocol: Protocol,

    /// Type of the ICE candidate.
    pub candidate_type: CandidateType,

    /// Calculated as defined in [Section 15.1 of RFC 5245][1].
    ///
    /// [1]: https://tools.ietf.org/html/rfc5245#section-15.1
    pub priority: u32,

    /// For local candidates this is the URL of the ICE server from which the
    /// candidate was obtained. It is the same as the
    /// [url surfaced in the RTCPeerConnectionIceEvent][1].
    ///
    /// `None` for remote candidates.
    ///
    /// [1]: https://w3.org/TR/webrtc#rtcpeerconnectioniceevent
    pub url: Option<String>,

    /// Protocol used by the endpoint to communicate with the TURN server.
    ///
    /// Only present for local candidates.
    pub relay_protocol: Option<Protocol>,
}

/// [`RtcStat`] fields of [`RtcStatsType::MediaSource`] type based on its
/// `kind`.
#[serde_with::skip_serializing_none]
#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum MediaKind {
    /// Fields when `kind` is `video`.
    Video {
        /// Width (in pixels) of the last frame originating from the source.
        /// Before a frame has been produced this attribute is missing.
        width: Option<u32>,

        /// Height (in pixels) of the last frame originating from the source.
        /// Before a frame has been produced this attribute is missing.
        height: Option<u32>,

        /// Number of frames originating from the source, measured during the
        /// last second. For the first second of this object's lifetime this
        /// attribute is missing.
        frames_per_second: Option<u32>,
    },

    /// Fields when `kind` is `audio`.
    Audio {
        /// Audio level of the media source.
        audio_level: Option<Float>,

        /// Audio energy of the media source.
        total_audio_energy: Option<Float>,

        /// Audio duration of the media source.
        total_samples_duration: Option<Float>,
    },
}

/// Statistics for the media produced by a [MediaStreamTrack][1] that is
/// currently attached to an [RTCRtpSender]. This reflects the media that is fed
/// to the encoder after [getUserMedia] constraints have been applied (i.e. not
/// the raw media produced by the camera).
///
/// [`RtcStatsType::MediaSource`] variant.
///
/// [Full doc on W3C][2].
///
/// [RTCRtpSender]: https://w3.org/TR/webrtc#rtcrtpsender-interface
/// [getUserMedia]: https://tinyurl.com/sngpyr6
/// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
/// [2]: https://w3.org/TR/webrtc-stats/#dom-rtcstatstype-media-source
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MediaSourceStats {
    /// Value of the [MediaStreamTrack][1]'s ID attribute.
    ///
    /// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack
    pub track_identifier: Option<String>,

    /// Fields which should be in the [`RtcStat`] based on `kind`.
    #[serde(flatten)]
    pub kind: MediaKind,
}

#[cfg(feature = "extended-stats")]
/// Statistics for a codec that is currently used by [RTP] streams being sent or
/// received by [RTCPeerConnection] object.
///
/// [`RtcStatsType::Codec`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
/// [RTCPeerConnection]: https://w3.org/TR/webrtc#dom-rtcpeerconnection
/// [1]: https://w3.org/TR/webrtc-stats/#dom-rtccodecstats
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcCodecStats {
    /// [Payload type][1] as used in [RTP] encoding or decoding.
    ///
    /// [RTP]: https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
    /// [1]: https://tools.ietf.org/html/rfc3550#page-14
    pub payload_type: u32,

    /// The codec MIME media `type/subtype` (e.g. `video/vp8` or equivalent).
    pub mime_type: String,

    /// Media sampling rate.
    pub clock_rate: u32,
}

#[cfg(feature = "extended-stats")]
/// Information about a certificate used by [RTCIceTransport].
///
/// [`RtcStatsType::Certificate`] variant.
///
/// [Full doc on W3C][1].
///
/// [RTCIceTransport]: https://w3.org/TR/webrtc#dom-rtcicetransport
/// [1]: https://w3.org/TR/webrtc-stats/#certificatestats-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcCertificateStats {
    /// Fingerprint of the certificate.
    ///
    /// Only use the fingerprint value as defined in [Section 5 of RFC
    /// 4572][1].
    ///
    /// [1]: https://tools.ietf.org/html/rfc4572#section-5
    pub fingerprint: String,

    /// Hash function used to compute the certificate fingerprint.
    /// For instance, `sha-256`.
    pub fingerprint_algorithm: String,

    /// The DER-encoded Base64 representation of the certificate.
    pub base64_certificate: String,
}

/// Representation of [DOMHighResTimeStamp][1].
///
/// Can be converted to the [`SystemTime`] with millisecond-wise accuracy.
///
/// [`HighResTimeStamp`] type is a [`f64`] and is used to store a time value
/// in milliseconds. This type can be used to describe a discrete point in time
/// or a time interval (the difference in time between two discrete points in
/// time).
///
/// The time, given in milliseconds, should be accurate to 5 µs (microseconds),
/// with the fractional part of the number indicating fractions of a
/// millisecond. However, if the browser is unable to provide a time value
/// accurate to 5 µs (due, for example, to hardware or software constraints),
/// the browser can represent the value as a time in milliseconds accurate to a
/// millisecond. Also note the section below on reduced time precision
/// controlled by browser preferences to avoid timing attacks and
/// fingerprinting.
///
/// Further, if the device or operating system the user agent is running on
/// doesn't have a clock accurate to the microsecond level, they may only be
/// accurate to the millisecond.
///
/// [1]: https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct HighResTimeStamp(pub f64);

impl From<HighResTimeStamp> for SystemTime {
    fn from(timestamp: HighResTimeStamp) -> Self {
        Self::UNIX_EPOCH + Duration::from_secs_f64(timestamp.0 / 100.0)
    }
}

impl TryFrom<SystemTime> for HighResTimeStamp {
    type Error = SystemTimeError;

    fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
        Ok(Self(
            time.duration_since(SystemTime::UNIX_EPOCH)?.as_secs_f64() * 100.0,
        ))
    }
}

/// Hashing string representation.
///
/// Some people believe that such behavior is incorrect (but in some programming
/// languages this is a default behavior) due to `NaN`, `Inf` or `-Inf` (they
/// all will have the same hashes).
/// But in the case of [`RtcStat`] received from the client, there should be no
/// such situations, and the hash will always be correct.
impl Hash for HighResTimeStamp {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.to_string().hash(state);
    }
}

/// Comparison string representations.
///
/// Such implementation is required, so that the results of comparing values and
/// comparing hashes match.
impl PartialEq for HighResTimeStamp {
    fn eq(&self, other: &Self) -> bool {
        self.0.to_string().eq(&other.0.to_string())
    }
}

/// [`f64`] wrapper with [`Hash`] implementation.
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub struct Float(pub f64);

/// Hashing string representation.
///
/// Some people believe that such behavior is incorrect (but in some programming
/// languages this is a default behavior) due to `NaN`, `Inf` or `-Inf` (they
/// all will have the same hashes).
/// But in the case of [`RtcStat`] received from the client, there should be no
/// such situations, and the hash will always be correct.
impl Hash for Float {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.to_string().hash(state);
    }
}

/// Comparison string representations.
///
/// Such implementation is required, so that the results of comparing values and
/// comparing hashes match.
impl PartialEq for Float {
    fn eq(&self, other: &Self) -> bool {
        self.0.to_string().eq(&other.0.to_string())
    }
}

#[cfg(feature = "extended-stats")]
/// Information about the connection to an ICE server (e.g. STUN or TURN).
///
/// [`RtcStatsType::IceServer`] variant.
///
/// [Full doc on W3C][1].
///
/// [1]: https://w3.org/TR/webrtc-stats/#ice-server-dict%2A
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RtcIceServerStats {
    /// URL of the ICE server (e.g. TURN or STUN server).
    pub url: String,

    /// Port number used by the client.
    pub port: u16,

    /// Protocol used by the client to connect to ICE server.
    pub protocol: Protocol,

    /// Total amount of requests that have been sent to this server.
    pub total_requests_sent: Option<u64>,

    /// Total amount of responses received from this server.
    pub total_responses_received: Option<u64>,

    /// Sum of RTTs for all requests that have been sent where a response has
    /// been received.
    pub total_round_trip_time: Option<HighResTimeStamp>,
}