Skip to content

Commit 6af5d40

Browse files
rcohjdisanti
andauthored
Add useful Debug impl to PropertyBag and ConfigBag (#2612)
## Motivation and Context - it's hard to debug what properties are set, especially for layered configuration ## Description Add Debug Info for PropertyBag and Config bag: ``` PropertyBag { contents: ["aws_smithy_http::property_bag::test::test_extensions::MyType"] } ``` ``` ConfigBag { layers: [ Layer { name: "c", properties: [ "aws_smithy_runtime_api::config_bag::Value<aws_smithy_runtime_api::config_bag::test::layered_property_bag::Prop3>", "aws_smithy_runtime_api::config_bag::Value<aws_smithy_runtime_api::config_bag::test::layered_property_bag::Prop4>", ], }, Layer { name: "b", properties: [ "aws_smithy_runtime_api::config_bag::Value<aws_smithy_runtime_api::config_bag::test::layered_property_bag::Prop3>", "aws_smithy_runtime_api::config_bag::Value<aws_smithy_runtime_api::config_bag::test::layered_property_bag::Prop2>", ], }, Layer { name: "a", properties: [ "aws_smithy_runtime_api::config_bag::Value<aws_smithy_runtime_api::config_bag::test::layered_property_bag::Prop1>", ], }, Layer { name: "base", properties: [], }, ], } ``` There is still some work to do, but this is a start ## Testing - [x] UT ## Checklist <!--- If a checkbox below is not applicable, then please DELETE it rather than leaving it unchecked --> - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [x] I have updated `CHANGELOG.next.toml` if I made changes to the AWS SDK, generated SDK code, or SDK runtime crates ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti <jdisanti@amazon.com>
1 parent cfd2245 commit 6af5d40

File tree

3 files changed

+119
-30
lines changed

3 files changed

+119
-30
lines changed

CHANGELOG.next.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,10 @@
1010
# references = ["smithy-rs#920"]
1111
# meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"}
1212
# author = "rcoh"
13+
14+
[[smithy-rs]]
15+
message = "The `Debug` implementation for `PropertyBag` now prints a list of the types it contains. This significantly improves debuggability."
16+
author = "rcoh"
17+
references = ["smithy-rs#2612"]
18+
meta = { "breaking" = false, "tada" = false, "bug" = false }
19+

rust-runtime/aws-smithy-http/src/property_bag.rs

Lines changed: 74 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,38 @@
1313
use std::any::{Any, TypeId};
1414
use std::collections::HashMap;
1515
use std::fmt;
16-
use std::fmt::Debug;
16+
use std::fmt::{Debug, Formatter};
1717
use std::hash::{BuildHasherDefault, Hasher};
1818
use std::ops::{Deref, DerefMut};
1919
use std::sync::{Arc, Mutex};
2020

21-
type AnyMap = HashMap<TypeId, Box<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>;
21+
type AnyMap = HashMap<TypeId, NamedType, BuildHasherDefault<IdHasher>>;
22+
23+
struct NamedType {
24+
name: &'static str,
25+
value: Box<dyn Any + Send + Sync>,
26+
}
27+
28+
impl NamedType {
29+
fn as_mut<T: 'static>(&mut self) -> Option<&mut T> {
30+
self.value.downcast_mut()
31+
}
32+
33+
fn as_ref<T: 'static>(&self) -> Option<&T> {
34+
self.value.downcast_ref()
35+
}
36+
37+
fn assume<T: 'static>(self) -> Option<T> {
38+
self.value.downcast().map(|t| *t).ok()
39+
}
40+
41+
fn new<T: Any + Send + Sync>(value: T) -> Self {
42+
Self {
43+
name: std::any::type_name::<T>(),
44+
value: Box::new(value),
45+
}
46+
}
47+
}
2248

2349
// With TypeIds as keys, there's no need to hash them. They are already hashes
2450
// themselves, coming from the compiler. The IdHasher just holds the u64 of
@@ -82,13 +108,8 @@ impl PropertyBag {
82108
/// ```
83109
pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
84110
self.map
85-
.insert(TypeId::of::<T>(), Box::new(val))
86-
.and_then(|boxed| {
87-
(boxed as Box<dyn Any + 'static>)
88-
.downcast()
89-
.ok()
90-
.map(|boxed| *boxed)
91-
})
111+
.insert(TypeId::of::<T>(), NamedType::new(val))
112+
.and_then(|val| val.assume())
92113
}
93114

94115
/// Get a reference to a type previously inserted on this `PropertyBag`.
@@ -106,7 +127,16 @@ impl PropertyBag {
106127
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
107128
self.map
108129
.get(&TypeId::of::<T>())
109-
.and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref())
130+
.and_then(|val| val.as_ref())
131+
}
132+
133+
/// Returns an iterator of the types contained in this PropertyBag
134+
///
135+
/// # Stability
136+
/// This method is unstable and may be removed or changed in a future release. The exact
137+
/// format of the returned types may also change.
138+
pub fn contents(&self) -> impl Iterator<Item = &'static str> + '_ {
139+
self.map.values().map(|tpe| tpe.name)
110140
}
111141

112142
/// Get a mutable reference to a type previously inserted on this `PropertyBag`.
@@ -124,7 +154,7 @@ impl PropertyBag {
124154
pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
125155
self.map
126156
.get_mut(&TypeId::of::<T>())
127-
.and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut())
157+
.map(|val| val.as_mut().expect("type mismatch!"))
128158
}
129159

