console_api/
common.rs

1use std::fmt;
2use std::hash::{Hash, Hasher};
3
4pub use generated::*;
5
6mod generated {
7    #![allow(warnings)]
8    include!("generated/rs.tokio.console.common.rs");
9}
10
11impl From<tracing_core::Level> for metadata::Level {
12    fn from(level: tracing_core::Level) -> Self {
13        match level {
14            tracing_core::Level::ERROR => metadata::Level::Error,
15            tracing_core::Level::WARN => metadata::Level::Warn,
16            tracing_core::Level::INFO => metadata::Level::Info,
17            tracing_core::Level::DEBUG => metadata::Level::Debug,
18            tracing_core::Level::TRACE => metadata::Level::Trace,
19        }
20    }
21}
22
23impl From<tracing_core::metadata::Kind> for metadata::Kind {
24    fn from(kind: tracing_core::metadata::Kind) -> Self {
25        // /!\ Note that this is intentionally *not* implemented using match.
26        // The `metadata::Kind` struct in `tracing_core` was written not
27        // intending to allow exhaustive matches, but accidentally did.
28        //
29        // Therefore, we shouldn't be able to write a match against both
30        // variants without a wildcard arm. However, on versions of
31        // `tracing_core` where the type was exhaustively matchable, a wildcard
32        // arm will result in a warning. Thus we must write this rather
33        // tortured-looking `if` statement to get non-exhaustive matching
34        // behavior.
35        if kind == tracing_core::metadata::Kind::SPAN {
36            metadata::Kind::Span
37        } else {
38            metadata::Kind::Event
39        }
40    }
41}
42
43impl<'a> From<&'a tracing_core::Metadata<'a>> for Metadata {
44    fn from(meta: &'a tracing_core::Metadata<'a>) -> Self {
45        let kind = if meta.is_span() {
46            metadata::Kind::Span
47        } else {
48            debug_assert!(meta.is_event());
49            metadata::Kind::Event
50        };
51
52        let field_names = meta.fields().iter().map(|f| f.name().to_string()).collect();
53        Metadata {
54            name: meta.name().to_string(),
55            target: meta.target().to_string(),
56            location: Some(meta.into()),
57            kind: kind as i32,
58            level: metadata::Level::from(*meta.level()) as i32,
59            field_names,
60            ..Default::default()
61        }
62    }
63}
64
65impl<'a> From<&'a tracing_core::Metadata<'a>> for Location {
66    fn from(meta: &'a tracing_core::Metadata<'a>) -> Self {
67        Location {
68            file: meta.file().map(String::from),
69            module_path: meta.module_path().map(String::from),
70            line: meta.line(),
71            column: None, // tracing doesn't support columns yet
72        }
73    }
74}
75
76impl<'a> From<&'a std::panic::Location<'a>> for Location {
77    fn from(loc: &'a std::panic::Location<'a>) -> Self {
78        Location {
79            file: Some(loc.file().to_string()),
80            line: Some(loc.line()),
81            column: Some(loc.column()),
82            ..Default::default()
83        }
84    }
85}
86
87impl fmt::Display for field::Value {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        match self {
90            field::Value::BoolVal(v) => fmt::Display::fmt(v, f)?,
91            field::Value::StrVal(v) => fmt::Display::fmt(v, f)?,
92            field::Value::U64Val(v) => fmt::Display::fmt(v, f)?,
93            field::Value::DebugVal(v) => fmt::Display::fmt(v, f)?,
94            field::Value::I64Val(v) => fmt::Display::fmt(v, f)?,
95        }
96
97        Ok(())
98    }
99}
100
101impl fmt::Display for Field {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        let name_val = (self.name.as_ref(), self.value.as_ref());
104        if let (Some(field::Name::StrName(name)), Some(val)) = name_val {
105            write!(f, "{}={}", name, val)?;
106        }
107
108        Ok(())
109    }
110}
111
112impl fmt::Display for Location {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        match (self.module_path.as_ref(), self.file.as_ref()) {
115            // Module paths take precedence because they're shorter...
116            (Some(module), _) => f.write_str(module.as_ref())?,
117            (None, Some(file)) => f.write_str(file.as_ref())?,
118            // If there's no file or module path, then printing the line and
119            // column makes no sense...
120            (None, None) => return f.write_str("<unknown location>"),
121        };
122
123        if let Some(line) = self.line {
124            write!(f, ":{}", line)?;
125
126            // Printing the column only makes sense if there's a line...
127            if let Some(column) = self.column {
128                write!(f, ":{}", column)?;
129            }
130        }
131
132        Ok(())
133    }
134}
135
136// === IDs ===
137
138impl From<&'static tracing_core::Metadata<'static>> for MetaId {
139    fn from(meta: &'static tracing_core::Metadata) -> Self {
140        MetaId {
141            id: meta as *const _ as u64,
142        }
143    }
144}
145
146impl From<tracing_core::span::Id> for SpanId {
147    fn from(id: tracing_core::span::Id) -> Self {
148        SpanId { id: id.into_u64() }
149    }
150}
151
152impl From<SpanId> for tracing_core::span::Id {
153    fn from(span_id: SpanId) -> Self {
154        tracing_core::span::Id::from_u64(span_id.id)
155    }
156}
157
158impl From<u64> for SpanId {
159    fn from(id: u64) -> Self {
160        SpanId { id }
161    }
162}
163
164impl From<&'static tracing_core::Metadata<'static>> for register_metadata::NewMetadata {
165    fn from(meta: &'static tracing_core::Metadata) -> Self {
166        register_metadata::NewMetadata {
167            id: Some(meta.into()),
168            metadata: Some(meta.into()),
169        }
170    }
171}
172
173impl From<i64> for field::Value {
174    fn from(val: i64) -> Self {
175        field::Value::I64Val(val)
176    }
177}
178
179impl From<u64> for field::Value {
180    fn from(val: u64) -> Self {
181        field::Value::U64Val(val)
182    }
183}
184
185impl From<bool> for field::Value {
186    fn from(val: bool) -> Self {
187        field::Value::BoolVal(val)
188    }
189}
190
191impl From<&str> for field::Value {
192    fn from(val: &str) -> Self {
193        field::Value::StrVal(val.into())
194    }
195}
196
197impl From<&str> for field::Name {
198    fn from(val: &str) -> Self {
199        field::Name::StrName(val.into())
200    }
201}
202
203impl From<&dyn std::fmt::Debug> for field::Value {
204    fn from(val: &dyn std::fmt::Debug) -> Self {
205        field::Value::DebugVal(format!("{:?}", val))
206    }
207}
208
209// Clippy warns when a type derives `PartialEq` but has a manual `Hash` impl,
210// or vice versa. However, this is unavoidable here, because `prost` generates
211// a struct with `#[derive(PartialEq)]`, but we cannot add`#[derive(Hash)]` to the
212// generated code.
213#[allow(clippy::derived_hash_with_manual_eq)]
214impl Hash for field::Name {
215    fn hash<H: Hasher>(&self, state: &mut H) {
216        match self {
217            field::Name::NameIdx(idx) => idx.hash(state),
218            field::Name::StrName(s) => s.hash(state),
219        }
220    }
221}
222
223impl Eq for field::Name {}
224
225// === IDs ===
226
227impl From<u64> for Id {
228    fn from(id: u64) -> Self {
229        Id { id }
230    }
231}
232
233impl From<Id> for u64 {
234    fn from(id: Id) -> Self {
235        id.id
236    }
237}
238
239impl From<tracing_core::span::Id> for Id {
240    fn from(id: tracing_core::span::Id) -> Self {
241        Id { id: id.into_u64() }
242    }
243}