1
//! Types and code for describing our type-based dispatch system.
2

            
3
use std::collections::{BTreeMap, BTreeSet};
4

            
5
use serde::Serialize;
6

            
7
use crate::{method::method_info_by_typeid, MethodInfo_, NoUpdates};
8

            
9
/// A table describing, for a single RPC method,
10
/// which types it expects and returns, and which objects it applies to.
11
///
12
/// This is, for now, a serialize-only type; it does nothing else.
13
///
14
/// See [`RpcDispatchInformation`] for caveats about type names.
15
#[derive(Serialize, Debug, Clone)]
16
struct MethodDescription {
17
    /// The type representing an invocation of this method.
18
    ///
19
    /// Its fields are the method's parameters.
20
    method_type: String,
21
    /// The type that this method returns on successful completion.
22
    output_type: String,
23
    /// The type (if any) that this method delivers as an incremental status update.
24
    update_type: Option<String>,
25
    /// A list of the types of Objects that this method can be applied to.
26
    applies_to_object_types: BTreeSet<String>,
27
}
28

            
29
/// A table describing the the set of RPC methods available,
30
/// which types they expect and return, and which objects they apply to.
31
///
32
/// This is, for now, a serialize-only type; it does nothing else.
33
///
34
/// All "type names" in this object refer to a Rust type in Arti.
35
/// These Rust types are mainly useful for finding the relevant types
36
/// in the generated Arti documentation.
37
/// They are not guaranteed to be stable across Arti versions:
38
/// for example, we might rename a type, or put it in a different module or crate.
39
/// They are not guaranteed to be stable across Rust versions:
40
/// see the caveats in [`std::any::type_name`].
41
#[derive(Serialize, Debug, Clone)]
42
pub struct RpcDispatchInformation {
43
    /// A map from RPC method name (such as "arti:foo") to a description of that method.
44
    methods: BTreeMap<String, MethodDescription>,
45

            
46
    /// A map from an object type to a list of the types that object can delegate to.
47
    delegations: BTreeMap<String, BTreeSet<String>>,
48
}
49

            
50
impl super::DispatchTable {
51
    /// Return a description for all of the RPC methods available,
52
    /// which types they expect and return, and which objects they apply to.
53
    ///
54
    /// Currently, the resulting object is good for nothing but serialization.
55
    pub fn dispatch_information(&self) -> RpcDispatchInformation {
56
        let mut methods = BTreeMap::new();
57
        for invoker_ent in self.map.values() {
58
            let Some(method_info) = method_info_by_typeid(invoker_ent.invoker.method_type()) else {
59
                continue; // This isn't an RpcMethod.
60
            };
61

            
62
            let rpc_method_name = method_info.method_name.to_owned();
63
            let (object_type_name, method_type_name) =
64
                invoker_ent.invoker.object_and_method_type_names();
65
            let description = methods
66
                .entry(rpc_method_name)
67
                .or_insert_with(|| MethodDescription::new(method_type_name, method_info));
68
            description.push_object_type(object_type_name);
69
        }
70

            
71
        let mut delegations = BTreeMap::new();
72
        for note in inventory::iter::<DelegationNote>() {
73
            let set = delegations
74
                .entry((note.from_type_name)().to_string())
75
                .or_insert_with(BTreeSet::new);
76
            set.insert((note.to_type_name)().to_string());
77
        }
78

            
79
        RpcDispatchInformation {
80
            methods,
81
            delegations,
82
        }
83
    }
84
}
85

            
86
impl MethodDescription {
87
    /// Construct a new `MethodDescription`.
88
    fn new(method_type_name: &str, info: &MethodInfo_) -> Self {
89
        let method_type_name = method_type_name.to_owned();
90
        let output_type_name = (info.output_name)().to_owned();
91
        let update_type_name = {
92
            let name = (info.update_name)();
93
            if name == std::any::type_name::<NoUpdates>() {
94
                None
95
            } else {
96
                Some(name.to_owned())
97
            }
98
        };
99

            
100
        MethodDescription {
101
            method_type: method_type_name,
102
            output_type: output_type_name,
103
            update_type: update_type_name,
104
            applies_to_object_types: Default::default(),
105
        }
106
    }
107

            
108
    /// Add `object_type_name` to the list of object types this method applies to.
109
    fn push_object_type(&mut self, object_type_name: &str) {
110
        self.applies_to_object_types
111
            .insert(object_type_name.to_owned());
112
    }
113
}
114

            
115
/// Helper to implement RpcDispatchInformation.
116
#[derive(Debug)]
117
#[doc(hidden)]
118
#[allow(clippy::exhaustive_structs)]
119
pub struct DelegationNote {
120
    /// Name of the type we're delegating from.
121
    ///
122
    /// (We use a fn() here since std::any::type_name isn't yet const.)
123
    pub from_type_name: fn() -> &'static str,
124
    /// Name of the type we're delegating to.
125
    pub to_type_name: fn() -> &'static str,
126
}
127
inventory::collect!(DelegationNote);
128

            
129
/// Helper: Declare that `$from_type` (which must implement `Object`)
130
/// can delegate to `$to_type` (which also must implement `Object`).
131
///
132
/// This declaration is used only to help implement `RpcDispatchInformation`.
133
#[macro_export]
134
#[doc(hidden)]
135
macro_rules! register_delegation_note {
136
    { $from_type:ty, $to_type:ty } => {
137
        $crate::inventory::submit!{$crate::DelegationNote {
138
            from_type_name: std::any::type_name::<$from_type>,
139
            to_type_name: std::any::type_name::<$to_type>,
140
        }}
141
    }
142
}