Skip to content

Commit

Permalink
Require all Listeners to implement Debug.
Browse files Browse the repository at this point in the history
This allows `dyn Listener` to implement `Debug`, so if we find ourselves
wondering “what listener is this?” the answer is available.
  • Loading branch information
kpreid committed Nov 22, 2023
1 parent 1314375 commit f5dbb3c
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 7 deletions.
8 changes: 8 additions & 0 deletions all-is-cubes-desktop/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ impl FluffListener {
}
}

impl fmt::Debug for FluffListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FluffListener")
.field("alive", &self.alive)
.finish_non_exhaustive()
}
}

impl Listener<Fluff> for FluffListener {
fn receive(&self, fluff: Fluff) {
match self.sender.try_send(AudioCommand::Fluff(fluff)) {
Expand Down
8 changes: 8 additions & 0 deletions all-is-cubes-desktop/src/record/record_main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt;
use std::time::Duration;

use all_is_cubes::character::{self, Character};
Expand Down Expand Up @@ -176,6 +177,13 @@ impl<M: Send> ChannelListener<M> {
Self { sender }
}
}

impl<M> fmt::Debug for ChannelListener<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChannelListener").finish_non_exhaustive()
}
}

impl<M: Send> listen::Listener<M> for ChannelListener<M> {
fn receive(&self, message: M) {
_ = self.sender.send(message);
Expand Down
7 changes: 7 additions & 0 deletions all-is-cubes-ui/src/vui/widgets/toolbar.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use alloc::sync::{Arc, Weak};
use std::error::Error;
use std::fmt;
use std::sync::Mutex;

use all_is_cubes::block::{self, Block, BlockAttributes, Primitive, Resolution, AIR};
Expand Down Expand Up @@ -403,3 +404,9 @@ impl Listener<CueMessage> for CueListener {
self.0.strong_count() > 0
}
}

impl fmt::Debug for CueListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CueListener").finish_non_exhaustive()
}
}
3 changes: 1 addition & 2 deletions all-is-cubes/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,9 +579,8 @@ impl Block {
}

/// Parameters to [`Block::evaluate2()`] to choose which information to compute.
#[allow(missing_debug_implementations)] // TODO: Debug for DynListener
#[allow(clippy::exhaustive_structs)]
#[derive(Clone)]
#[derive(Clone, Debug)]
pub(crate) struct EvalFilter {
/// If true, don't actually evaluate, but return a placeholder value and do listen.
///
Expand Down
11 changes: 10 additions & 1 deletion all-is-cubes/src/listen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ impl<M> fmt::Debug for Notifier<M> {
///
/// Implementors must also implement [`Send`] and [`Sync`] if the `std` feature of
/// `all-is-cubes` is enabled.
pub trait Listener<M>: SendSyncIfStd {
pub trait Listener<M>: fmt::Debug + SendSyncIfStd {
/// Process and store a message.
///
/// Note that, since this method takes `&Self`, a `Listener` must use interior
Expand Down Expand Up @@ -323,4 +323,13 @@ mod tests {
drop(sink);
assert!(!listener.alive());
}

/// Demonstrate that [`DynListener`] implements [`fmt::Debug`].
#[test]
fn erased_debug() {
let sink: Sink<&str> = Sink::new();
let listener: DynListener<&str> = Arc::new(sink.listener());

assert_eq!(format!("{listener:?}"), "SinkListener { alive: true }");
}
}
40 changes: 38 additions & 2 deletions all-is-cubes/src/listen/listeners.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use alloc::vec::Vec;
use core::fmt;
use core::sync::atomic::{AtomicBool, Ordering};

use manyfmt::formats::Unquote;
use manyfmt::Refmt as _;

use crate::listen::{Listen, Listener};
use crate::util::maybe_sync::{RwLock, SendSyncIfStd};

Expand All @@ -22,7 +25,7 @@ impl<M> Listener<M> for NullListener {

/// A [`Listener`] which delivers messages by calling a function on a [`Weak`] reference's
/// referent, and stops when the weak reference breaks.
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct FnListener<F, T> {
function: F,
weak_target: Weak<T>,
Expand All @@ -38,6 +41,17 @@ impl<F, T> FnListener<F, T> {
}
}

impl<F, T> fmt::Debug for FnListener<F, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FnListener")
// function's type name may be the function name
.field("function", &core::any::type_name::<F>().refmt(&Unquote))
// not useful to print weak_target unless we were to upgrade and lock it
.field("alive", &(self.weak_target.strong_count() > 0))
.finish()
}
}

impl<M, F, T> Listener<M> for FnListener<F, T>
where
F: Fn(&T, M) + SendSyncIfStd,
Expand All @@ -63,7 +77,6 @@ pub struct Sink<M> {
}

/// [`Sink::listener()`] implementation.
#[derive(Debug)]
pub struct SinkListener<M> {
weak_messages: Weak<RwLock<VecDeque<M>>>,
}
Expand Down Expand Up @@ -131,6 +144,15 @@ impl<M> Sink<M> {
}
}

impl<M> fmt::Debug for SinkListener<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SinkListener")
// not useful to print weak_messages unless we were to upgrade and lock it
.field("alive", &(self.weak_messages.strong_count() > 0))
.finish()
}
}

