vox_types/services.rs
1use facet::{Facet, Shape};
2
3/// Static descriptor for a vox RPC service.
4///
5/// Contains the service name and all method descriptors. Built once per service
6/// via OnceLock in macro-generated code.
7pub struct ServiceDescriptor {
8 /// Service name (e.g., "Calculator").
9 pub service_name: &'static str,
10
11 /// All methods in this service.
12 pub methods: &'static [&'static MethodDescriptor],
13
14 /// Documentation string, if any.
15 pub doc: Option<&'static str>,
16}
17
18impl ServiceDescriptor {
19 /// Look up a method descriptor by method ID.
20 pub fn by_id(&self, method_id: MethodId) -> Option<&'static MethodDescriptor> {
21 self.methods.iter().find(|m| m.id == method_id).copied()
22 }
23}
24
25/// Static descriptor for a single RPC method.
26///
27/// Contains static metadata needed for dispatching and calling this method.
28pub struct MethodDescriptor {
29 /// Method ID (hash of service name, method name, arg shapes, return shape).
30 pub id: MethodId,
31
32 /// Service name (e.g., "Calculator").
33 pub service_name: &'static str,
34
35 /// Method name (e.g., "add").
36 pub method_name: &'static str,
37
38 /// Args type shape
39 pub args_shape: &'static Shape,
40
41 /// Arguments in declaration order.
42 pub args: &'static [ArgDescriptor],
43
44 /// Return type shape.
45 pub return_shape: &'static Shape,
46
47 /// Whether `args_shape` reaches a channel (Tx/Rx) anywhere in its tree.
48 /// Computed once via `shape_contains_channel` when the descriptor is
49 /// built, so the driver's cancel/failure paths can short-circuit
50 /// instead of re-walking the shape per request.
51 pub args_have_channels: bool,
52
53 /// Static retry policy for this method.
54 pub retry: RetryPolicy,
55
56 /// Documentation string, if any.
57 pub doc: Option<&'static str>,
58}
59
60impl std::fmt::Debug for MethodDescriptor {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 f.debug_struct("MethodDescriptor")
63 .field("id", &self.id)
64 .field("service_name", &self.service_name)
65 .field("method_name", &self.method_name)
66 .field("retry", &self.retry)
67 .finish_non_exhaustive()
68 }
69}
70
71/// Static retry policy for a method.
72#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
73pub struct RetryPolicy {
74 /// Whether an admitted operation must persist once started.
75 pub persist: bool,
76
77 /// Whether re-executing the same logical operation is semantically safe.
78 pub idem: bool,
79}
80
81impl RetryPolicy {
82 pub const VOLATILE: Self = Self {
83 persist: false,
84 idem: false,
85 };
86
87 pub const IDEM: Self = Self {
88 persist: false,
89 idem: true,
90 };
91
92 pub const PERSIST: Self = Self {
93 persist: true,
94 idem: false,
95 };
96
97 pub const PERSIST_IDEM: Self = Self {
98 persist: true,
99 idem: true,
100 };
101}
102
103declare_id!(
104 /// A unique method identifier — hash of service name, method name, arg shapes, return shape
105 MethodId, u64
106);
107
108/// Descriptor for a single RPC method argument.
109///
110/// Contains metadata about an argument including its name, shape, and
111/// whether it's a channel type (Rx/Tx).
112#[derive(Debug)]
113pub struct ArgDescriptor {
114 /// Argument name (e.g., "user_id", "stream").
115 pub name: &'static str,
116
117 /// Argument type shape.
118 pub shape: &'static Shape,
119}
120
121impl ServiceDescriptor {
122 /// An empty service descriptor for dispatchers that don't serve any methods.
123 pub const EMPTY: ServiceDescriptor = ServiceDescriptor {
124 service_name: "<Empty>",
125 methods: &[],
126 doc: None,
127 };
128}