130160
/// Remove a type from this `PropertyBag`.
@@ -141,8 +171,8 @@ impl PropertyBag {
141171
/// assert!(props.get::<i32>().is_none());
142172
/// ```
143173
pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
144-
self.map.remove(&TypeId::of::<T>()).and_then(|boxed| {
145-
(boxed as Box<dyn Any + 'static>)
174+
self.map.remove(&TypeId::of::<T>()).and_then(|tpe| {
175+
(tpe.value as Box<dyn Any + 'static>)
146176
.downcast()
147177
.ok()
148178
.map(|boxed| *boxed)
@@ -168,7 +198,16 @@ impl PropertyBag {
168198

169199
impl fmt::Debug for PropertyBag {
170200
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171-
f.debug_struct("PropertyBag").finish()
201+
let mut fmt = f.debug_struct("PropertyBag");
202+
203+
struct Contents<'a>(&'a PropertyBag);
204+
impl<'a> Debug for Contents<'a> {
205+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
206+
f.debug_list().entries(self.0.contents()).finish()
207+
}
208+
}
209+
fmt.field("contents", &Contents(self));
210+
fmt.finish()
172211
}
173212
}
174213

@@ -225,22 +264,30 @@ impl From<PropertyBag> for SharedPropertyBag {
225264
}
226265

227266
#[cfg(test)]
228-
#[test]
229-
fn test_extensions() {
230-
#[derive(Debug, PartialEq)]
231-
struct MyType(i32);
267+
mod test {
268+
use crate::property_bag::PropertyBag;
232269

233-
let mut extensions = PropertyBag::new();
270+
#[test]
271+
fn test_extensions() {
272+
#[derive(Debug, PartialEq)]
273+
struct MyType(i32);
234274

235-
extensions.insert(5i32);
236-
extensions.insert(MyType(10));
275+
let mut property_bag = PropertyBag::new();
237276

238-
assert_eq!(extensions.get(), Some(&5i32));
239-
assert_eq!(extensions.get_mut(), Some(&mut 5i32));
277+
property_bag.insert(5i32);
278+
property_bag.insert(MyType(10));
240279

241-
assert_eq!(extensions.remove::<i32>(), Some(5i32));
242-
assert!(extensions.get::<i32>().is_none());
280+
assert_eq!(property_bag.get(), Some(&5i32));
281+
assert_eq!(property_bag.get_mut(), Some(&mut 5i32));
243282

244-
assert_eq!(extensions.get::<bool>(), None);
245-
assert_eq!(extensions.get(), Some(&MyType(10)));
283+
assert_eq!(property_bag.remove::<i32>(), Some(5i32));
284+
assert!(property_bag.get::<i32>().is_none());
285+
286+
assert_eq!(property_bag.get::<bool>(), None);
287+
assert_eq!(property_bag.get(), Some(&MyType(10)));
288+
assert_eq!(
289+
format!("{:?}", property_bag),
290+
r#"PropertyBag { contents: ["aws_smithy_http::property_bag::test::test_extensions::MyType"] }"#
291+
);
292+
}
246293
}

rust-runtime/aws-smithy-runtime-api/src/config_bag.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,40 @@
1010
//! 1. A new layer of configuration may be applied onto an existing configuration structure without modifying it or taking ownership.
1111
//! 2. No lifetime shenanigans to deal with
1212
use aws_smithy_http::property_bag::PropertyBag;
13-
use std::fmt::Debug;
13+
use std::fmt::{Debug, Formatter};
1414
use std::ops::Deref;
1515
use std::sync::Arc;
1616

1717
/// Layered Configuration Structure
1818
///
1919
/// [`ConfigBag`] is the "unlocked" form of the bag. Only the top layer of the bag may be unlocked.
2020
#[must_use]
21-
#[derive(Debug)]
2221
pub struct ConfigBag {
2322
head: Layer,
2423
tail: Option<FrozenConfigBag>,
2524
}
2625

26+
impl Debug for ConfigBag {
27+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
28+
struct Layers<'a>(&'a ConfigBag);
29+
impl Debug for Layers<'_> {
30+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
31+
let mut list = f.debug_list();
32+
list.entry(&self.0.head);
33+
let mut us = self.0.tail.as_ref();
34+
while let Some(bag) = us {
35+
list.entry(&bag.head);
36+
us = bag.tail.as_ref()
37+
}
38+
list.finish()
39+
}
40+
}
41+
f.debug_struct("ConfigBag")
42+
.field("layers", &Layers(self))
43+
.finish()
44+
}
45+
}
46+
2747
/// Layered Configuration Structure
2848
///
2949
/// [`FrozenConfigBag`] is the "locked" form of the bag.
@@ -55,12 +75,26 @@ enum Value<T> {
5575
ExplicitlyUnset,
5676
}
5777

58-
#[derive(Debug)]
5978
struct Layer {
6079
name: &'static str,
6180
props: PropertyBag,
6281
}
6382

83+
impl Debug for Layer {
84+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
85+
struct Contents<'a>(&'a Layer);
86+
impl Debug for Contents<'_> {
87+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88+
f.debug_list().entries(self.0.props.contents()).finish()
89+
}
90+
}
91+
f.debug_struct("Layer")
92+
.field("name", &self.name)
93+
.field("properties", &Contents(self))
94+
.finish()
95+
}
96+
}
97+
6498
fn no_op(_: &mut ConfigBag) {}
6599

66100
impl FrozenConfigBag {
@@ -258,6 +292,7 @@ mod test {
258292
assert!(final_bag.get::<Prop2>().is_some());
259293
// we unset prop3
260294
assert!(final_bag.get::<Prop3>().is_none());
295+
println!("{:#?}", final_bag);
261296
}
262297

263298
#[test]

0 commit comments

Comments
 (0)