impl<M: Send + Sync> Listener<M> for SinkListener<M> {
fn receive(&self, message: M) {
if let Some(cell) = self.weak_messages.upgrade() {
Expand Down Expand Up @@ -242,6 +264,20 @@ mod tests {
assert_eq!(notifier.count(), 0);
}

#[test]
fn fn_debug() {
let listener = FnListener::new(&Arc::new(()), |_recipient: &(), _msg: ()| {});
assert_eq!(
format!("{listener:#?}"),
indoc::indoc! { "
FnListener {
function: all_is_cubes::listen::listeners::tests::fn_debug::{{closure}},
alive: false,
}\
" }
);
}

#[test]
fn sink_alive() {
let notifier: Notifier<()> = Notifier::new();
Expand Down
24 changes: 22 additions & 2 deletions all-is-cubes/src/listen/util.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use alloc::sync::{Arc, Weak};
use core::fmt;

use manyfmt::formats::Unquote;
use manyfmt::Refmt as _;

use crate::listen::{Listener, Notifier};

/// A [`Listener`] which transforms or discards messages before passing them on.
Expand All @@ -9,13 +12,23 @@ use crate::listen::{Listener, Notifier};
/// This may be used to drop uninteresting messages or reduce their granularity.
///
/// TODO: add doc test
#[derive(Debug)]
pub struct Filter<F, T> {
/// The function to transform and possibly discard each message.
pub(super) function: F,
/// The recipient of the messages.
pub(super) target: T,
}

impl<F, T: fmt::Debug> fmt::Debug for Filter<F, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Filter")
// function's type name may be the function name
.field("function", &core::any::type_name::<F>().refmt(&Unquote))
.field("target", &self.target)
.finish()
}
}

impl<MI, MO, F, T> Listener<MI> for Filter<F, T>
where
F: Fn(MI) -> Option<MO> + Send + Sync,
Expand Down Expand Up @@ -81,9 +94,16 @@ where

/// A [`Listener`] which forwards messages through a [`Notifier`] to its listeners.
/// Constructed by [`Notifier::forwarder()`].
#[derive(Debug)]
pub struct NotifierForwarder<M>(pub(super) Weak<Notifier<M>>);

impl<M> fmt::Debug for NotifierForwarder<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NotifierForwarder")
.field("alive(shallow)", &(self.0.strong_count() > 0))
.finish_non_exhaustive()
}
}

impl<M: Clone + Send> Listener<M> for NotifierForwarder<M> {
fn receive(&self, message: M) {
if let Some(notifier) = self.0.upgrade() {
Expand Down

0 comments on commit f5dbb3c

Please sign in to comment.