1
//! Object type for our RPC system.
2

            
3
pub(crate) mod cast;
4

            
5
use std::sync::Arc;
6

            
7
use derive_deftly::define_derive_deftly;
8
use downcast_rs::DowncastSync;
9
use serde::{Deserialize, Serialize};
10

            
11
use self::cast::CastTable;
12

            
13
/// An object in our RPC system to which methods can be addressed.
14
///
15
/// You shouldn't implement this trait yourself; instead, use the
16
/// [`derive_deftly(Object)`].
17
///
18
/// See the documentation for [`derive_deftly(Object)`]
19
/// for examples of how to declare and
20
/// downcast `Object`s.
21
///
22
/// [`derive_deftly(Object)`]: crate::templates::derive_deftly_template_Object
23
pub trait Object: DowncastSync + Send + Sync + 'static {
24
    /// Return true if this object should be given an identifier that allows it
25
    /// to be used outside of the session that generated it.
26
    ///
27
    /// Currently, the only use for such IDs in arti is identifying stream
28
    /// contexts in when opening a SOCKS connection: When an application opens a
29
    /// stream, it needs to declare what RPC context (like a `TorClient`) it's
30
    /// using, which requires that some identifier for that context exist
31
    /// outside of the RPC session that owns it.
32
    fn expose_outside_of_session(&self) -> bool {
33
        false
34
    }
35

            
36
    /// Return a [`CastTable`] that can be used to downcast a `dyn Object` of
37
    /// this type into various kinds of `dyn Trait` references.
38
    ///
39
    /// The default implementation of this method declares that the `Object`
40
    /// can't be downcast into any traits.
41
    ///
42
    /// You should not implement this method yourself; instead use
43
    /// [`derive_deftly(Object)`](crate::templates::derive_deftly_template_Object).
44
    fn get_cast_table(&self) -> &CastTable {
45
        &cast::EMPTY_CAST_TABLE
46
    }
47

            
48
    /// Optionally, return a delegation target for this `Object``.
49
    ///
50
    /// If method lookup fails on this object, then the `delegate`
51
6
    fn delegate(&self) -> Option<Arc<dyn Object>> {
52
6
        None
53
6
    }
54
}
55
downcast_rs::impl_downcast!(sync Object);
56

            
57
/// An identifier for an Object within the context of a Session.
58
///
59
/// These are opaque from the client's perspective.
60
36
#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)]
61
#[serde(transparent)]
62
pub struct ObjectId(
63
    // (We use Box<str> to save a word here, since these don't have to be
64
    // mutable ever.)
65
    Box<str>,
66
);
67

            
68
impl AsRef<str> for ObjectId {
69
18340
    fn as_ref(&self) -> &str {
70
18340
        self.0.as_ref()
71
18340
    }
72
}
73

            
74
impl<T> From<T> for ObjectId
75
where
76
    T: Into<Box<str>>,
