1use std::any::TypeId;
15
16use smallvec::SmallVec;
17
18use crate::Unit;
19
20#[derive(Debug, Clone, Copy)]
22#[non_exhaustive]
23pub struct Style {
24 pub index: u8,
26 pub name: &'static str,
28}
29
30#[non_exhaustive]
35pub struct Styles;
36
37impl Styles {
38 pub const PRESERVE: Style = Style {
40 index: 0,
41 name: "preserve",
42 };
43 pub const PASCAL: Style = Style {
45 index: 1,
46 name: "PascalCase",
47 };
48 pub const SNAKE: Style = Style {
50 index: 2,
51 name: "snake_case",
52 };
53 pub const KEBAB: Style = Style {
55 index: 3,
56 name: "kebab-case",
57 };
58 pub const SCREAMING_SNAKE: Style = Style {
60 index: 4,
61 name: "SCREAMING_SNAKE_CASE",
62 };
63 pub const ALL: &'static [Style] = &[
65 Self::PRESERVE,
66 Self::PASCAL,
67 Self::SNAKE,
68 Self::KEBAB,
69 Self::SCREAMING_SNAKE,
70 ];
71 pub const COUNT: usize = Self::ALL.len();
73}
74
75#[derive(Debug)]
77pub struct EntryDescriptor {
78 name: &'static str,
79 fields: &'static [FieldDescriptor],
80 timestamp: Option<TimestampDescriptor>,
81}
82
83impl EntryDescriptor {
84 pub const fn builder(
86 name: &'static str,
87 fields: &'static [FieldDescriptor],
88 ) -> EntryDescriptorBuilder {
89 EntryDescriptorBuilder {
90 name,
91 fields,
92 timestamp: None,
93 }
94 }
95}
96
97#[derive(Debug)]
99pub struct FieldDescriptor {
100 names: [&'static str; Styles::COUNT],
101 flags: &'static [FieldFlag],
102 skipped_flags: &'static [FieldFlag],
103 shape: FieldShape<'static>,
104 unit: Option<Unit>,
105}
106
107impl FieldDescriptor {
108 pub const fn builder(name: &'static str) -> FieldDescriptorBuilder {
114 FieldDescriptorBuilder {
115 names: [name; Styles::COUNT],
116 flags: &[],
117 skipped_flags: &[],
118 shape: FieldShape::Opaque,
119 unit: None,
120 }
121 }
122}
123
124#[derive(Debug)]
126pub struct TimestampDescriptor {
127 name: &'static str,
128}
129
130impl TimestampDescriptor {
131 pub const fn new(name: &'static str) -> Self {
133 Self { name }
134 }
135
136 pub fn name(&self) -> &str {
138 self.name
139 }
140}
141
142#[derive(Debug, Clone)]
144#[non_exhaustive]
145pub enum Descriptors<'a> {
146 Available(AvailableDescriptors<'a>),
148 Unavailable,
150}
151
152#[derive(Debug, Clone)]
154pub struct AvailableDescriptors<'a>(SmallVec<[DescriptorRef<'a>; 2]>);
155
156impl<'a> AvailableDescriptors<'a> {
157 pub fn iter(&self) -> impl Iterator<Item = &DescriptorRef<'a>> {
159 self.0.iter()
160 }
161
162 pub fn len(&self) -> usize {
164 self.0.len()
165 }
166
167 pub fn is_empty(&self) -> bool {
169 self.0.is_empty()
170 }
171}
172
173impl<'a> std::ops::Index<usize> for AvailableDescriptors<'a> {
174 type Output = DescriptorRef<'a>;
175 fn index(&self, index: usize) -> &Self::Output {
176 &self.0[index]
177 }
178}
179
180impl<'a> IntoIterator for AvailableDescriptors<'a> {
181 type Item = DescriptorRef<'a>;
182 type IntoIter = DescriptorIter<'a>;
183 fn into_iter(self) -> Self::IntoIter {
184 DescriptorIter(self.0.into_iter())
185 }
186}
187
188#[derive(Debug)]
190pub struct DescriptorIter<'a>(smallvec::IntoIter<[DescriptorRef<'a>; 2]>);
191
192impl<'a> Iterator for DescriptorIter<'a> {
193 type Item = DescriptorRef<'a>;
194 fn next(&mut self) -> Option<Self::Item> {
195 self.0.next()
196 }
197
198 fn size_hint(&self) -> (usize, Option<usize>) {
199 self.0.size_hint()
200 }
201}
202
203impl<'a> ExactSizeIterator for DescriptorIter<'a> {}
204
205impl<'a> Descriptors<'a> {
206 pub fn available(iter: impl IntoIterator<Item = DescriptorRef<'a>>) -> Self {
208 Descriptors::Available(AvailableDescriptors(iter.into_iter().collect()))
209 }
210
211 pub fn is_available(&self) -> bool {
213 matches!(self, Descriptors::Available(_))
214 }
215
216 pub fn unwrap(self) -> AvailableDescriptors<'a> {
221 match self {
222 Descriptors::Available(v) => v,
223 Descriptors::Unavailable => panic!("called unwrap() on Descriptors::Unavailable"),
224 }
225 }
226
227 pub fn into_available(self) -> Option<AvailableDescriptors<'a>> {
229 match self {
230 Descriptors::Available(v) => Some(v),
231 Descriptors::Unavailable => None,
232 }
233 }
234
235 pub fn map_available(self, f: impl FnMut(DescriptorRef<'a>) -> DescriptorRef<'a>) -> Self {
237 match self {
238 Descriptors::Available(a) => {
239 let mapped: SmallVec<[DescriptorRef<'a>; 2]> = a.0.into_iter().map(f).collect();
240 Descriptors::Available(AvailableDescriptors(mapped))
241 }
242 Descriptors::Unavailable => Descriptors::Unavailable,
243 }
244 }
245
246 pub fn chain(self, other: Descriptors<'a>) -> Self {
250 match (self, other) {
251 (Descriptors::Available(mut a), Descriptors::Available(b)) => {
252 a.0.extend(b.0);
253 Descriptors::Available(a)
254 }
255 _ => Descriptors::Unavailable,
256 }
257 }
258}
259
260#[derive(Clone, Debug)]
281pub struct DescriptorRef<'a> {
282 descriptor: &'a EntryDescriptor,
283 id: DescriptorId,
284 prefixes: SmallVec<[&'static str; 1]>,
285 style_index: u8,
286 extra_flags: &'static [FieldFlag],
287}
288
289impl<'a> DescriptorRef<'a> {
290 #[doc(hidden)]
292 pub fn from_static(
293 descriptor: &'static EntryDescriptor,
294 style_index: u8,
295 ) -> DescriptorRef<'static> {
296 let id = DescriptorId::compute(descriptor, &[]);
297 DescriptorRef {
298 descriptor,
299 id,
300 prefixes: SmallVec::new(),
301 style_index,
302 extra_flags: &[],
303 }
304 }
305
306 #[doc(hidden)]
309 pub fn with_prefix(mut self, prefix: &'static str) -> Self {
310 self.prefixes.insert(0, prefix);
311 self.id = DescriptorId::compute(self.descriptor, &self.prefixes);
312 self
313 }
314
315 #[doc(hidden)]
319 pub fn with_extra_flags(mut self, flags: &'static [FieldFlag]) -> Self {
320 self.extra_flags = flags;
321 self
322 }
323
324 pub fn id(&self) -> DescriptorId {
326 self.id
327 }
328
329 pub fn name(&self) -> &str {
331 self.descriptor.name
332 }
333
334 pub fn fields_len(&self) -> usize {
336 self.descriptor.fields.len()
337 }
338
339 pub fn timestamp(&self) -> Option<&TimestampDescriptor> {
341 self.descriptor.timestamp.as_ref()
342 }
343
344 pub fn fields(&self) -> impl Iterator<Item = FieldView<'_>> {
346 (0..self.descriptor.fields.len()).map(move |i| FieldView { desc: self, idx: i })
347 }
348}
349
350#[derive(Clone, Debug)]
352pub struct FieldView<'a> {
353 desc: &'a DescriptorRef<'a>,
354 idx: usize,
355}
356
357impl<'a> FieldView<'a> {
358 pub fn name_parts(&self) -> impl Iterator<Item = &str> {
361 self.desc.prefixes.iter().copied().chain(std::iter::once(
362 self.desc.descriptor.fields[self.idx].names[self.desc.style_index as usize],
363 ))
364 }
365
366 pub fn base_name(&self) -> &'static str {
371 self.desc.descriptor.fields[self.idx].names[self.desc.style_index as usize]
372 }
373 pub fn flags(&self) -> impl Iterator<Item = &'a FieldFlag> {
376 let field = &self.desc.descriptor.fields[self.idx];
377 let skipped = field.skipped_flags;
378 field.flags.iter().chain(
379 self.desc
380 .extra_flags
381 .iter()
382 .filter(move |ef| !skipped.iter().any(|s| s.type_id() == ef.type_id())),
383 )
384 }
385
386 pub fn shape(&self) -> FieldShape<'a> {
388 self.desc.descriptor.fields[self.idx].shape
389 }
390
391 pub fn unit(&self) -> Option<Unit> {
393 self.desc.descriptor.fields[self.idx].unit
394 }
395}
396
397#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
406pub struct DescriptorId(u64);
407
408impl DescriptorId {
409 fn compute(descriptor: &EntryDescriptor, prefixes: &[&'static str]) -> Self {
411 let mut id = descriptor as *const EntryDescriptor as u64;
412 for p in prefixes {
413 id = id.wrapping_mul(31).wrapping_add(p.as_ptr() as u64);
414 }
415 DescriptorId(id)
416 }
417}
418
419#[non_exhaustive]
421#[derive(Debug, Clone, Copy, PartialEq, Eq)]
422pub enum FieldShape<'a> {
423 Known(KnownShape),
425 Optional(ShapeRef<'a>),
427 Flex {
429 key: StringShape,
431 value: ShapeRef<'a>,
433 },
434 List(ShapeRef<'a>),
436 Opaque,
438}
439
440#[non_exhaustive]
442#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
443pub enum KnownShape {
444 Bool,
446 U8,
448 U16,
450 U32,
452 U64,
454 I8,
456 I16,
458 I32,
460 I64,
462 F32,
464 F64,
466 String,
468 Bytes,
470}
471
472#[non_exhaustive]
474#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
475pub enum StringShape {
476 String,
478}
479
480#[derive(Debug, Clone, Copy, PartialEq, Eq)]
482pub struct ShapeRef<'a> {
483 inner: &'a FieldShape<'a>,
484}
485
486impl<'a> ShapeRef<'a> {
487 pub fn get(&self) -> &FieldShape<'a> {
489 self.inner
490 }
491
492 pub const fn new(inner: &'a FieldShape<'a>) -> Self {
494 Self { inner }
495 }
496}
497
498#[derive(Debug)]
533pub struct FieldFlag {
534 type_id: TypeId,
535 construct: fn() -> crate::value::MetricFlags<'static>,
536}
537
538impl FieldFlag {
539 pub const fn new<T: crate::value::FlagConstructor + 'static>() -> Self {
541 Self {
542 type_id: TypeId::of::<T>(),
543 construct: T::construct,
544 }
545 }
546
547 pub fn type_id(&self) -> TypeId {
549 self.type_id
550 }
551
552 pub fn is<T: 'static>(&self) -> bool {
554 self.type_id == TypeId::of::<T>()
555 }
556
557 pub fn construct(&self) -> crate::value::MetricFlags<'static> {
563 (self.construct)()
564 }
565}
566
567pub struct EntryDescriptorBuilder {
569 name: &'static str,
570 fields: &'static [FieldDescriptor],
571 timestamp: Option<TimestampDescriptor>,
572}
573
574impl EntryDescriptorBuilder {
575 pub const fn timestamp(mut self, ts: TimestampDescriptor) -> Self {
577 self.timestamp = Some(ts);
578 self
579 }
580
581 pub const fn maybe_timestamp(mut self, ts: Option<TimestampDescriptor>) -> Self {
583 self.timestamp = ts;
584 self
585 }
586
587 pub const fn build(self) -> EntryDescriptor {
589 EntryDescriptor {
590 name: self.name,
591 fields: self.fields,
592 timestamp: self.timestamp,
593 }
594 }
595}
596
597pub struct FieldDescriptorBuilder {
599 names: [&'static str; Styles::COUNT],
600 flags: &'static [FieldFlag],
601 skipped_flags: &'static [FieldFlag],
602 shape: FieldShape<'static>,
603 unit: Option<Unit>,
604}
605
606impl FieldDescriptorBuilder {
607 pub const fn pascal(mut self, name: &'static str) -> Self {
609 self.names[Styles::PASCAL.index as usize] = name;
610 self
611 }
612
613 pub const fn snake(mut self, name: &'static str) -> Self {
615 self.names[Styles::SNAKE.index as usize] = name;
616 self
617 }
618
619 pub const fn kebab(mut self, name: &'static str) -> Self {
621 self.names[Styles::KEBAB.index as usize] = name;
622 self
623 }
624
625 pub const fn screaming_snake(mut self, name: &'static str) -> Self {
627 self.names[Styles::SCREAMING_SNAKE.index as usize] = name;
628 self
629 }
630
631 pub const fn flags(mut self, flags: &'static [FieldFlag]) -> Self {
633 self.flags = flags;
634 self
635 }
636
637 pub const fn skipped_flags(mut self, flags: &'static [FieldFlag]) -> Self {
640 self.skipped_flags = flags;
641 self
642 }
643
644 pub const fn shape(mut self, shape: FieldShape<'static>) -> Self {
646 self.shape = shape;
647 self
648 }
649
650 pub const fn unit(mut self, unit: Unit) -> Self {
652 self.unit = Some(unit);
653 self
654 }
655
656 pub const fn maybe_unit(mut self, unit: Option<Unit>) -> Self {
658 self.unit = unit;
659 self
660 }
661
662 pub const fn build(self) -> FieldDescriptor {
664 FieldDescriptor {
665 names: self.names,
666 flags: self.flags,
667 skipped_flags: self.skipped_flags,
668 shape: self.shape,
669 unit: self.unit,
670 }
671 }
672}
673
674const _: () = assert!(
678 Styles::COUNT == 5,
679 "Styles::COUNT changed; update FieldDescriptorBuilder with a new style method"
680);
681#[cfg(test)]
682mod tests {
683 use super::*;
684
685 #[test]
686 fn descriptor_ref_stable_id() {
687 static DESC: EntryDescriptor = EntryDescriptor::builder("Test", &[]).build();
688 let r1 = DescriptorRef::from_static(&DESC, 0);
689 let r2 = DescriptorRef::from_static(&DESC, 0);
690 assert_eq!(r1.id(), r2.id());
691 assert_eq!(r1.name(), "Test");
692 }
693
694 #[test]
695 fn different_descriptors_different_ids() {
696 static A: EntryDescriptor = EntryDescriptor::builder("A", &[]).build();
697 static B: EntryDescriptor = EntryDescriptor::builder("B", &[]).build();
698 assert_ne!(
699 DescriptorRef::from_static(&A, 0).id(),
700 DescriptorRef::from_static(&B, 0).id()
701 );
702 }
703
704 #[test]
705 fn prefix_changes_id() {
706 static DESC: EntryDescriptor = EntryDescriptor::builder("T", &[]).build();
707 let plain = DescriptorRef::from_static(&DESC, 0);
708 let prefixed = DescriptorRef::from_static(&DESC, 0).with_prefix("Api");
709 assert_ne!(plain.id(), prefixed.id());
710 }
711
712 #[test]
713 fn field_name_no_prefix() {
714 static FIELDS: [FieldDescriptor; 1] = [FieldDescriptor::builder("MyField").build()];
715 static DESC: EntryDescriptor = EntryDescriptor::builder("T", &FIELDS).build();
716
717 let d = DescriptorRef::from_static(&DESC, 0);
718 assert_eq!(d.fields().next().unwrap().base_name(), "MyField");
719 }
720
721 #[test]
722 fn field_name_with_prefix() {
723 static FIELDS: [FieldDescriptor; 1] = [FieldDescriptor::builder("Latency").build()];
724 static DESC: EntryDescriptor = EntryDescriptor::builder("T", &FIELDS).build();
725
726 let d = DescriptorRef::from_static(&DESC, 0).with_prefix("Api");
727 let fields: Vec<_> = d.fields().collect();
728 let parts: Vec<&str> = fields[0].name_parts().collect();
729 assert_eq!(parts, vec!["Api", "Latency"]);
730 }
731
732 #[test]
733 fn field_name_with_nested_prefixes() {
734 static FIELDS: [FieldDescriptor; 1] = [FieldDescriptor::builder("Latency").build()];
735 static DESC: EntryDescriptor = EntryDescriptor::builder("T", &FIELDS).build();
736
737 let d = DescriptorRef::from_static(&DESC, 0)
739 .with_prefix("Api")
740 .with_prefix("Http");
741 let fields: Vec<_> = d.fields().collect();
742 let parts: Vec<&str> = fields[0].name_parts().collect();
743 assert_eq!(parts, vec!["Http", "Api", "Latency"]);
744 }
745
746 #[test]
747 fn field_view_iteration() {
748 static FIELDS: [FieldDescriptor; 2] = [
749 FieldDescriptor::builder("Alpha").build(),
750 FieldDescriptor::builder("Beta").unit(Unit::Count).build(),
751 ];
752 static DESC: EntryDescriptor = EntryDescriptor::builder("T", &FIELDS).build();
753
754 let d = DescriptorRef::from_static(&DESC, 0);
755 let fields: Vec<_> = d.fields().collect();
756 assert_eq!(fields.len(), 2);
757 assert_eq!(fields[0].base_name(), "Alpha");
758 assert_eq!(fields[1].base_name(), "Beta");
759 assert_eq!(fields[1].unit(), Some(Unit::Count));
760 }
761
762 #[test]
763 fn timestamp() {
764 static DESC: EntryDescriptor = EntryDescriptor::builder("E", &[])
765 .timestamp(TimestampDescriptor::new("ts"))
766 .build();
767 let d = DescriptorRef::from_static(&DESC, 0);
768 assert_eq!(d.timestamp().unwrap().name(), "ts");
769 }
770
771 #[test]
772 fn hand_written_entry_empty() {
773 use crate::{Entry, EntryWriter};
774 struct HandWritten;
775 impl Entry for HandWritten {
776 fn write<'a>(&'a self, _w: &mut impl EntryWriter<'a>) {}
777 }
778 assert_eq!(HandWritten.descriptors().is_available(), false);
779 }
780
781 #[test]
782 fn boxentry_forwards() {
783 use crate::{BoxEntry, Entry, EntryWriter};
784 static DESC: EntryDescriptor = EntryDescriptor::builder("X", &[]).build();
785 struct WithDesc;
786 impl Entry for WithDesc {
787 fn write<'a>(&'a self, _w: &mut impl EntryWriter<'a>) {}
788 fn descriptors(&self) -> Descriptors<'_> {
789 Descriptors::available(std::iter::once(DescriptorRef::from_static(&DESC, 0)))
790 }
791 }
792 let boxed = BoxEntry::new(WithDesc);
793 let descs = boxed.descriptors().unwrap();
794 assert_eq!(descs.len(), 1);
795 assert_eq!(descs[0].name(), "X");
796 }
797}