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}