Skip to main content

wide_log/
key.rs

1/// Trait for wide-event keys.
2///
3/// Each variant of a key enum represents a JSON key in the wide event.
4/// The `wide_log!` macro generates the enum and this trait impl
5/// automatically from the JSON structure — users do not implement this
6/// trait manually.
7///
8/// # Path-Based Duration / Timestamp / ID
9///
10/// [`DURATION_PATH`](Key::DURATION_PATH) specifies the nested path to the
11/// duration field (e.g. `[Duration, TotalMs]`). The guard auto-sets this
12/// field to the elapsed time in milliseconds on drop.
13///
14/// [`TIMESTAMP_PATH`](Key::TIMESTAMP_PATH) specifies the nested path to the
15/// timestamp field (e.g. `[Event, Timestamp]`). The guard auto-sets this
16/// field to the current time as an RFC 3339 string on drop.
17///
18/// [`ID_PATH`](Key::ID_PATH) specifies the nested path to the id field
19/// (e.g. `[Event, Id]`). The builder auto-sets this field to the generated
20/// ID string on `build()`.
21///
22/// The user does **not** define `Log`, `Level`, or `Message` variants. Log
23/// entries are handled entirely internally by a `LogEntry` type and the
24/// `log_entries` field on [`WideEvent`].
25///
26/// [`WideEvent`]: crate::WideEvent
27pub trait Key: Copy + Eq + 'static {
28    /// Returns the JSON string representation of this key.
29    fn as_str(self) -> &'static str;
30
31    /// The total number of keys in the enum.
32    const MAX_KEYS: usize;
33
34    /// All keys in enum order, indexed by `as_index`.
35    const KEYS: &'static [Self];
36
37    /// All key strings in enum order, indexed by `as_index`.
38    /// Used for O(1) `as_str()` via array index instead of a match.
39    const KEY_STRS: &'static [&'static str];
40
41    /// Returns the discriminant index of this key.
42    fn as_index(self) -> usize;
43
44    /// Path to the duration field (e.g. `[Duration, TotalMs]`).
45    /// Auto-set by the guard on drop. The value is in milliseconds (u64).
46    const DURATION_PATH: &'static [Self];
47
48    /// Path to the timestamp field (e.g. `[Event, Timestamp]`).
49    /// Auto-set by the guard on drop. The value is an RFC 3339 string.
50    const TIMESTAMP_PATH: &'static [Self];
51
52    /// Path to the id field (e.g. `[Event, Id]`).
53    /// Auto-set by the builder on `build()`. The value is a string.
54    const ID_PATH: &'static [Self];
55}
56
57#[cfg(test)]
58pub(crate) mod test_support {
59    use super::Key;
60
61    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
62    #[repr(u8)]
63    pub enum TestKey {
64        Duration,
65        TotalMs,
66        Event,
67        Timestamp,
68        Id,
69        Service,
70        Name,
71        Version,
72        Requests,
73        Status,
74        Details,
75        Tag,
76        Count,
77        Flag,
78    }
79
80    impl Key for TestKey {
81        fn as_str(self) -> &'static str {
82            match self {
83                TestKey::Duration => "duration",
84                TestKey::TotalMs => "total_ms",
85                TestKey::Event => "event",
86                TestKey::Timestamp => "timestamp",
87                TestKey::Id => "id",
88                TestKey::Service => "service",
89                TestKey::Name => "name",
90                TestKey::Version => "version",
91                TestKey::Requests => "requests",
92                TestKey::Status => "status",
93                TestKey::Details => "details",
94                TestKey::Tag => "tag",
95                TestKey::Count => "count",
96                TestKey::Flag => "flag",
97            }
98        }
99
100        const MAX_KEYS: usize = 14;
101
102        const KEYS: &'static [Self] = &[
103            TestKey::Duration,
104            TestKey::TotalMs,
105            TestKey::Event,
106            TestKey::Timestamp,
107            TestKey::Id,
108            TestKey::Service,
109            TestKey::Name,
110            TestKey::Version,
111            TestKey::Requests,
112            TestKey::Status,
113            TestKey::Details,
114            TestKey::Tag,
115            TestKey::Count,
116            TestKey::Flag,
117        ];
118
119        const KEY_STRS: &'static [&'static str] = &[
120            "duration",
121            "total_ms",
122            "event",
123            "timestamp",
124            "id",
125            "service",
126            "name",
127            "version",
128            "requests",
129            "status",
130            "details",
131            "tag",
132            "count",
133            "flag",
134        ];
135
136        fn as_index(self) -> usize {
137            self as usize
138        }
139
140        const DURATION_PATH: &'static [Self] = &[TestKey::Duration, TestKey::TotalMs];
141        const TIMESTAMP_PATH: &'static [Self] = &[TestKey::Event, TestKey::Timestamp];
142        const ID_PATH: &'static [Self] = &[TestKey::Event, TestKey::Id];
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    use test_support::TestKey;
150
151    #[test]
152    fn key_as_str() {
153        assert_eq!(TestKey::Duration.as_str(), "duration");
154        assert_eq!(TestKey::TotalMs.as_str(), "total_ms");
155        assert_eq!(TestKey::Event.as_str(), "event");
156        assert_eq!(TestKey::Timestamp.as_str(), "timestamp");
157        assert_eq!(TestKey::Id.as_str(), "id");
158        assert_eq!(TestKey::Service.as_str(), "service");
159        assert_eq!(TestKey::Name.as_str(), "name");
160        assert_eq!(TestKey::Version.as_str(), "version");
161        assert_eq!(TestKey::Requests.as_str(), "requests");
162        assert_eq!(TestKey::Status.as_str(), "status");
163        assert_eq!(TestKey::Details.as_str(), "details");
164        assert_eq!(TestKey::Tag.as_str(), "tag");
165        assert_eq!(TestKey::Count.as_str(), "count");
166        assert_eq!(TestKey::Flag.as_str(), "flag");
167    }
168
169    #[test]
170    fn key_as_index() {
171        assert_eq!(TestKey::Duration.as_index(), 0);
172        assert_eq!(TestKey::TotalMs.as_index(), 1);
173        assert_eq!(TestKey::Event.as_index(), 2);
174        assert_eq!(TestKey::Timestamp.as_index(), 3);
175        assert_eq!(TestKey::Id.as_index(), 4);
176        assert_eq!(TestKey::Service.as_index(), 5);
177        assert_eq!(TestKey::Name.as_index(), 6);
178        assert_eq!(TestKey::Version.as_index(), 7);
179        assert_eq!(TestKey::Requests.as_index(), 8);
180        assert_eq!(TestKey::Status.as_index(), 9);
181        assert_eq!(TestKey::Details.as_index(), 10);
182        assert_eq!(TestKey::Tag.as_index(), 11);
183        assert_eq!(TestKey::Count.as_index(), 12);
184        assert_eq!(TestKey::Flag.as_index(), 13);
185    }
186
187    #[test]
188    fn max_keys_correct() {
189        assert_eq!(TestKey::MAX_KEYS, 14);
190        assert_eq!(TestKey::KEYS.len(), 14);
191    }
192
193    #[test]
194    fn duration_path_correct() {
195        assert_eq!(
196            TestKey::DURATION_PATH,
197            &[TestKey::Duration, TestKey::TotalMs]
198        );
199    }
200
201    #[test]
202    fn timestamp_path_correct() {
203        assert_eq!(
204            TestKey::TIMESTAMP_PATH,
205            &[TestKey::Event, TestKey::Timestamp]
206        );
207    }
208
209    #[test]
210    fn id_path_correct() {
211        assert_eq!(TestKey::ID_PATH, &[TestKey::Event, TestKey::Id]);
212    }
213}