metrique_core/lib.rs
1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4#![deny(missing_docs)]
5#![doc = include_str!("../README.md")]
6#![cfg_attr(docsrs, feature(doc_cfg))]
7
8use metrique_writer_core::{EntryWriter, entry::SampleGroupElement};
9
10mod atomics;
11mod close_value_impls;
12pub mod concat;
13mod inflectable_entry_impls;
14mod namestyle;
15
16pub use atomics::Counter;
17pub use namestyle::NameStyle;
18
19/// Close a given value
20///
21/// This gives an opportunity do things like stopping timers, collecting fanned-in data, etc.
22///
23/// If possible, implement this trait for both `&MyValue` and `MyValue`, as this will allow
24/// use via smart pointers (e.g. on `Arc<MyValue>`).
25///
26/// ```
27/// use metrique::{CloseValue, CloseValueRef};
28///
29/// struct MyValue;
30///
31/// impl CloseValue for &'_ MyValue {
32/// type Closed = u32;
33/// fn close(self) -> Self::Closed { 42 }
34/// }
35///
36/// impl CloseValue for MyValue {
37/// type Closed = u32;
38/// fn close(self) -> Self::Closed { self.close_ref() /* delegate to by-ref implementation */ }
39/// }
40/// ```
41///
42/// This trait is also used for entries that can be closed as [`CloseEntry`].
43///
44/// ## Why `CloseValue` is not implemented for `&str` or `&String`
45///
46/// Since `CloseValue` takes its argument by value, an implementation of `CloseValue`
47/// for `&str` or `&String` would have to allocate.
48///
49/// The most common case this is encountered is when using `#[metrics(subfield)]`:
50///
51/// ```compile_fail
52/// # use std::sync::Arc;
53/// use metrique::unit_of_work::metrics;
54///
55/// #[metrics(subfield)]
56/// struct ChildMetrics {
57/// field: String,
58/// }
59///
60/// #[metrics]
61/// struct MainMetric {
62/// // this is allowed when using `#[metrics(subfield)]`
63/// #[metrics(flatten)]
64/// child: Arc<ChildMetrics>,
65/// }
66/// ```
67///
68/// Since `#[metrics(subfield)]` supports taking the fields by reference, as
69/// you this would have to allocate and therefore there is no implementation
70/// to avoid surprise allocations.
71///
72/// There are a few options to deal with it:
73///
74/// 1. Use a `&'static str` or a `#[metrics(value(string))]` enum instead of a `String`, avoiding
75/// the allocation.
76/// ```rust
77/// # use metrique::unit_of_work::metrics;
78/// #[metrics(value(string))]
79/// enum MyEnum { Foo }
80///
81/// #[metrics(subfield)]
82/// struct ChildMetrics {
83/// field_1: &'static str,
84/// field_2: MyEnum,
85/// }
86/// ```
87/// 2. Use `#[metrics(subfield_owned)]`, which implements `CloseValue` by value.
88/// In that case, you can't use the subfield via an `Arc` but only by-value:
89/// ```rust
90/// # use metrique::unit_of_work::metrics;
91/// #[metrics(subfield_owned)]
92/// struct ChildMetrics {
93/// field: String,
94/// }
95///
96/// #[metrics]
97/// struct MainMetric {
98/// // must use by-value
99/// #[metrics(flatten)]
100/// child: ChildMetrics,
101/// }
102/// ```
103/// 3. If you are fine with allocating, you could make your own string wrapper type.
104/// ```rust
105/// # use metrique::unit_of_work::metrics;
106/// struct StringValue(String);
107/// impl metrique::CloseValue for &StringValue {
108/// type Closed = String;
109///
110/// fn close(self) -> String { self.0.clone() }
111/// }
112///
113/// impl metrique::CloseValue for StringValue {
114/// type Closed = String;
115///
116/// fn close(self) -> String { self.0 }
117/// }
118///
119/// #[metrics(subfield)]
120/// struct ChildMetrics {
121/// field: StringValue,
122/// }
123/// ```
124#[diagnostic::on_unimplemented(
125 message = "CloseValue is not implemented for {Self}",
126 note = "You may need to add `#[metrics]` to `{Self}` or implement `CloseValue` directly."
127)]
128pub trait CloseValue {
129 /// The type produced by closing this value
130 type Closed;
131
132 /// Close the value
133 fn close(self) -> Self::Closed;
134}
135
136mod private {
137 pub trait Sealed {}
138}
139
140/// Close a value without taking ownership
141///
142/// This trait is meant to be used for [`CloseValue`] impls for smart-pointer-like
143/// types, as in
144///
145/// ```
146/// use metrique::{CloseValue, CloseValueRef};
147///
148/// struct Smaht<T>(T);
149///
150/// impl<T: CloseValueRef> CloseValue for &'_ Smaht<T> {
151/// type Closed = T::Closed;
152/// fn close(self) -> T::Closed { self.0.close_ref() }
153/// }
154///
155/// impl<T: CloseValueRef> CloseValue for Smaht<T> {
156/// type Closed = T::Closed;
157/// fn close(self) -> T::Closed { self.0.close_ref() }
158/// }
159/// ```
160///
161/// This trait is not to be implemented or called directly. It mostly exists
162/// because it makes trait inference a bit smarter (it's also not a full
163/// trait alias due to trait inference reasons).
164#[diagnostic::on_unimplemented(
165 message = "CloseValueRef is not implemented for {Self}",
166 note = "You may need to add `#[metrics]` to `{Self}` or implement `CloseValueRef` directly."
167)]
168pub trait CloseValueRef: private::Sealed {
169 /// The type produced by closing this value
170 type Closed;
171
172 /// Close the value
173 fn close_ref(&self) -> Self::Closed;
174}
175
176impl<C, T> private::Sealed for T where for<'a> &'a Self: CloseValue<Closed = C> {}
177
178impl<C, T> CloseValueRef for T
179where
180 for<'a> &'a Self: CloseValue<Closed = C>,
181{
182 type Closed = C;
183 fn close_ref(&self) -> Self::Closed {
184 <&Self>::close(self)
185 }
186}
187
188/// An object that can be closed into an [InflectableEntry]. This is the
189/// normal way of generating a metric entry - by starting with a a struct
190/// that implements this trait (that is generally generated using the `#[metrics]` macro),
191/// wrapping it in a [`RootEntry`] to generate an [`Entry`], and then emitting that
192/// to an [`EntrySink`].
193///
194/// This is just a trait alias for `CloseValue<Closed: InflectableEntry>`.
195///
196/// [close-value]: CloseValue
197/// [`Entry`]: metrique_writer_core::Entry
198/// [`EntrySink`]: metrique_writer_core::EntrySink
199/// [`RootEntry`]: https://docs.rs/metrique/0.1/metrique/struct.RootEntry.html
200pub trait CloseEntry: CloseValue<Closed: InflectableEntry> {}
201impl<T: ?Sized + CloseValue<Closed: InflectableEntry>> CloseEntry for T {}
202
203/// A trait for metric entries where the names of the fields can be "inflected"
204/// using a [`NameStyle`]. This defines the interface for metric *sources*
205/// that want to be able to generate metric structs that can be renamed
206/// without having any string operations happen at runtime.
207///
208/// Both `InflectableEntry` and [`Entry`] are intended to be "pure" structs - all
209/// references to channels, counters and the like are expected to be resolved when
210/// creating the `InflectableEntry`.
211///
212/// An `InflectableEntry` with any specific set of type parameters is equivalent to an
213/// [`Entry`]. It should be wrapped by a wrapper that implements [`Entry`] and delegates
214/// to it with a particular set of type parameters, for example `RootEntry`, and then
215/// emitting that to an [`EntrySink`].
216///
217/// The normal way of generating a metric entry is by starting with a struct
218/// that implements [`CloseEntry`] (that is generally generated
219/// using the `#[metrics]` macro), wrapping it in a `RootEntry` to generate an
220/// [`Entry`], and then emitting that to an entry sink.
221///
222/// Design note: in theory you could have a world where `InflectableEntry`
223/// and [`Entry`] are the same trait (where the sinks use the default type parameters).
224/// In practice, it is desired that the trait [`Entry`] will have very few breaking
225/// changes since it needs to be identical throughout a program that wants to emit
226/// metrics to a single destination, and therefore `InflectableEntry` is kept separate.
227///
228/// ## Manual Implementations
229///
230/// Currently, there is no (stable) non-macro way of generating an [`InflectableEntry`]
231/// that actually inflects names. If you want to make a manual entry, it is recommended
232/// to implement the [`Entry`] trait, then use a field with `#[metrics(flatten_entry)]`
233/// as follows - though note that this will ignore inflections:
234///
235/// ```
236/// use metrique::unit_of_work::metrics;
237/// use metrique::writer::{Entry, EntryWriter};
238///
239/// struct MyCustomEntry;
240///
241/// impl Entry for MyCustomEntry {
242/// fn write<'a>(&'a self, writer: &mut impl EntryWriter<'a>) {
243/// writer.value("custom", "custom");
244/// }
245/// }
246///
247/// #[metrics]
248/// struct MyMetric {
249/// #[metrics(flatten_entry, no_close)]
250/// field: MyCustomEntry,
251/// }
252/// ```
253///
254/// [`Entry`]: metrique_writer_core::Entry
255/// [`NameStyle`]: namestyle::NameStyle
256/// [`Entry`]: metrique_writer_core::Entry
257/// [`EntrySink`]: metrique_writer_core::EntrySink
258pub trait InflectableEntry<NS: namestyle::NameStyle = namestyle::Identity> {
259 /// Write this metric entry to an EntryWriter
260 fn write<'a>(&'a self, w: &mut impl EntryWriter<'a>);
261 /// Sample group
262 fn sample_group(&self) -> impl Iterator<Item = SampleGroupElement> {
263 vec![].into_iter()
264 }
265}