77
{
78
1088
    fn from(value: T) -> Self {
79
1088
        Self(value.into())
80
1088
    }
81
}
82

            
83
/// Extension trait for `Arc<dyn Object>` to support convenient
84
/// downcasting to `dyn Trait`.
85
///
86
/// You don't need to use this for downcasting to an object's concrete
87
/// type; for that, use [`downcast_rs::DowncastSync`].
88
///
89
/// # Examples
90
///
91
/// ```
92
/// use tor_rpcbase::{Object, ObjectArcExt, templates::*};
93
/// use derive_deftly::Deftly;
94
/// use std::sync::Arc;
95
///
96
/// #[derive(Deftly)]
97
/// #[derive_deftly(Object)]
98
/// #[deftly(rpc(downcastable_to = "HasFeet"))]
99
/// pub struct Frog {}
100
/// pub trait HasFeet {
101
///     fn num_feet(&self) -> usize;
102
/// }
103
/// impl HasFeet for Frog {
104
///     fn num_feet(&self) -> usize { 4 }
105
/// }
106
///
107
/// /// If `obj` is a HasFeet, return how many feet it has.
108
/// /// Otherwise, return 0.
109
/// fn check_feet(obj: Arc<dyn Object>) -> usize {
110
///     let maybe_has_feet: Option<&dyn HasFeet> = obj.cast_to_trait();
111
///     match maybe_has_feet {
112
///         Some(foot_haver) => foot_haver.num_feet(),
113
///         None => 0,
114
///     }
115
/// }
116
///
117
/// assert_eq!(check_feet(Arc::new(Frog{})), 4);
118
/// ```
119
pub trait ObjectArcExt {
120
    /// Try to cast this `Arc<dyn Object>` to a `T`.  On success, return a reference to
121
    /// T; on failure, return None.
122
    fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T>;
123

            
124
    /// Try to cast this `Arc<dyn Object>` to an `Arc<T>`.
125
    fn cast_to_arc_trait<T: ?Sized + 'static>(self) -> Result<Arc<T>, Arc<dyn Object>>;
126
}
127

            
128
impl dyn Object {
129
    /// Try to cast this `Object` to a `T`.  On success, return a reference to
130
    /// T; on failure, return None.
131
    ///
132
    /// This method is only for casting to `&dyn Trait`;
133
    /// see [`ObjectArcExt`] for limitations.
134
8
    pub fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T> {
135
8
        let table = self.get_cast_table();
136
8
        table.cast_object_to(self)
137
8
    }
138
}
139

            
140
impl ObjectArcExt for Arc<dyn Object> {
141
2
    fn cast_to_trait<T: ?Sized + 'static>(&self) -> Option<&T> {
142
2
        let obj: &dyn Object = self.as_ref();
143
2
        obj.cast_to_trait()
144
2
    }
145
4
    fn cast_to_arc_trait<T: ?Sized + 'static>(self) -> Result<Arc<T>, Arc<dyn Object>> {
146
4
        let table = self.get_cast_table();
147
4
        table.cast_object_to_arc(self.clone())
148
4
    }
