feat: `tracing` support for component props (#1531)

This commit is contained in:
luoxiaozero 2023-08-18 20:29:41 +08:00 committed by GitHub
parent d9abebb4be
commit 18deb398ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 202 additions and 20 deletions

View File

@ -31,6 +31,7 @@ smallvec = "1"
tracing = "0.1"
wasm-bindgen = { version = "0.2", features = ["enable-interning"] }
wasm-bindgen-futures = "0.4.31"
serde = "1"
[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2", features = ["js"] }

View File

@ -2,6 +2,8 @@ mod into_attribute;
mod into_class;
mod into_property;
mod into_style;
#[doc(hidden)]
pub mod tracing_property;
pub use into_attribute::*;
pub use into_class::*;
pub use into_property::*;

View File

@ -0,0 +1,166 @@
use wasm_bindgen::UnwrapThrowExt;
#[macro_export]
/// Use for tracing property
macro_rules! tracing_props {
() => {
::leptos::leptos_dom::tracing::span!(
::leptos::leptos_dom::tracing::Level::INFO,
"leptos_dom::tracing_props",
props = String::from("[]")
);
};
($($prop:tt),+ $(,)?) => {
{
use ::leptos::leptos_dom::tracing_property::{Match, SerializeMatch, DefaultMatch};
let mut props = String::from('[');
$(
let prop = (&&Match {
name: stringify!{$prop},
value: std::cell::Cell::new(Some(&$prop))
}).spez();
props.push_str(&format!("{prop},"));
)*
props.pop();
props.push(']');
::leptos::leptos_dom::tracing::span!(
::leptos::leptos_dom::tracing::Level::INFO,
"leptos_dom::tracing_props",
props
);
}
};
}
// Implementation based on spez
// see https://github.com/m-ou-se/spez
pub struct Match<T> {
pub name: &'static str,
pub value: std::cell::Cell<Option<T>>,
}
pub trait SerializeMatch {
type Return;
fn spez(&self) -> Self::Return;
}
impl<T: serde::Serialize> SerializeMatch for &Match<&T> {
type Return = String;
fn spez(&self) -> Self::Return {
let name = self.name;
serde_json::to_string(self.value.get().unwrap_throw()).map_or_else(
|err| format!(r#"{{"name": "{name}", "error": "{err}"}}"#),
|value| format!(r#"{{"name": "{name}", "value": {value}}}"#),
)
}
}
pub trait DefaultMatch {
type Return;
fn spez(&self) -> Self::Return;
}
impl<T> DefaultMatch for Match<&T> {
type Return = String;
fn spez(&self) -> Self::Return {
let name = self.name;
format!(
r#"{{"name": "{name}", "error": "The trait `serde::Serialize` is not implemented"}}"#
)
}
}
#[test]
fn match_primitive() {
// String
let test = String::from("string");
let prop = (&&Match {
name: stringify! {test},
value: std::cell::Cell::new(Some(&test)),
})
.spez();
assert_eq!(prop, r#"{"name": "test", "value": "string"}"#);
// &str
let test = "string";
let prop = (&&Match {
name: stringify! {test},
value: std::cell::Cell::new(Some(&test)),
})
.spez();
assert_eq!(prop, r#"{"name": "test", "value": "string"}"#);
// u128
let test: u128 = 1;
let prop = (&&Match {
name: stringify! {test},
value: std::cell::Cell::new(Some(&test)),
})
.spez();
assert_eq!(prop, r#"{"name": "test", "value": 1}"#);
// i128
let test: i128 = -1;
let prop = (&&Match {
name: stringify! {test},
value: std::cell::Cell::new(Some(&test)),
})
.spez();
assert_eq!(prop, r#"{"name": "test", "value": -1}"#);
// f64
let test = 3.14;
let prop = (&&Match {
name: stringify! {test},
value: std::cell::Cell::new(Some(&test)),
})
.spez();
assert_eq!(prop, r#"{"name": "test", "value": 3.14}"#);
// bool
let test = true;
let prop = (&&Match {
name: stringify! {test},
value: std::cell::Cell::new(Some(&test)),
})
.spez();
assert_eq!(prop, r#"{"name": "test", "value": true}"#);
}
#[test]
fn match_serialize() {
use serde::Serialize;
#[derive(Serialize)]
struct CustomStruct {
field: &'static str,
}
let test = CustomStruct { field: "field" };
let prop = (&&Match {
name: stringify! {test},
value: std::cell::Cell::new(Some(&test)),
})
.spez();
assert_eq!(prop, r#"{"name": "test", "value": {"field":"field"}}"#);
// Verification of ownership
assert_eq!(test.field, "field");
}
#[test]
fn match_no_serialize() {
struct CustomStruct {
field: &'static str,
}
let test = CustomStruct { field: "field" };
let prop = (&&Match {
name: stringify! {test},
value: std::cell::Cell::new(Some(&test)),
})
.spez();
assert_eq!(
prop,
r#"{"name": "test", "error": "The trait `serde::Serialize` is not implemented"}"#
);
// Verification of ownership
assert_eq!(test.field, "field");
}

View File

@ -178,8 +178,12 @@ impl ToTokens for Model {
let component_fn_prop_docs = generate_component_fn_prop_docs(props);
let (tracing_instrument_attr, tracing_span_expr, tracing_guard_expr) =
if cfg!(feature = "tracing") {
let (
tracing_instrument_attr,
tracing_span_expr,
tracing_guard_expr,
tracing_props_expr,
) = if cfg!(feature = "tracing") {
(
quote! {
#[allow(clippy::let_with_type_underscore)]
@ -195,9 +199,16 @@ impl ToTokens for Model {
#[cfg(debug_assertions)]
let _guard = span.entered();
},
if no_props {
quote! {}
} else {
quote! {
::leptos::leptos_dom::tracing_props![#prop_names];
}
},
)
} else {
(quote! {}, quote! {}, quote! {})
(quote! {}, quote! {}, quote! {}, quote! {})
};
let component = if *is_transparent {
@ -211,6 +222,8 @@ impl ToTokens for Model {
move |cx| {
#tracing_guard_expr
#tracing_props_expr
#body_name(cx, #prop_names)
}
)