forked from tokio-rs/tracing
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: reverse the polarity of conversions, fix clippy (tokio-rs#1335)
This backports PR tokio-rs#1335 from `master` to `v0.1.x`. Clippy now warns about implementing `Into` rather than `From`, since `From` automatically provides `Into` but `Into` does not provide `From`. This commit fixes the direction of those conversions, placating Clippy. Additionally, it fixes a redundant use of slice syntax that Clippy also complained about. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
- Loading branch information
Showing
2 changed files
with
393 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
use tracing::{ | ||
callsite, | ||
callsite::Callsite, | ||
field::{self, Field, Value, Visit}, | ||
metadata::Kind, | ||
}; | ||
|
||
use std::{collections::HashMap, fmt}; | ||
|
||
#[derive(Default, Debug, Eq, PartialEq)] | ||
pub struct Expect { | ||
fields: HashMap<String, MockValue>, | ||
only: bool, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct MockField { | ||
name: String, | ||
value: MockValue, | ||
} | ||
|
||
#[derive(Debug, Eq, PartialEq)] | ||
pub enum MockValue { | ||
I64(i64), | ||
U64(u64), | ||
Bool(bool), | ||
Str(String), | ||
Debug(String), | ||
Any, | ||
} | ||
|
||
pub fn mock<K>(name: K) -> MockField | ||
where | ||
String: From<K>, | ||
{ | ||
MockField { | ||
name: name.into(), | ||
value: MockValue::Any, | ||
} | ||
} | ||
|
||
impl MockField { | ||
/// Expect a field with the given name and value. | ||
pub fn with_value(self, value: &dyn Value) -> Self { | ||
Self { | ||
value: MockValue::from(value), | ||
..self | ||
} | ||
} | ||
|
||
pub fn and(self, other: MockField) -> Expect { | ||
Expect { | ||
fields: HashMap::new(), | ||
only: false, | ||
} | ||
.and(self) | ||
.and(other) | ||
} | ||
|
||
pub fn only(self) -> Expect { | ||
Expect { | ||
fields: HashMap::new(), | ||
only: true, | ||
} | ||
.and(self) | ||
} | ||
} | ||
|
||
impl From<MockField> for Expect { | ||
fn from(field: MockField) -> Self { | ||
Expect { | ||
fields: HashMap::new(), | ||
only: false, | ||
} | ||
.and(field) | ||
} | ||
} | ||
|
||
impl Expect { | ||
pub fn and(mut self, field: MockField) -> Self { | ||
self.fields.insert(field.name, field.value); | ||
self | ||
} | ||
|
||
/// Indicates that no fields other than those specified should be expected. | ||
pub fn only(self) -> Self { | ||
Self { only: true, ..self } | ||
} | ||
|
||
fn compare_or_panic(&mut self, name: &str, value: &dyn Value, ctx: &str) { | ||
let value = value.into(); | ||
match self.fields.remove(name) { | ||
Some(MockValue::Any) => {} | ||
Some(expected) => assert!( | ||
expected == value, | ||
"\nexpected `{}` to contain:\n\t`{}{}`\nbut got:\n\t`{}{}`", | ||
ctx, | ||
name, | ||
expected, | ||
name, | ||
value | ||
), | ||
None if self.only => panic!( | ||
"\nexpected `{}` to contain only:\n\t`{}`\nbut got:\n\t`{}{}`", | ||
ctx, self, name, value | ||
), | ||
_ => {} | ||
} | ||
} | ||
|
||
pub fn checker(&mut self, ctx: String) -> CheckVisitor<'_> { | ||
CheckVisitor { expect: self, ctx } | ||
} | ||
|
||
pub fn is_empty(&self) -> bool { | ||
self.fields.is_empty() | ||
} | ||
} | ||
|
||
impl fmt::Display for MockValue { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
MockValue::I64(v) => write!(f, "i64 = {:?}", v), | ||
MockValue::U64(v) => write!(f, "u64 = {:?}", v), | ||
MockValue::Bool(v) => write!(f, "bool = {:?}", v), | ||
MockValue::Str(v) => write!(f, "&str = {:?}", v), | ||
MockValue::Debug(v) => write!(f, "&fmt::Debug = {:?}", v), | ||
MockValue::Any => write!(f, "_ = _"), | ||
} | ||
} | ||
} | ||
|
||
pub struct CheckVisitor<'a> { | ||
expect: &'a mut Expect, | ||
ctx: String, | ||
} | ||
|
||
impl<'a> Visit for CheckVisitor<'a> { | ||
fn record_i64(&mut self, field: &Field, value: i64) { | ||
self.expect | ||
.compare_or_panic(field.name(), &value, &self.ctx[..]) | ||
} | ||
|
||
fn record_u64(&mut self, field: &Field, value: u64) { | ||
self.expect | ||
.compare_or_panic(field.name(), &value, &self.ctx[..]) | ||
} | ||
|
||
fn record_bool(&mut self, field: &Field, value: bool) { | ||
self.expect | ||
.compare_or_panic(field.name(), &value, &self.ctx[..]) | ||
} | ||
|
||
fn record_str(&mut self, field: &Field, value: &str) { | ||
self.expect | ||
.compare_or_panic(field.name(), &value, &self.ctx[..]) | ||
} | ||
|
||
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { | ||
self.expect | ||
.compare_or_panic(field.name(), &field::debug(value), &self.ctx) | ||
} | ||
} | ||
|
||
impl<'a> CheckVisitor<'a> { | ||
pub fn finish(self) { | ||
assert!( | ||
self.expect.fields.is_empty(), | ||
"{}missing {}", | ||
self.expect, | ||
self.ctx | ||
); | ||
} | ||
} | ||
|
||
impl<'a> From<&'a dyn Value> for MockValue { | ||
fn from(value: &'a dyn Value) -> Self { | ||
struct MockValueBuilder { | ||
value: Option<MockValue>, | ||
} | ||
|
||
impl Visit for MockValueBuilder { | ||
fn record_i64(&mut self, _: &Field, value: i64) { | ||
self.value = Some(MockValue::I64(value)); | ||
} | ||
|
||
fn record_u64(&mut self, _: &Field, value: u64) { | ||
self.value = Some(MockValue::U64(value)); | ||
} | ||
|
||
fn record_bool(&mut self, _: &Field, value: bool) { | ||
self.value = Some(MockValue::Bool(value)); | ||
} | ||
|
||
fn record_str(&mut self, _: &Field, value: &str) { | ||
self.value = Some(MockValue::Str(value.to_owned())); | ||
} | ||
|
||
fn record_debug(&mut self, _: &Field, value: &dyn fmt::Debug) { | ||
self.value = Some(MockValue::Debug(format!("{:?}", value))); | ||
} | ||
} | ||
|
||
let fake_field = callsite!(name: "fake", kind: Kind::EVENT, fields: fake_field) | ||
.metadata() | ||
.fields() | ||
.field("fake_field") | ||
.unwrap(); | ||
let mut builder = MockValueBuilder { value: None }; | ||
value.record(&fake_field, &mut builder); | ||
builder | ||
.value | ||
.expect("finish called before a value was recorded") | ||
} | ||
} | ||
|
||
impl fmt::Display for Expect { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "fields ")?; | ||
let entries = self | ||
.fields | ||
.iter() | ||
.map(|(k, v)| (field::display(k), field::display(v))); | ||
f.debug_map().entries(entries).finish() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
#![allow(missing_docs)] | ||
use super::{field, metadata, Parent}; | ||
use std::fmt; | ||
|
||
/// A mock span. | ||
/// | ||
/// This is intended for use with the mock subscriber API in the | ||
/// `subscriber` module. | ||
#[derive(Clone, Debug, Default, Eq, PartialEq)] | ||
pub struct MockSpan { | ||
pub(in crate::support) metadata: metadata::Expect, | ||
} | ||
|
||
#[derive(Debug, Default, Eq, PartialEq)] | ||
pub struct NewSpan { | ||
pub(in crate::support) span: MockSpan, | ||
pub(in crate::support) fields: field::Expect, | ||
pub(in crate::support) parent: Option<Parent>, | ||
} | ||
|
||
pub fn mock() -> MockSpan { | ||
MockSpan { | ||
..Default::default() | ||
} | ||
} | ||
|
||
impl MockSpan { | ||
pub fn named<I>(self, name: I) -> Self | ||
where | ||
I: Into<String>, | ||
{ | ||
Self { | ||
metadata: metadata::Expect { | ||
name: Some(name.into()), | ||
..self.metadata | ||
}, | ||
} | ||
} | ||
|
||
pub fn at_level(self, level: tracing::Level) -> Self { | ||
Self { | ||
metadata: metadata::Expect { | ||
level: Some(level), | ||
..self.metadata | ||
}, | ||
} | ||
} | ||
|
||
pub fn with_target<I>(self, target: I) -> Self | ||
where | ||
I: Into<String>, | ||
{ | ||
Self { | ||
metadata: metadata::Expect { | ||
target: Some(target.into()), | ||
..self.metadata | ||
}, | ||
} | ||
} | ||
|
||
pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan { | ||
let parent = match parent { | ||
Some(name) => Parent::Explicit(name.into()), | ||
None => Parent::ExplicitRoot, | ||
}; | ||
NewSpan { | ||
parent: Some(parent), | ||
span: self, | ||
..Default::default() | ||
} | ||
} | ||
|
||
pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan { | ||
let parent = match parent { | ||
Some(name) => Parent::Contextual(name.into()), | ||
None => Parent::ContextualRoot, | ||
}; | ||
NewSpan { | ||
parent: Some(parent), | ||
span: self, | ||
..Default::default() | ||
} | ||
} | ||
|
||
pub fn name(&self) -> Option<&str> { | ||
self.metadata.name.as_ref().map(String::as_ref) | ||
} | ||
|
||
pub fn with_field<I>(self, fields: I) -> NewSpan | ||
where | ||
I: Into<field::Expect>, | ||
{ | ||
NewSpan { | ||
span: self, | ||
fields: fields.into(), | ||
..Default::default() | ||
} | ||
} | ||
|
||
pub(in crate::support) fn check_metadata(&self, actual: &tracing::Metadata<'_>) { | ||
self.metadata.check(actual, format_args!("span {}", self)); | ||
assert!(actual.is_span(), "expected a span but got {:?}", actual); | ||
} | ||
} | ||
|
||
impl fmt::Display for MockSpan { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
if self.metadata.name.is_some() { | ||
write!(f, "a span{}", self.metadata) | ||
} else { | ||
write!(f, "any span{}", self.metadata) | ||
} | ||
} | ||
} | ||
|
||
impl From<MockSpan> for NewSpan { | ||
fn from(span: MockSpan) -> Self { | ||
Self { | ||
span, | ||
..Default::default() | ||
} | ||
} | ||
} | ||
|
||
impl NewSpan { | ||
pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan { | ||
let parent = match parent { | ||
Some(name) => Parent::Explicit(name.into()), | ||
None => Parent::ExplicitRoot, | ||
}; | ||
NewSpan { | ||
parent: Some(parent), | ||
..self | ||
} | ||
} | ||
|
||
pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan { | ||
let parent = match parent { | ||
Some(name) => Parent::Contextual(name.into()), | ||
None => Parent::ContextualRoot, | ||
}; | ||
NewSpan { | ||
parent: Some(parent), | ||
..self | ||
} | ||
} | ||
|
||
pub fn with_field<I>(self, fields: I) -> NewSpan | ||
where | ||
I: Into<field::Expect>, | ||
{ | ||
NewSpan { | ||
fields: fields.into(), | ||
..self | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for NewSpan { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "a new span{}", self.span.metadata)?; | ||
if !self.fields.is_empty() { | ||
write!(f, " with {}", self.fields)?; | ||
} | ||
Ok(()) | ||
} | ||
} |