rclrs/subscription/
callback.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use rosidl_runtime_rs::Message;

use super::MessageInfo;
use crate::ReadOnlyLoanedMessage;

/// A trait for allowed callbacks for subscriptions.
///
/// See [`AnySubscriptionCallback`] for a list of possible callback signatures.
pub trait SubscriptionCallback<T, Args>: Send + 'static
where
    T: Message,
{
    /// Converts the callback into an enum.
    ///
    /// User code never needs to call this function.
    fn into_callback(self) -> AnySubscriptionCallback<T>;
}

/// An enum capturing the various possible function signatures for subscription callbacks.
///
/// The correct enum variant is deduced by the [`SubscriptionCallback`] trait.
pub enum AnySubscriptionCallback<T>
where
    T: Message,
{
    /// A callback with only the message as an argument.
    Regular(Box<dyn FnMut(T) + Send>),
    /// A callback with the message and the message info as arguments.
    RegularWithMessageInfo(Box<dyn FnMut(T, MessageInfo) + Send>),
    /// A callback with only the boxed message as an argument.
    Boxed(Box<dyn FnMut(Box<T>) + Send>),
    /// A callback with the boxed message and the message info as arguments.
    BoxedWithMessageInfo(Box<dyn FnMut(Box<T>, MessageInfo) + Send>),
    /// A callback with only the loaned message as an argument.
    #[allow(clippy::type_complexity)]
    Loaned(Box<dyn for<'a> FnMut(ReadOnlyLoanedMessage<'a, T>) + Send>),
    /// A callback with the loaned message and the message info as arguments.
    #[allow(clippy::type_complexity)]
    LoanedWithMessageInfo(Box<dyn for<'a> FnMut(ReadOnlyLoanedMessage<'a, T>, MessageInfo) + Send>),
}

// We need one implementation per arity. This was inspired by Bevy's systems.
impl<T, A0, Func> SubscriptionCallback<T, (A0,)> for Func
where
    Func: FnMut(A0) + Send + 'static,
    (A0,): ArgTuple<T, Func>,
    T: Message,
{
    fn into_callback(self) -> AnySubscriptionCallback<T> {
        <(A0,) as ArgTuple<T, Func>>::into_callback_with_args(self)
    }
}

impl<T, A0, A1, Func> SubscriptionCallback<T, (A0, A1)> for Func
where
    Func: FnMut(A0, A1) + Send + 'static,
    (A0, A1): ArgTuple<T, Func>,
    T: Message,
{
    fn into_callback(self) -> AnySubscriptionCallback<T> {
        <(A0, A1) as ArgTuple<T, Func>>::into_callback_with_args(self)
    }
}

// Helper trait for SubscriptionCallback.
//
// For each tuple of args, it provides conversion from a function with
// these args to the correct enum variant.
trait ArgTuple<T, Func>
where
    T: Message,
{
    fn into_callback_with_args(func: Func) -> AnySubscriptionCallback<T>;
}

impl<T, Func> ArgTuple<T, Func> for (T,)
where
    T: Message,
    Func: FnMut(T) + Send + 'static,
{
    fn into_callback_with_args(func: Func) -> AnySubscriptionCallback<T> {
        AnySubscriptionCallback::Regular(Box::new(func))
    }
}

impl<T, Func> ArgTuple<T, Func> for (T, MessageInfo)
where
    T: Message,
    Func: FnMut(T, MessageInfo) + Send + 'static,
{
    fn into_callback_with_args(func: Func) -> AnySubscriptionCallback<T> {
        AnySubscriptionCallback::RegularWithMessageInfo(Box::new(func))
    }
}

impl<T, Func> ArgTuple<T, Func> for (Box<T>,)
where
    T: Message,
    Func: FnMut(Box<T>) + Send + 'static,
{
    fn into_callback_with_args(func: Func) -> AnySubscriptionCallback<T> {
        AnySubscriptionCallback::Boxed(Box::new(func))
    }
}

impl<T, Func> ArgTuple<T, Func> for (Box<T>, MessageInfo)
where
    T: Message,
    Func: FnMut(Box<T>, MessageInfo) + Send + 'static,
{
    fn into_callback_with_args(func: Func) -> AnySubscriptionCallback<T> {
        AnySubscriptionCallback::BoxedWithMessageInfo(Box::new(func))
    }
}

impl<T, Func> ArgTuple<T, Func> for (ReadOnlyLoanedMessage<'_, T>,)
where
    T: Message,
    Func: for<'b> FnMut(ReadOnlyLoanedMessage<'b, T>) + Send + 'static,
{
    fn into_callback_with_args(func: Func) -> AnySubscriptionCallback<T> {
        AnySubscriptionCallback::Loaned(Box::new(func))
    }
}

impl<T, Func> ArgTuple<T, Func> for (ReadOnlyLoanedMessage<'_, T>, MessageInfo)
where
    T: Message,
    Func: for<'b> FnMut(ReadOnlyLoanedMessage<'b, T>, MessageInfo) + Send + 'static,
{
    fn into_callback_with_args(func: Func) -> AnySubscriptionCallback<T> {
        AnySubscriptionCallback::LoanedWithMessageInfo(Box::new(func))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn callback_conversion() {
        type Message = test_msgs::msg::BoundedSequences;
        let cb = |_msg: Message| {};
        assert!(matches!(
            cb.into_callback(),
            AnySubscriptionCallback::<Message>::Regular(_)
        ));
        let cb = |_msg: Message, _info: MessageInfo| {};
        assert!(matches!(
            cb.into_callback(),
            AnySubscriptionCallback::<Message>::RegularWithMessageInfo(_)
        ));
        let cb = |_msg: Box<Message>| {};
        assert!(matches!(
            cb.into_callback(),
            AnySubscriptionCallback::<Message>::Boxed(_)
        ));
        let cb = |_msg: Box<Message>, _info: MessageInfo| {};
        assert!(matches!(
            cb.into_callback(),
            AnySubscriptionCallback::<Message>::BoxedWithMessageInfo(_)
        ));
        let cb = |_msg: ReadOnlyLoanedMessage<'_, Message>| {};
        assert!(matches!(
            cb.into_callback(),
            AnySubscriptionCallback::<Message>::Loaned(_)
        ));
        let cb = |_msg: ReadOnlyLoanedMessage<'_, Message>, _info: MessageInfo| {};
        assert!(matches!(
            cb.into_callback(),
            AnySubscriptionCallback::<Message>::LoanedWithMessageInfo(_)
        ));
    }
}