opentelemetry_derive/
lib.rs

1//! # Usage
2//!
3//! Add the crate to your `Cargo.toml`:
4//!
5//! ```toml
6//! opentelemetry_derive = "0.1"
7//! ```
8//!
9//! You also need to have `opentelemetry` as a dependency, because the macros will generate code
10//! that references `opentelemetry::{Key, KeyValue, StringValue, Value}`. The version does not
11//! matter as long as it publishes those types.
12//!
13//! ## `Key`
14//!
15//! If you do not set the key explicitly, the macro will autogenerate one,
16//! which will be your type's name, lowercased:
17//!
18//! ```rust
19//! use opentelemetry_derive::Key;
20//!
21//! #[derive(Key)]
22//! struct Auto;
23//!
24//! #[derive(Key)]
25//! #[otel(key = "custom")]
26//! struct Overriden;
27//! ```
28//!
29//! ## `Value`
30//!
31//! You must specify an intermediate type, into which your own type will be converted,
32//! that is itself already convertible into a [Value]:
33//!
34//! ```rust
35//! use opentelemetry_derive::Value;
36//!
37//! #[derive(Value)]
38//! #[otel(variant = i64)]
39//! struct Counter {
40//!     count: i64,
41//! }
42//!
43//! impl From<&Counter> for i64 {
44//!     fn from(value: &Counter) -> Self {
45//!         value.count
46//!     }
47//! }
48//! ```
49//!
50//! ## `StringValue`
51//!
52//! Your type must implement [ToString] (probably through [Display](std::fmt::Display)):
53//!
54//! ```rust
55//! use std::fmt;
56//!
57//! use opentelemetry_derive::StringValue;
58//!
59//! #[derive(StringValue)]
60//! enum Method {
61//!     Get,
62//!     Post,
63//! }
64//!
65//! impl fmt::Display for Method {
66//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67//!         write!(
68//!             f,
69//!             "{}",
70//!             match self {
71//!                 Self::Get => "get",
72//!                 Self::Post => "post",
73//!             }
74//!         )
75//!     }
76//! }
77//! ```
78//!
79//! ## `KeyValue`
80//!
81//! References to your type must be both `Into<Key>` and `Into<Value>`:
82//!
83//! ```rust
84//! use opentelemetry::{Key, Value};
85//! use opentelemetry_derive::KeyValue;
86//!
87//! #[derive(KeyValue)]
88//! struct Config {
89//!     value: bool,
90//! }
91//!
92//! const KEY: &str = "config";
93//!
94//! impl From<&Config> for Key {
95//!     fn from(_: &Config) -> Self {
96//!         Self::from(KEY)
97//!     }
98//! }
99//!
100//! impl From<&Config> for Value {
101//!     fn from(value: &Config) -> Self {
102//!         Value::from(value.value)
103//!     }
104//! }
105//! ```
106//!
107//! Of course you can combine all the derives instead of manually implementing the required conversions:
108//!
109//! ```rust
110//! use std::fmt;
111//!
112//! use opentelemetry::StringValue;
113//! use opentelemetry_derive::{Key, KeyValue, StringValue, Value};
114//!
115//! #[derive(Key, KeyValue, StringValue, Value)]
116//! #[otel(key = "req", variant = StringValue)]
117//! struct Request {
118//!     query: String,
119//! }
120//!
121//! impl fmt::Display for Request {
122//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123//!         write!(f, "{}", self.query)
124//!     }
125//! }
126//! ```
127//!
128//! [Value]: https://docs.rs/opentelemetry/latest/opentelemetry/enum.Value.html
129
130/// Derive conversion into [Key].
131///
132/// The optional `key` attribute overrides the autogenerated key (type name, lowercased).
133///
134/// [Key]: https://docs.rs/opentelemetry/latest/opentelemetry/struct.Key.html
135pub use opentelemetry_derive_impl::Key;
136
137/// Derive conversion into [KeyValue].
138///
139/// [KeyValue]: https://docs.rs/opentelemetry/latest/opentelemetry/struct.KeyValue.html
140pub use opentelemetry_derive_impl::KeyValue;
141
142/// Derive conversion into [StringValue].
143///
144/// [StringValue]: https://docs.rs/opentelemetry/latest/opentelemetry/struct.StringValue.html
145pub use opentelemetry_derive_impl::StringValue;
146
147/// Derive conversion into [Value].
148///
149/// The mandatory `variant` attribute is the intermediate type, into which your value will be converted
150/// (e.g. [StringValue]
151/// if your type should be represented as a string, or [i64]).
152/// This variant should itself be one of the types than can be implicitly converted to [Value].
153///
154/// [StringValue]: https://docs.rs/opentelemetry/latest/opentelemetry/struct.StringValue.html
155/// [Value]: https://docs.rs/opentelemetry/latest/opentelemetry/enum.Value.html
156pub use opentelemetry_derive_impl::Value;
157
158#[cfg(test)]
159mod tests {
160    extern crate self as opentelemetry_derive;
161
162    use std::fmt;
163
164    use opentelemetry::{Key, KeyValue, StringValue, Value};
165
166    use crate::{Key, KeyValue, StringValue, Value};
167
168    #[test]
169    fn test_key() {
170        #[derive(Key)]
171        struct Auto;
172
173        assert_eq!(Key::from(Auto).as_str(), "auto");
174        assert_eq!(Key::from(&Auto).as_str(), "auto");
175
176        #[derive(Key)]
177        #[otel(key = "custom")]
178        struct Overriden;
179
180        assert_eq!(Key::from(Overriden).as_str(), "custom");
181        assert_eq!(Key::from(&Overriden).as_str(), "custom");
182    }
183
184    #[test]
185    fn test_value() {
186        #[derive(Value)]
187        #[otel(variant = i64)]
188        struct Counter {
189            count: i64,
190        }
191
192        impl From<&Counter> for i64 {
193            fn from(value: &Counter) -> Self {
194                value.count
195            }
196        }
197
198        let count = 3;
199        let counter = Counter { count };
200
201        assert_eq!(Value::from(&counter).as_str(), count.to_string());
202        assert_eq!(Value::from(counter).as_str(), count.to_string());
203    }
204
205    #[test]
206    fn test_string_value() {
207        #[derive(StringValue)]
208        enum Method {
209            Get,
210            Post,
211        }
212
213        impl fmt::Display for Method {
214            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215                write!(
216                    f,
217                    "{}",
218                    match self {
219                        Self::Get => "get",
220                        Self::Post => "post",
221                    }
222                )
223            }
224        }
225
226        assert_eq!(
227            StringValue::from(&Method::Get).as_str(),
228            Method::Get.to_string()
229        );
230        assert_eq!(
231            StringValue::from(Method::Post).as_str(),
232            Method::Post.to_string()
233        );
234    }
235
236    #[test]
237    fn test_key_value() {
238        #[derive(KeyValue)]
239        struct Config {
240            value: bool,
241        }
242
243        const KEY: &str = "config";
244
245        impl From<&Config> for Key {
246            fn from(_: &Config) -> Self {
247                Self::from(KEY)
248            }
249        }
250
251        impl From<&Config> for Value {
252            fn from(value: &Config) -> Self {
253                Value::from(value.value)
254            }
255        }
256
257        let value = true;
258        let config = Config { value };
259
260        assert_eq!(KeyValue::from(&config), KeyValue::new(KEY, value));
261        assert_eq!(KeyValue::from(config), KeyValue::new(KEY, value));
262    }
263
264    #[test]
265    fn test_all() {
266        #[derive(Key, KeyValue, StringValue, Value)]
267        #[otel(key = "req", variant = StringValue)]
268        struct Request {
269            query: String,
270        }
271
272        impl fmt::Display for Request {
273            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274                write!(f, "{}", self.query)
275            }
276        }
277
278        let query = "foo=bar";
279        let request = Request {
280            query: query.to_string(),
281        };
282
283        assert_eq!(KeyValue::from(&request), KeyValue::new("req", query));
284        assert_eq!(KeyValue::from(request), KeyValue::new("req", query));
285    }
286}