149
}
150

            
151
define_derive_deftly! {
152
/// Allow a type to participate as an Object in the RPC system.
153
///
154
/// This template implements `Object` for the
155
/// target type, and can be used to cause objects to participate in the trait
156
/// downcasting system.
157
///
158
/// # Examples
159
///
160
/// ## Simple case, just implements `Object`.
161
///
162
/// ```
163
/// use tor_rpcbase::{self as rpc, templates::*};
164
/// use derive_deftly::Deftly;
165
///
166
/// #[derive(Default, Deftly)]
167
/// #[derive_deftly(Object)]
168
/// struct Houseplant {
169
///    oxygen_per_sec: f64,
170
///    benign_neglect: u8
171
/// }
172
///
173
/// // You can downcast an Object to a concrete type.
174
/// use downcast_rs::DowncastSync;
175
/// use std::sync::Arc;
176
/// let plant_obj: Arc<dyn rpc::Object> = Arc::new(Houseplant::default());
177
/// let as_plant: Arc<Houseplant> = plant_obj.downcast_arc().ok().unwrap();
178
/// ```
179
///
180
/// ## With trait downcasting
181
///
182
/// By default, you can use [`downcast_rs`] to downcast a `dyn Object` to its
183
/// concrete type.  If you also need to be able to downcast a `dyn Object` to a given
184
/// trait that it implements, you can use the `downcastable_to` attributes for `Object` to have
185
/// it participate in trait downcasting:
186
///
187
/// ```
188
/// use tor_rpcbase::{self as rpc, templates::*};
189
/// use derive_deftly::Deftly;
190
///
191
/// #[derive(Deftly)]
192
/// #[derive_deftly(Object)]
193
/// #[deftly(rpc(downcastable_to = "Gizmo, Doodad"))]
194
/// struct Frobnitz {}
195
///
196
/// trait Gizmo {}
197
/// trait Doodad {}
198
/// impl Gizmo for Frobnitz {}
199
/// impl Doodad for Frobnitz {}
200
///
201
/// use std::sync::Arc;
202
/// use rpc::ObjectArcExt; // for the cast_to method.
203
/// let frob_obj: Arc<dyn rpc::Object> = Arc::new(Frobnitz {});
204
/// let gizmo: &dyn Gizmo = frob_obj.cast_to_trait().unwrap();
205
/// let doodad: &dyn Doodad = frob_obj.cast_to_trait().unwrap();
206
/// ```
207
///
208
/// ## With generic objects
209
///
210
/// Right now, a generic object can't participate in our method lookup system,
211
/// but it _can_ participate in trait downcasting.  We'll try to remove this
212
/// limitation in the future.
213
///
214
/// ```
215
/// use tor_rpcbase::{self as rpc, templates::*};
216
/// use derive_deftly::Deftly;
217
///
218
/// #[derive(Deftly)]
219
/// #[derive_deftly(Object)]
220
/// #[deftly(rpc(downcastable_to = "ExampleTrait"))]
221
/// struct Generic<T,U> where T:Clone, U:PartialEq {
222
///     t: T,
223
///     u: U,
224
/// }
225
///
226
/// trait ExampleTrait {}
227
/// impl<T:Clone,U:PartialEq> ExampleTrait for Generic<T,U> {}
228
///
229
/// use std::sync::Arc;
230
/// use rpc::ObjectArcExt; // for the cast_to method.
231
/// let obj: Arc<dyn rpc::Object> = Arc::new(Generic { t: 42_u8, u: 42_u8 });
232
/// let tr: &dyn ExampleTrait = obj.cast_to_trait().unwrap();
233
/// ```
234
///
235
/// ## Making an object "exposed outside of the session"
236
///
237
/// You can flag any kind of Object so that its identifiers will be exported
238
/// outside of the local RPC session.  (Arti uses this for Objects whose
239
/// ObjectId needs to be used as a SOCKS identifier.)  To do so,
240
/// use the `expose_outside_session` attribute:
241
///
242
/// ```
243
/// use tor_rpcbase::{self as rpc, templates::*};
244
/// use derive_deftly::Deftly;
245
///
246
/// #[derive(Deftly)]
247
/// #[derive_deftly(Object)]
248
/// #[deftly(rpc(expose_outside_of_session))]
249
/// struct Visible {}
250
/// ```
251
///
252
/// ## Delegation
253
///
254
/// You can give an Object the ability to delegate
255
/// method invocations to another object it contains.
256
/// The inner object must be an `Arc`.
257
/// To do so, use the `delegate_with` attribute.
258
/// The attribute must contain an expression of type
259
/// `FnOnce(&Self) -> Option(Arc<T>)`, where T implements Object.
260
///
261
/// ```
262
/// use tor_rpcbase::{self as rpc, templates::*};
263
/// use derive_deftly::Deftly;
264
/// use std::sync::Arc;
265
///
266
/// #[derive(Deftly)]
267
/// #[derive_deftly(Object)]
268
/// struct Inner {}
269
///
270
/// #[derive(Deftly)]
271
/// #[derive_deftly(Object)]
272
/// #[deftly(rpc(
273
///      delegate_with="|this: &Self| Some(this.inner.clone())",
274
///      delegate_type="Inner"
275
/// ))]
276
/// struct Outer {
277
///     inner: Arc<Inner>,
278
/// }
279
/// ```
280
///
281
    export Object expect items:
282

            
283

            
284
    impl<$tgens> $ttype where
285
        // We need this restriction in case there are generics
286
        // that might not impl these traits.
287
        $ttype: Send + Sync + 'static,
288
        $twheres
289
    {
290
        /// Construct a new `CastTable` for this type.
291
        ///
292
        /// This is a function so that we can call it multiple times as
293
        /// needed if the type is generic.
294
        ///
295
        /// Don't invoke this yourself; instead use `decl_object!`.
296
        #[doc(hidden)]
297
10
        fn make_cast_table() -> $crate::CastTable {
298
            ${if tmeta(rpc(downcastable_to)) {
299
                $crate::cast_table_deftness_helper!{
300
                    // TODO ideally we would support multiple downcastable_to rather
301
                    // than a single list, and use `as ty`
302
                    ${tmeta(rpc(downcastable_to)) as token_stream}
303
                }
304
            } else {
305
                $crate::CastTable::default()
306
            }}
307
        }
308
    }
309

            
310
    ${if tmeta(rpc(delegate_type)) {
311
        $crate::register_delegation_note!(
312
            $ttype,
313
            ${tmeta(rpc(delegate_type )) as ty}
314
        );
315
    }}
316

            
317
    ${if tmeta(rpc(delegate_type)) {
318
        #[doc = "Delegates to [`"]
319
        #[doc = ${tmeta(rpc(delegate_type)) as str}]
320
        #[doc = "`]"]
321
    }}
322
    impl<$tgens> $crate::Object for $ttype where
323
        // We need this restriction in case there are generics
324
        // that might not impl these traits.
325
        $ttype: Send + Sync + 'static,
326
        $twheres
327
    {
328
        ${if tmeta(rpc(expose_outside_of_session)) {
329
            fn expose_outside_of_session(&self) -> bool {
330
                true
331
            }
332
        }}
333

            
334
        ${if tmeta(rpc(delegate_with)) {
335
12
            fn delegate(&self) -> Option<Arc<dyn $crate::Object>> {
336
                let r: Option<Arc<${tmeta(rpc(delegate_type)) as ty}>> = (${tmeta(rpc(delegate_with)) as expr})(self);
337

            
338
10
                r.map(|v| v as Arc<dyn $crate::Object>)
339
            }
340
        }}
341

            
342
10
        fn get_cast_table(&self) -> &$crate::CastTable {
343
            ${if tgens {
344
                // For generic types, we have a potentially unbounded number
345
                // of CastTables: one for each instantiation of the type.
346
                // Therefore we keep a mutable add-only HashMap of CastTables.
347

            
348
                use $crate::once_cell::sync::Lazy;
349
                use std::sync::RwLock;
350
                use std::collections::HashMap;
351
                use std::any::TypeId;
352
                // Map from concrete type to CastTable.
353
                //
354
                // Note that we use `&'static CastTable` here, not
355
                // `Box<CastTable>`: If we used Box<>, the borrow checker would
356
                // worry that our `CastTable`s might get freed after we returned
357
                // a reference to them.  Using `&'static` guarantees that the CastTable
358
                // references are safe to return.
359
                //
360
                // In order to get a `&'static`, we need to use Box::leak().
361
                // That's fine, since we only create one CastTable per
362
                // instantiation of the type.
363
                static TABLES: Lazy<RwLock<HashMap<TypeId, &'static $crate::CastTable>>> =
364
2
                Lazy::new(|| RwLock::new(HashMap::new()));
365
                {
366
                    let tables_r = TABLES.read().expect("poisoned lock");
367
                    if let Some(table) = tables_r.get(&TypeId::of::<Self>()) {
368
                        // Fast case: we already had a CastTable for this instantiation.
369
                        table
370
                    } else {
371
                        // We didn't find a CastTable.
372
                        drop(tables_r); // prevent deadlock.
373
                        TABLES
374
                         .write()
375
                         .expect("poisoned lock")
376
                         .entry(TypeId::of::<Self>())
377
                         // We use `or_insert_with` here to avoid a race
378
                         // condition: we only want to call make_cast_table if
379
                         // one didn't already exist.
380
2
                         .or_insert_with(|| Box::leak(Box::new(Self::make_cast_table())))
381
                    }
382
                }
383
            } else {
384
                // For non-generic types, we only ever have a single CastTable,
385
                // so we can just construct it once and return it.
386
                use $crate::once_cell::sync::Lazy;
387
4
                static TABLE: Lazy<$crate::CastTable> = Lazy::new(|| $ttype::make_cast_table());
388
                &TABLE
389
            }}
390
        }
391
    }
392
}
393
pub use derive_deftly_template_Object;
394

            
395
#[cfg(test)]
396
mod test {
397
    // @@ begin test lint list maintained by maint/add_warning @@
398
    #![allow(clippy::bool_assert_comparison)]
399
    #![allow(clippy::clone_on_copy)]
400
    #![allow(clippy::dbg_macro)]
401
    #![allow(clippy::mixed_attributes_style)]
402
    #![allow(clippy::print_stderr)]
403
    #![allow(clippy::print_stdout)]
404
    #![allow(clippy::single_char_pattern)]
405
    #![allow(clippy::unwrap_used)]
406
    #![allow(clippy::unchecked_duration_subtraction)]
407
    #![allow(clippy::useless_vec)]
408
    #![allow(clippy::needless_pass_by_value)]
409
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
410

            
411
    use super::*;
412
    use derive_deftly::Deftly;
413

            
414
    #[derive(Deftly)]
415
    #[derive_deftly(Object)]
416
    #[deftly(rpc(downcastable_to = "HasWheels"))]
417
    struct Bicycle {}
418
    trait HasWheels {
419
        fn num_wheels(&self) -> usize;
420
    }
421
    impl HasWheels for Bicycle {
422
        fn num_wheels(&self) -> usize {
423
            2
424
        }
425
    }
426

            
427
    #[derive(Deftly)]
428
    #[derive_deftly(Object)]
429
    struct Opossum {}
430

            
431
    #[test]
432
    fn standard_cast() {
433
        let bike = Bicycle {};
434
        let erased_bike: &dyn Object = &bike;
435
        let has_wheels: &dyn HasWheels = erased_bike.cast_to_trait().unwrap();
436
        assert_eq!(has_wheels.num_wheels(), 2);
437

            
438
        let pogo = Opossum {};
439
        let erased_pogo: &dyn Object = &pogo;
440
        let has_wheels: Option<&dyn HasWheels> = erased_pogo.cast_to_trait();
441
        assert!(has_wheels.is_none());
442
    }
443

            
444
    #[derive(Deftly)]
445
    #[derive_deftly(Object)]
446
    #[deftly(rpc(downcastable_to = "HasWheels"))]
447
    struct Crowd<T: HasWheels + Send + Sync + 'static> {
448
        members: Vec<T>,
449
    }
