Skip to main content

buffa_reflect/
service.rs

1//! [`ServiceDescriptor`] and [`MethodDescriptor`] — handles to gRPC
2//! services and their methods.
3//!
4//! Services + methods are walked at pool-build time and cached on the
5//! pool. Cross-file `input_type` / `output_type` references resolve
6//! through the same name table the field resolver uses.
7
8use buffa_descriptor::generated::descriptor::{MethodDescriptorProto, ServiceDescriptorProto};
9
10use crate::{
11    file::FileDescriptor,
12    message::MessageDescriptor,
13    pool::{DescriptorPool, FileIndex, MessageIndex},
14};
15
16/// Internal index into [`crate::pool::PoolInner::services`].
17pub(crate) type ServiceIndex = u32;
18
19/// Internal entry for a service in the pool.
20#[derive(Clone)]
21pub(crate) struct ServiceEntry {
22    pub(crate) full_name: Box<str>,
23    pub(crate) name: Box<str>,
24    pub(crate) file: FileIndex,
25    pub(crate) proto_index: u32,
26    pub(crate) methods: Vec<MethodEntry>,
27}
28
29#[derive(Clone)]
30pub(crate) struct MethodEntry {
31    pub(crate) name: Box<str>,
32    pub(crate) full_name: Box<str>,
33    pub(crate) input: MessageIndex,
34    pub(crate) output: MessageIndex,
35    pub(crate) is_client_streaming: bool,
36    pub(crate) is_server_streaming: bool,
37    pub(crate) proto_index: u32,
38}
39
40/// Handle to one service in a [`DescriptorPool`].
41#[derive(Clone, Debug)]
42pub struct ServiceDescriptor {
43    pub(crate) pool: DescriptorPool,
44    pub(crate) index: ServiceIndex,
45}
46
47impl ServiceDescriptor {
48    fn entry(&self) -> &ServiceEntry {
49        &self.pool.inner.services[self.index as usize]
50    }
51
52    /// Leaf name (the part after the last `.`).
53    #[must_use]
54    pub fn name(&self) -> &str {
55        &self.entry().name
56    }
57
58    /// Fully-qualified name (`<package>.<ServiceName>`).
59    #[must_use]
60    pub fn full_name(&self) -> &str {
61        &self.entry().full_name
62    }
63
64    /// Owning file.
65    #[must_use]
66    pub fn parent_file(&self) -> FileDescriptor {
67        FileDescriptor {
68            pool: self.pool.clone(),
69            index: self.entry().file,
70        }
71    }
72
73    /// All declared methods, in proto declaration order.
74    pub fn methods(&self) -> impl ExactSizeIterator<Item = MethodDescriptor> + '_ {
75        let len = self.entry().methods.len() as u32;
76        let pool = self.pool.clone();
77        let svc = self.index;
78        (0..len).map(move |idx| MethodDescriptor {
79            pool: pool.clone(),
80            service: svc,
81            index: idx,
82        })
83    }
84
85    /// Raw [`ServiceDescriptorProto`] for advanced use.
86    #[must_use]
87    pub fn descriptor_proto(&self) -> &ServiceDescriptorProto {
88        let entry = self.entry();
89        let file = &self.pool.inner.files[entry.file as usize];
90        &file.proto.service[entry.proto_index as usize]
91    }
92}
93
94impl PartialEq for ServiceDescriptor {
95    fn eq(&self, other: &Self) -> bool {
96        std::sync::Arc::ptr_eq(&self.pool.inner, &other.pool.inner) && self.index == other.index
97    }
98}
99
100impl Eq for ServiceDescriptor {}
101
102/// Handle to one method on a [`ServiceDescriptor`].
103#[derive(Clone, Debug)]
104pub struct MethodDescriptor {
105    pub(crate) pool: DescriptorPool,
106    pub(crate) service: ServiceIndex,
107    pub(crate) index: u32,
108}
109
110impl MethodDescriptor {
111    fn entry(&self) -> &MethodEntry {
112        &self.pool.inner.services[self.service as usize].methods[self.index as usize]
113    }
114
115    /// Leaf name.
116    #[must_use]
117    pub fn name(&self) -> &str {
118        &self.entry().name
119    }
120
121    /// Fully-qualified name (`<service.full_name>.<MethodName>`).
122    #[must_use]
123    pub fn full_name(&self) -> &str {
124        &self.entry().full_name
125    }
126
127    /// Resolved input message descriptor.
128    #[must_use]
129    pub fn input(&self) -> MessageDescriptor {
130        MessageDescriptor {
131            pool: self.pool.clone(),
132            index: self.entry().input,
133        }
134    }
135
136    /// Resolved output message descriptor.
137    #[must_use]
138    pub fn output(&self) -> MessageDescriptor {
139        MessageDescriptor {
140            pool: self.pool.clone(),
141            index: self.entry().output,
142        }
143    }
144
145    /// True iff the method's input is a stream.
146    #[must_use]
147    pub fn is_client_streaming(&self) -> bool {
148        self.entry().is_client_streaming
149    }
150
151    /// True iff the method's output is a stream.
152    #[must_use]
153    pub fn is_server_streaming(&self) -> bool {
154        self.entry().is_server_streaming
155    }
156
157    /// Owning service.
158    #[must_use]
159    pub fn parent_service(&self) -> ServiceDescriptor {
160        ServiceDescriptor {
161            pool: self.pool.clone(),
162            index: self.service,
163        }
164    }
165
166    /// Raw [`MethodDescriptorProto`] for advanced use.
167    #[must_use]
168    pub fn descriptor_proto(&self) -> &MethodDescriptorProto {
169        let svc_entry = &self.pool.inner.services[self.service as usize];
170        let file = &self.pool.inner.files[svc_entry.file as usize];
171        &file.proto.service[svc_entry.proto_index as usize].method
172            [self.entry().proto_index as usize]
173    }
174}
175
176impl PartialEq for MethodDescriptor {
177    fn eq(&self, other: &Self) -> bool {
178        std::sync::Arc::ptr_eq(&self.pool.inner, &other.pool.inner)
179            && self.service == other.service
180            && self.index == other.index
181    }
182}
183
184impl Eq for MethodDescriptor {}
185
186impl DescriptorPool {
187    /// Iterate every service in the pool.
188    pub fn services(&self) -> impl ExactSizeIterator<Item = ServiceDescriptor> + '_ {
189        let pool = self.clone();
190        (0..self.inner.services.len() as u32).map(move |idx| ServiceDescriptor {
191            pool: pool.clone(),
192            index: idx,
193        })
194    }
195
196    /// Resolve a fully-qualified service name (with or without leading dot).
197    #[must_use]
198    pub fn get_service_by_name(&self, full_name: &str) -> Option<ServiceDescriptor> {
199        let key = full_name.strip_prefix('.').unwrap_or(full_name);
200        self.inner
201            .service_names
202            .get(key)
203            .map(|idx| ServiceDescriptor {
204                pool: self.clone(),
205                index: *idx,
206            })
207    }
208}
209
210impl FileDescriptor {
211    /// Services declared in this file.
212    pub fn services(&self) -> impl ExactSizeIterator<Item = ServiceDescriptor> + '_ {
213        let pool = self.pool.clone();
214        let entry = &self.pool.inner.files[self.index as usize];
215        entry
216            .service_indices
217            .clone()
218            .into_iter()
219            .map(move |idx| ServiceDescriptor {
220                pool: pool.clone(),
221                index: idx,
222            })
223    }
224}