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, CounterGuard, OwnedCounterGuard};
17pub use namestyle::{DynamicNameStyle, Identity, KebabCase, NameStyle, PascalCase, SnakeCase};
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 label = "This type must implement `CloseValue`",
126 message = "CloseValue is not implemented for {Self}",
127 note = "You may need to add `#[metrics]` to `{Self}` or implement `CloseValue` directly.",
128 note = "if {Self} implements `Value` but not `CloseValue`, add `#[metrics(no_close)]`",
129 note = "If this type is `&T`, is closed inside a flattened entry, and `T` implements `CloseValue`, consider using `#[metrics(subfield_owned)]`."
130)]
131pub trait CloseValue {
132 /// The type produced by closing this value
133 type Closed;
134
135 /// Close the value
136 fn close(self) -> Self::Closed;
137}
138
139mod private {
140 pub trait Sealed {}
141}
142
143/// Close a value without taking ownership
144///
145/// This trait is meant to be used for [`CloseValue`] impls for smart-pointer-like
146/// types, as in
147///
148/// ```
149/// use metrique::{CloseValue, CloseValueRef};
150///
151/// struct Smaht<T>(T);
152///
153/// impl<T: CloseValueRef> CloseValue for &'_ Smaht<T> {
154/// type Closed = T::Closed;
155/// fn close(self) -> T::Closed { self.0.close_ref() }
156/// }
157///
158/// impl<T: CloseValueRef> CloseValue for Smaht<T> {
159/// type Closed = T::Closed;
160/// fn close(self) -> T::Closed { self.0.close_ref() }
161/// }
162/// ```
163///
164/// This trait is not to be implemented or called directly. It mostly exists
165/// because it makes trait inference a bit smarter (it's also not a full
166/// trait alias due to trait inference reasons).
167#[diagnostic::on_unimplemented(
168 message = "CloseValueRef is not implemented for {Self}",
169 note = "You may need to add `#[metrics]` to `{Self}` or implement `CloseValueRef` directly."
170)]
171pub trait CloseValueRef: private::Sealed {
172 /// The type produced by closing this value
173 type Closed;
174
175 /// Close the value
176 fn close_ref(&self) -> Self::Closed;
177}
178
179impl<C, T> private::Sealed for T where for<'a> &'a Self: CloseValue<Closed = C> {}
180
181impl<C, T> CloseValueRef for T
182where
183 for<'a> &'a Self: CloseValue<Closed = C>,
184{
185 type Closed = C;
186 fn close_ref(&self) -> Self::Closed {
187 <&Self>::close(self)
188 }
189}
190
191/// An object that can be closed into an [InflectableEntry]. This is the
192/// normal way of generating a metric entry - by starting with a a struct
193/// that implements this trait (that is generally generated using the `#[metrics]` macro),
194/// wrapping it in a [`RootEntry`] to generate an [`Entry`], and then emitting that
195/// to an [`EntrySink`].
196///
197/// This is just a trait alias for `CloseValue<Closed: InflectableEntry>`.
198///
199/// [close-value]: CloseValue
200/// [`Entry`]: metrique_writer_core::Entry
201/// [`EntrySink`]: metrique_writer_core::EntrySink
202/// [`RootEntry`]: https://docs.rs/metrique/0.1/metrique/struct.RootEntry.html
203pub trait CloseEntry: CloseValue<Closed: InflectableEntry> {}
204impl<T: ?Sized + CloseValue<Closed: InflectableEntry>> CloseEntry for T {}
205
206/// A trait for metric entries where the names of the fields can be "inflected"
207/// using a [`NameStyle`]. This defines the interface for metric *sources*
208/// that want to be able to generate metric structs that can be renamed
209/// without having any string operations happen at runtime.
210///
211/// Both `InflectableEntry` and [`Entry`] are intended to be "pure" structs - all
212/// references to channels, counters and the like are expected to be resolved when
213/// creating the `InflectableEntry`.
214///
215/// An `InflectableEntry` with any specific set of type parameters is equivalent to an
216/// [`Entry`]. It should be wrapped by a wrapper that implements [`Entry`] and delegates
217/// to it with a particular set of type parameters, for example `RootEntry`, and then
218/// emitting that to an [`EntrySink`].
219///
220/// The normal way of generating a metric entry is by starting with a struct
221/// that implements [`CloseEntry`] (that is generally generated
222/// using the `#[metrics]` macro), wrapping it in a `RootEntry` to generate an
223/// [`Entry`], and then emitting that to an entry sink.
224///
225/// Design note: in theory you could have a world where `InflectableEntry`
226/// and [`Entry`] are the same trait (where the sinks use the default type parameters).
227/// In practice, it is desired that the trait [`Entry`] will have very few breaking
228/// changes since it needs to be identical throughout a program that wants to emit
229/// metrics to a single destination, and therefore `InflectableEntry` is kept separate.
230///
231/// ## Manual Implementations
232///
233/// Currently, there is no (stable) non-macro way of generating an [`InflectableEntry`]
234/// that actually inflects names. If you want to make a manual entry, it is recommended
235/// to implement the [`Entry`] trait, then use a field with `#[metrics(flatten_entry)]`
236/// as follows - though note that this will ignore inflections:
237///
238/// ```
239/// use metrique::unit_of_work::metrics;
240/// use metrique::writer::{Entry, EntryWriter};
241///
242/// struct MyCustomEntry;
243///
244/// impl Entry for MyCustomEntry {
245/// fn write<'a>(&'a self, writer: &mut impl EntryWriter<'a>) {
246/// writer.value("custom", "custom");
247/// }
248/// }
249///
250/// #[metrics]
251/// struct MyMetric {
252/// #[metrics(flatten_entry, no_close)]
253/// field: MyCustomEntry,
254/// }
255/// ```
256///
257/// [`Entry`]: metrique_writer_core::Entry
258/// [`NameStyle`]: namestyle::NameStyle
259/// [`Entry`]: metrique_writer_core::Entry
260/// [`EntrySink`]: metrique_writer_core::EntrySink
261pub trait InflectableEntry<NS: namestyle::NameStyle = namestyle::Identity> {
262 /// Write this metric entry to an EntryWriter
263 fn write<'a>(&'a self, w: &mut impl EntryWriter<'a>);
264 /// Sample group
265 fn sample_group(&self) -> impl Iterator<Item = SampleGroupElement> {
266 vec![].into_iter()
267 }
268}