450
    impl<T: HasWheels + Send + Sync> HasWheels for Crowd<T> {
451
        fn num_wheels(&self) -> usize {
452
            self.members.iter().map(T::num_wheels).sum()
453
        }
454
    }
455

            
456
    #[test]
457
    fn generic_cast() {
458
        let bikes = Crowd {
459
            members: vec![Bicycle {}, Bicycle {}],
460
        };
461
        let erased_bikes: &dyn Object = &bikes;
462
        let has_wheels: &dyn HasWheels = erased_bikes.cast_to_trait().unwrap();
463
        assert_eq!(has_wheels.num_wheels(), 4);
464

            
465
        let arc_bikes = Arc::new(bikes);
466
        let erased_arc_bytes: Arc<dyn Object> = arc_bikes.clone();
467
        let arc_has_wheels: Arc<dyn HasWheels> =
468
            erased_arc_bytes.clone().cast_to_arc_trait().ok().unwrap();
469
        assert_eq!(arc_has_wheels.num_wheels(), 4);
470

            
471
        let ref_has_wheels: &dyn HasWheels = erased_arc_bytes.cast_to_trait().unwrap();
472
        assert_eq!(ref_has_wheels.num_wheels(), 4);
473

            
474
        trait SomethingElse {}
475
        let arc_something_else: Result<Arc<dyn SomethingElse>, _> =
476
            erased_arc_bytes.clone().cast_to_arc_trait();
477
        let err_arc = arc_something_else.err().unwrap();
478
        assert!(Arc::ptr_eq(&err_arc, &erased_arc_bytes));
479
    }
480

            
481
    #[derive(Deftly)]
482
    #[derive_deftly(Object)]
483
    #[deftly(rpc(delegate_with = "|cage: &Self| Some(cage.possum.clone())"))]
484
    #[deftly(rpc(delegate_type = "Opossum"))]
485
    struct PossumCage {
486
        possum: Arc<Opossum>,
487
    }
488
}