1use core::ops::ControlFlow;
6
7use emit_core::{
8 and::And,
9 emitter::Emitter,
10 event::{Event, ToEvent},
11 extent::{Extent, ToExtent},
12 or::Or,
13 path::Path,
14 props::{ErasedProps, Props},
15 str::{Str, ToStr},
16 template::{self, Template},
17 timestamp::Timestamp,
18 value::{ToValue, Value},
19 well_known::{KEY_EVT_KIND, KEY_METRIC_AGG, KEY_METRIC_NAME, KEY_METRIC_VALUE},
20};
21
22use crate::kind::Kind;
23
24pub use self::{sampler::Sampler, source::Source};
25
26pub struct Metric<'a, P> {
34 mdl: Path<'a>,
35 name: Str<'a>,
36 agg: Str<'a>,
37 extent: Option<Extent>,
38 tpl: Option<Template<'a>>,
39 value: Value<'a>,
40 props: P,
41}
42
43impl<'a, P> Metric<'a, P> {
44 pub fn new(
57 mdl: impl Into<Path<'a>>,
58 name: impl Into<Str<'a>>,
59 agg: impl Into<Str<'a>>,
60 extent: impl ToExtent,
61 value: impl Into<Value<'a>>,
62 props: P,
63 ) -> Self {
64 Metric {
65 mdl: mdl.into(),
66 extent: extent.to_extent(),
67 tpl: None,
68 name: name.into(),
69 agg: agg.into(),
70 value: value.into(),
71 props,
72 }
73 }
74
75 pub fn mdl(&self) -> &Path<'a> {
79 &self.mdl
80 }
81
82 pub fn with_mdl(mut self, mdl: impl Into<Path<'a>>) -> Self {
86 self.mdl = mdl.into();
87 self
88 }
89
90 pub fn name(&self) -> &Str<'a> {
94 &self.name
95 }
96
97 pub fn with_name(mut self, name: impl Into<Str<'a>>) -> Self {
101 self.name = name.into();
102 self
103 }
104
105 pub fn agg(&self) -> &Str<'a> {
111 &self.agg
112 }
113
114 pub fn with_agg(mut self, agg: impl Into<Str<'a>>) -> Self {
120 self.agg = agg.into();
121 self
122 }
123
124 pub fn value(&self) -> &Value<'a> {
128 &self.value
129 }
130
131 pub fn with_value(mut self, value: impl Into<Value<'a>>) -> Self {
135 self.value = value.into();
136 self
137 }
138
139 pub fn extent(&self) -> Option<&Extent> {
143 self.extent.as_ref()
144 }
145
146 pub fn with_extent(mut self, extent: impl ToExtent) -> Self {
150 self.extent = extent.to_extent();
151 self
152 }
153
154 pub fn ts(&self) -> Option<&Timestamp> {
160 self.extent.as_ref().map(|extent| extent.as_point())
161 }
162
163 pub fn ts_start(&self) -> Option<&Timestamp> {
169 self.extent
170 .as_ref()
171 .and_then(|extent| extent.as_range())
172 .map(|span| &span.start)
173 }
174
175 pub fn tpl(&self) -> &Template<'a> {
179 self.tpl.as_ref().unwrap_or(&TEMPLATE)
180 }
181
182 pub fn with_tpl(mut self, tpl: impl Into<Template<'a>>) -> Self {
186 self.tpl = Some(tpl.into());
187 self
188 }
189
190 pub fn props(&self) -> &P {
194 &self.props
195 }
196
197 pub fn with_props<U>(self, props: U) -> Metric<'a, U> {
201 Metric {
202 mdl: self.mdl,
203 extent: self.extent,
204 tpl: self.tpl,
205 name: self.name,
206 agg: self.agg,
207 value: self.value,
208 props,
209 }
210 }
211
212 pub fn map_props<U>(self, map: impl FnOnce(P) -> U) -> Metric<'a, U> {
216 Metric {
217 mdl: self.mdl,
218 extent: self.extent,
219 tpl: self.tpl,
220 name: self.name,
221 agg: self.agg,
222 value: self.value,
223 props: map(self.props),
224 }
225 }
226}
227
228impl<'a, P: Props> ToEvent for Metric<'a, P> {
229 type Props<'b>
230 = &'b Self
231 where
232 Self: 'b;
233
234 fn to_event<'b>(&'b self) -> Event<'b, Self::Props<'b>> {
235 Event::new(
236 self.mdl.by_ref(),
237 self.tpl().by_ref(),
238 self.extent.clone(),
239 self,
240 )
241 }
242}
243
244impl<'a, P: Props> Metric<'a, P> {
245 pub fn by_ref<'b>(&'b self) -> Metric<'b, &'b P> {
249 Metric {
250 mdl: self.mdl.by_ref(),
251 extent: self.extent.clone(),
252 tpl: self.tpl.as_ref().map(|tpl| tpl.by_ref()),
253 name: self.name.by_ref(),
254 agg: self.agg.by_ref(),
255 value: self.value.by_ref(),
256 props: &self.props,
257 }
258 }
259
260 pub fn erase<'b>(&'b self) -> Metric<'b, &'b dyn ErasedProps> {
264 Metric {
265 mdl: self.mdl.by_ref(),
266 extent: self.extent.clone(),
267 tpl: self.tpl.as_ref().map(|tpl| tpl.by_ref()),
268 name: self.name.by_ref(),
269 agg: self.agg.by_ref(),
270 value: self.value.by_ref(),
271 props: &self.props,
272 }
273 }
274}
275
276impl<'a, P> ToExtent for Metric<'a, P> {
277 fn to_extent(&self) -> Option<Extent> {
278 self.extent.clone()
279 }
280}
281
282impl<'a, P: Props> Props for Metric<'a, P> {
283 fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
284 &'kv self,
285 mut for_each: F,
286 ) -> ControlFlow<()> {
287 for_each(KEY_EVT_KIND.to_str(), Kind::Metric.to_value())?;
288 for_each(KEY_METRIC_NAME.to_str(), self.name.to_value())?;
289 for_each(KEY_METRIC_AGG.to_str(), self.agg.to_value())?;
290 for_each(KEY_METRIC_VALUE.to_str(), self.value.by_ref())?;
291
292 self.props.for_each(for_each)
293 }
294}
295
296const TEMPLATE_PARTS: &'static [template::Part<'static>] = &[
298 template::Part::hole("metric_agg"),
299 template::Part::text(" of "),
300 template::Part::hole("metric_name"),
301 template::Part::text(" is "),
302 template::Part::hole("metric_value"),
303];
304
305static TEMPLATE: Template<'static> = Template::new(TEMPLATE_PARTS);
306
307pub mod source {
308 use self::sampler::ErasedSampler;
315
316 use super::*;
317
318 pub trait Source {
322 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S);
326
327 fn and_sample<U>(self, other: U) -> And<Self, U>
331 where
332 Self: Sized,
333 {
334 And::new(self, other)
335 }
336 }
337
338 impl<'a, T: Source + ?Sized> Source for &'a T {
339 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
340 (**self).sample_metrics(sampler)
341 }
342 }
343
344 impl<T: Source> Source for Option<T> {
345 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
346 if let Some(source) = self {
347 source.sample_metrics(sampler);
348 }
349 }
350 }
351
352 #[cfg(feature = "alloc")]
353 impl<'a, T: Source + ?Sized + 'a> Source for alloc::boxed::Box<T> {
354 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
355 (**self).sample_metrics(sampler)
356 }
357 }
358
359 #[cfg(feature = "alloc")]
360 impl<'a, T: Source + ?Sized + 'a> Source for alloc::sync::Arc<T> {
361 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
362 (**self).sample_metrics(sampler)
363 }
364 }
365
366 impl<T: Source, U: Source> Source for And<T, U> {
367 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
368 self.left().sample_metrics(&sampler);
369 self.right().sample_metrics(&sampler);
370 }
371 }
372
373 impl<T: Source, U: Source> Source for Or<T, U> {
374 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
375 self.left().sample_metrics(&sampler);
376 self.right().sample_metrics(&sampler);
377 }
378 }
379
380 pub struct FromFn<F = fn(&mut dyn ErasedSampler)>(F);
386
387 pub const fn from_fn<F: Fn(&mut dyn ErasedSampler)>(source: F) -> FromFn<F> {
391 FromFn::new(source)
392 }
393
394 impl<F> FromFn<F> {
395 pub const fn new(source: F) -> Self {
399 FromFn(source)
400 }
401 }
402
403 impl<F: Fn(&mut dyn ErasedSampler)> Source for FromFn<F> {
404 fn sample_metrics<S: sampler::Sampler>(&self, mut sampler: S) {
405 (self.0)(&mut sampler)
406 }
407 }
408
409 mod internal {
410 use super::*;
411
412 pub trait DispatchSource {
413 fn dispatch_sample_metrics(&self, sampler: &dyn sampler::ErasedSampler);
414 }
415
416 pub trait SealedSource {
417 fn erase_source(&self) -> crate::internal::Erased<&dyn DispatchSource>;
418 }
419 }
420
421 pub trait ErasedSource: internal::SealedSource {}
427
428 impl<T: Source> ErasedSource for T {}
429
430 impl<T: Source> internal::SealedSource for T {
431 fn erase_source(&self) -> crate::internal::Erased<&dyn internal::DispatchSource> {
432 crate::internal::Erased(self)
433 }
434 }
435
436 impl<T: Source> internal::DispatchSource for T {
437 fn dispatch_sample_metrics(&self, sampler: &dyn sampler::ErasedSampler) {
438 self.sample_metrics(sampler)
439 }
440 }
441
442 impl<'a> Source for dyn ErasedSource + 'a {
443 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
444 self.erase_source().0.dispatch_sample_metrics(&sampler)
445 }
446 }
447
448 impl<'a> Source for dyn ErasedSource + Send + Sync + 'a {
449 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
450 (self as &(dyn ErasedSource + 'a)).sample_metrics(sampler)
451 }
452 }
453
454 #[cfg(test)]
455 mod tests {
456 use super::*;
457 use std::cell::Cell;
458
459 #[test]
460 fn source_sample_emit() {
461 struct MySource;
462
463 impl Source for MySource {
464 fn sample_metrics<S: Sampler>(&self, sampler: S) {
465 sampler.metric(Metric::new(
466 Path::new_raw("test"),
467 "metric 1",
468 "count",
469 crate::Empty,
470 42,
471 crate::Empty,
472 ));
473
474 sampler.metric(Metric::new(
475 Path::new_raw("test"),
476 "metric 2",
477 "count",
478 crate::Empty,
479 42,
480 crate::Empty,
481 ));
482 }
483 }
484
485 let calls = Cell::new(0);
486
487 MySource.sample_metrics(sampler::from_fn(|_| {
488 calls.set(calls.get() + 1);
489 }));
490
491 assert_eq!(2, calls.get());
492 }
493
494 #[test]
495 fn and_sample() {
496 let calls = Cell::new(0);
497
498 from_fn(|sampler| {
499 sampler.metric(Metric::new(
500 Path::new_raw("test"),
501 "metric 1",
502 "count",
503 crate::Empty,
504 42,
505 crate::Empty,
506 ));
507 })
508 .and_sample(from_fn(|sampler| {
509 sampler.metric(Metric::new(
510 Path::new_raw("test"),
511 "metric 2",
512 "count",
513 crate::Empty,
514 42,
515 crate::Empty,
516 ));
517 }))
518 .sample_metrics(sampler::from_fn(|_| {
519 calls.set(calls.get() + 1);
520 }));
521
522 assert_eq!(2, calls.get());
523 }
524
525 #[test]
526 fn from_fn_source() {
527 let calls = Cell::new(0);
528
529 from_fn(|sampler| {
530 sampler.metric(Metric::new(
531 Path::new_raw("test"),
532 "metric 1",
533 "count",
534 crate::Empty,
535 42,
536 crate::Empty,
537 ));
538
539 sampler.metric(Metric::new(
540 Path::new_raw("test"),
541 "metric 2",
542 "count",
543 crate::Empty,
544 42,
545 crate::Empty,
546 ));
547 })
548 .sample_metrics(sampler::from_fn(|_| {
549 calls.set(calls.get() + 1);
550 }));
551
552 assert_eq!(2, calls.get());
553 }
554
555 #[test]
556 fn erased_source() {
557 let source = from_fn(|sampler| {
558 sampler.metric(Metric::new(
559 Path::new_raw("test"),
560 "metric 1",
561 "count",
562 crate::Empty,
563 42,
564 crate::Empty,
565 ));
566
567 sampler.metric(Metric::new(
568 Path::new_raw("test"),
569 "metric 2",
570 "count",
571 crate::Empty,
572 42,
573 crate::Empty,
574 ));
575 });
576
577 let source = &source as &dyn ErasedSource;
578
579 let calls = Cell::new(0);
580
581 source.sample_metrics(sampler::from_fn(|_| {
582 calls.set(calls.get() + 1);
583 }));
584
585 assert_eq!(2, calls.get());
586 }
587 }
588}
589
590#[cfg(feature = "alloc")]
591mod alloc_support {
592 use super::*;
593
594 use alloc::{boxed::Box, vec::Vec};
595 use core::ops::Range;
596
597 use crate::{
598 clock::{Clock, ErasedClock},
599 metric::source::{ErasedSource, Source},
600 };
601
602 pub struct Reporter {
620 sources: Vec<Box<dyn ErasedSource + Send + Sync>>,
621 clock: ReporterClock,
622 }
623
624 impl Reporter {
625 pub const fn new() -> Self {
632 Reporter {
633 sources: Vec::new(),
634 clock: {
635 #[cfg(feature = "std")]
636 {
637 ReporterClock::System
638 }
639 #[cfg(not(feature = "std"))]
640 {
641 ReporterClock::Other(None)
642 }
643 },
644 }
645 }
646
647 pub fn normalize_with_clock(
651 &mut self,
652 clock: impl Clock + Send + Sync + 'static,
653 ) -> &mut Self {
654 self.clock = ReporterClock::Other(Some(Box::new(clock)));
655
656 self
657 }
658
659 pub fn without_normalization(&mut self) -> &mut Self {
663 self.clock = ReporterClock::Other(None);
664
665 self
666 }
667
668 pub fn add_source(&mut self, source: impl Source + Send + Sync + 'static) -> &mut Self {
672 self.sources.push(Box::new(source));
673
674 self
675 }
676
677 pub fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
681 let sampler = TimeNormalizer::new(self.clock.now(), sampler);
682
683 for source in &self.sources {
684 source.sample_metrics(&sampler);
685 }
686 }
687
688 pub fn emit_metrics<E: Emitter>(&self, emitter: E) {
692 self.sample_metrics(sampler::from_emitter(emitter))
693 }
694 }
695
696 impl Source for Reporter {
697 fn sample_metrics<S: sampler::Sampler>(&self, sampler: S) {
698 self.sample_metrics(sampler)
699 }
700 }
701
702 struct TimeNormalizer<S> {
703 now: Option<Timestamp>,
704 inner: S,
705 }
706
707 impl<S> TimeNormalizer<S> {
708 fn new(now: Option<Timestamp>, sampler: S) -> TimeNormalizer<S> {
709 TimeNormalizer {
710 now,
711 inner: sampler,
712 }
713 }
714 }
715
716 impl<S: Sampler> Sampler for TimeNormalizer<S> {
717 fn metric<P: Props>(&self, metric: Metric<P>) {
718 if let Some(now) = self.now {
719 let extent = metric.extent();
720
721 let extent = if let Some(range) = extent.and_then(|extent| extent.as_range()) {
722 normalize_range(now, range)
724 .map(Extent::range)
725 .unwrap_or_else(|| Extent::range(range.clone()))
727 } else {
728 Extent::point(now)
730 };
731
732 self.inner.metric(metric.with_extent(extent))
733 } else {
734 self.inner.metric(metric)
735 }
736 }
737 }
738
739 fn normalize_range(now: Timestamp, range: &Range<Timestamp>) -> Option<Range<Timestamp>> {
740 let len = range.end.duration_since(range.start)?;
743 let start = now.checked_sub(len)?;
744
745 Some(start..now)
746 }
747
748 enum ReporterClock {
749 #[cfg(feature = "std")]
750 System,
751 Other(Option<Box<dyn ErasedClock + Send + Sync>>),
752 }
753
754 impl Clock for ReporterClock {
755 fn now(&self) -> Option<Timestamp> {
756 match self {
757 #[cfg(feature = "std")]
758 ReporterClock::System => crate::platform::system_clock::SystemClock::new().now(),
759 ReporterClock::Other(clock) => clock.now(),
760 }
761 }
762 }
763
764 #[cfg(test)]
765 mod tests {
766 use super::*;
767 use std::time::Duration;
768
769 #[test]
770 fn reporter_is_send_sync() {
771 fn check<T: Send + Sync>() {}
772
773 check::<Reporter>();
774 }
775
776 #[test]
777 #[cfg(not(miri))]
778 fn reporter_sample() {
779 use std::cell::Cell;
780
781 let mut reporter = Reporter::new();
782
783 reporter
784 .add_source(source::from_fn(|sampler| {
785 sampler.metric(Metric::new(
786 Path::new_raw("test"),
787 "metric 1",
788 "count",
789 crate::Empty,
790 42,
791 crate::Empty,
792 ));
793 }))
794 .add_source(source::from_fn(|sampler| {
795 sampler.metric(Metric::new(
796 Path::new_raw("test"),
797 "metric 2",
798 "count",
799 crate::Empty,
800 42,
801 crate::Empty,
802 ));
803 }));
804
805 let calls = Cell::new(0);
806
807 reporter.sample_metrics(sampler::from_fn(|_| {
808 calls.set(calls.get() + 1);
809 }));
810
811 assert_eq!(2, calls.get());
812 }
813
814 struct TestClock(Option<Timestamp>);
815
816 impl Clock for TestClock {
817 fn now(&self) -> Option<Timestamp> {
818 self.0
819 }
820 }
821
822 #[test]
823 #[cfg(all(feature = "std", not(miri)))]
824 fn reporter_normalize_std() {
825 let mut reporter = Reporter::new();
826
827 reporter.add_source(source::from_fn(|sampler| {
828 sampler.metric(Metric::new(
829 Path::new_raw("test"),
830 "metric 1",
831 "count",
832 crate::Empty,
833 42,
834 crate::Empty,
835 ));
836 }));
837
838 reporter.sample_metrics(sampler::from_fn(|metric| {
839 assert!(metric.extent().is_some());
840 }));
841 }
842
843 #[test]
844 fn reporter_normalize_empty_extent() {
845 let mut reporter = Reporter::new();
846
847 reporter.normalize_with_clock(TestClock(Some(Timestamp::MIN)));
848
849 reporter.add_source(source::from_fn(|sampler| {
850 sampler.metric(Metric::new(
851 Path::new_raw("test"),
852 "metric 1",
853 "count",
854 crate::Empty,
855 42,
856 crate::Empty,
857 ));
858 }));
859
860 reporter.sample_metrics(sampler::from_fn(|metric| {
861 assert_eq!(Timestamp::MIN, metric.extent().unwrap().as_point());
862 }));
863 }
864
865 #[test]
866 fn reporter_normalize_point_extent() {
867 let mut reporter = Reporter::new();
868
869 reporter.normalize_with_clock(TestClock(Some(
870 Timestamp::from_unix(Duration::from_secs(37)).unwrap(),
871 )));
872
873 reporter.add_source(source::from_fn(|sampler| {
874 sampler.metric(Metric::new(
875 Path::new_raw("test"),
876 "metric 1",
877 "count",
878 Timestamp::from_unix(Duration::from_secs(100)).unwrap(),
879 42,
880 crate::Empty,
881 ));
882 }));
883
884 reporter.sample_metrics(sampler::from_fn(|metric| {
885 assert_eq!(
886 Timestamp::from_unix(Duration::from_secs(37)).unwrap(),
887 metric.extent().unwrap().as_point()
888 );
889 }));
890 }
891
892 #[test]
893 fn reporter_normalize_range_extent() {
894 let mut reporter = Reporter::new();
895
896 reporter.normalize_with_clock(TestClock(Some(
897 Timestamp::from_unix(Duration::from_secs(350)).unwrap(),
898 )));
899
900 reporter.add_source(source::from_fn(|sampler| {
901 sampler.metric(Metric::new(
902 Path::new_raw("test"),
903 "metric 1",
904 "count",
905 Timestamp::from_unix(Duration::from_secs(100)).unwrap()
906 ..Timestamp::from_unix(Duration::from_secs(200)).unwrap(),
907 42,
908 crate::Empty,
909 ));
910 }));
911
912 reporter.sample_metrics(sampler::from_fn(|metric| {
913 assert_eq!(
914 Timestamp::from_unix(Duration::from_secs(250)).unwrap()
915 ..Timestamp::from_unix(Duration::from_secs(350)).unwrap(),
916 metric.extent().unwrap().as_range().unwrap().clone()
917 );
918 }));
919 }
920 }
921}
922
923#[cfg(feature = "alloc")]
924pub use self::alloc_support::*;
925
926pub mod sampler {
927 use emit_core::empty::Empty;
934
935 use super::*;
936
937 pub trait Sampler {
941 fn metric<P: Props>(&self, metric: Metric<P>);
945 }
946
947 impl<'a, T: Sampler + ?Sized> Sampler for &'a T {
948 fn metric<P: Props>(&self, metric: Metric<P>) {
949 (**self).metric(metric)
950 }
951 }
952
953 impl Sampler for Empty {
954 fn metric<P: Props>(&self, _: Metric<P>) {}
955 }
956
957 pub struct FromEmitter<E>(E);
965
966 impl<E: Emitter> Sampler for FromEmitter<E> {
967 fn metric<P: Props>(&self, metric: Metric<P>) {
968 self.0.emit(metric)
969 }
970 }
971
972 impl<E> FromEmitter<E> {
973 pub const fn new(emitter: E) -> Self {
977 FromEmitter(emitter)
978 }
979 }
980
981 pub const fn from_emitter<E: Emitter>(emitter: E) -> FromEmitter<E> {
987 FromEmitter(emitter)
988 }
989
990 pub struct FromFn<F = fn(Metric<&dyn ErasedProps>)>(F);
996
997 pub const fn from_fn<F: Fn(Metric<&dyn ErasedProps>)>(f: F) -> FromFn<F> {
1001 FromFn(f)
1002 }
1003
1004 impl<F> FromFn<F> {
1005 pub const fn new(sampler: F) -> FromFn<F> {
1009 FromFn(sampler)
1010 }
1011 }
1012
1013 impl<F: Fn(Metric<&dyn ErasedProps>)> Sampler for FromFn<F> {
1014 fn metric<P: Props>(&self, metric: Metric<P>) {
1015 (self.0)(metric.erase())
1016 }
1017 }
1018
1019 mod internal {
1020 use super::*;
1021
1022 pub trait DispatchSampler {
1023 fn dispatch_metric(&self, metric: Metric<&dyn ErasedProps>);
1024 }
1025
1026 pub trait SealedSampler {
1027 fn erase_sampler(&self) -> crate::internal::Erased<&dyn DispatchSampler>;
1028 }
1029 }
1030
1031 pub trait ErasedSampler: internal::SealedSampler {}
1037
1038 impl<T: Sampler> ErasedSampler for T {}
1039
1040 impl<T: Sampler> internal::SealedSampler for T {
1041 fn erase_sampler(&self) -> crate::internal::Erased<&dyn internal::DispatchSampler> {
1042 crate::internal::Erased(self)
1043 }
1044 }
1045
1046 impl<T: Sampler> internal::DispatchSampler for T {
1047 fn dispatch_metric(&self, metric: Metric<&dyn ErasedProps>) {
1048 self.metric(metric)
1049 }
1050 }
1051
1052 impl<'a> Sampler for dyn ErasedSampler + 'a {
1053 fn metric<P: Props>(&self, metric: Metric<P>) {
1054 self.erase_sampler().0.dispatch_metric(metric.erase())
1055 }
1056 }
1057
1058 impl<'a> Sampler for dyn ErasedSampler + Send + Sync + 'a {
1059 fn metric<P: Props>(&self, metric: Metric<P>) {
1060 (self as &(dyn ErasedSampler + 'a)).metric(metric)
1061 }
1062 }
1063
1064 #[cfg(test)]
1065 mod tests {
1066 use super::*;
1067 use std::cell::Cell;
1068
1069 #[test]
1070 fn from_fn_sampler() {
1071 let called = Cell::new(false);
1072
1073 let sampler = from_fn(|metric| {
1074 assert_eq!("test", metric.name());
1075
1076 called.set(true);
1077 });
1078
1079 sampler.metric(Metric::new(
1080 Path::new_raw("test"),
1081 "test",
1082 "count",
1083 Empty,
1084 1,
1085 Empty,
1086 ));
1087
1088 assert!(called.get());
1089 }
1090
1091 #[test]
1092 fn erased_sampler() {
1093 let called = Cell::new(false);
1094
1095 let sampler = from_fn(|metric| {
1096 assert_eq!("test", metric.name());
1097
1098 called.set(true);
1099 });
1100
1101 let sampler = &sampler as &dyn ErasedSampler;
1102
1103 sampler.metric(Metric::new(
1104 Path::new_raw("test"),
1105 "test",
1106 "count",
1107 Empty,
1108 1,
1109 Empty,
1110 ));
1111
1112 assert!(called.get());
1113 }
1114 }
1115}
1116
1117#[cfg(test)]
1118mod tests {
1119 use super::*;
1120 use std::time::Duration;
1121
1122 use crate::Timestamp;
1123
1124 #[test]
1125 fn metric_new() {
1126 let metric = Metric::new(
1127 Path::new_raw("test"),
1128 "my metric",
1129 "count",
1130 Timestamp::from_unix(Duration::from_secs(1)),
1131 42,
1132 ("metric_prop", true),
1133 );
1134
1135 assert_eq!("test", metric.mdl());
1136 assert_eq!(
1137 Timestamp::from_unix(Duration::from_secs(1)).unwrap(),
1138 metric.extent().unwrap().as_point()
1139 );
1140 assert_eq!("my metric", metric.name());
1141 assert_eq!("count", metric.agg());
1142 assert_eq!(42, metric.value().by_ref().cast::<i32>().unwrap());
1143 assert_eq!(true, metric.props().pull::<bool, _>("metric_prop").unwrap());
1144 }
1145
1146 #[test]
1147 fn metric_to_event() {
1148 let metric = Metric::new(
1149 Path::new_raw("test"),
1150 "my metric",
1151 "count",
1152 Timestamp::from_unix(Duration::from_secs(1)),
1153 42,
1154 ("metric_prop", true),
1155 );
1156
1157 let evt = metric.to_event();
1158
1159 assert_eq!("test", evt.mdl());
1160 assert_eq!(
1161 Timestamp::from_unix(Duration::from_secs(1)).unwrap(),
1162 evt.extent().unwrap().as_point()
1163 );
1164 assert_eq!("count of my metric is 42", evt.msg().to_string());
1165 assert_eq!("count", evt.props().pull::<Str, _>(KEY_METRIC_AGG).unwrap());
1166 assert_eq!(42, evt.props().pull::<i32, _>(KEY_METRIC_VALUE).unwrap());
1167 assert_eq!(
1168 "my metric",
1169 evt.props().pull::<Str, _>(KEY_METRIC_NAME).unwrap()
1170 );
1171 assert_eq!(true, evt.props().pull::<bool, _>("metric_prop").unwrap());
1172 assert_eq!(
1173 Kind::Metric,
1174 evt.props().pull::<Kind, _>(KEY_EVT_KIND).unwrap()
1175 );
1176 }
1177
1178 #[test]
1179 fn metric_to_event_uses_tpl() {
1180 assert_eq!(
1181 "test",
1182 Metric::new(
1183 Path::new_raw("test"),
1184 "my metric",
1185 "count",
1186 Timestamp::from_unix(Duration::from_secs(1)),
1187 42,
1188 ("metric_prop", true),
1189 )
1190 .with_tpl(Template::literal("test"))
1191 .to_event()
1192 .msg()
1193 .to_string(),
1194 );
1195 }
1196
1197 #[test]
1198 fn metric_to_extent() {
1199 for (case, expected) in [
1200 (
1201 Some(Timestamp::from_unix(Duration::from_secs(1)).unwrap()),
1202 Some(Extent::point(
1203 Timestamp::from_unix(Duration::from_secs(1)).unwrap(),
1204 )),
1205 ),
1206 (None, None),
1207 ] {
1208 let metric = Metric::new(
1209 Path::new_raw("test"),
1210 "my metric",
1211 "count",
1212 case,
1213 42,
1214 ("metric_prop", true),
1215 );
1216
1217 let extent = metric.to_extent();
1218
1219 assert_eq!(
1220 expected.map(|extent| extent.as_range().cloned()),
1221 extent.map(|extent| extent.as_range().cloned())
1222 );
1223 }
1224 }
1225}