use proc_macro2::TokenStream;
use quote::quote;
use crate::generated::descriptor::{FileDescriptorProto, FileDescriptorSet};
pub(crate) fn reflectable_impl(ty: &TokenStream, buffa_path: &TokenStream) -> TokenStream {
quote! {
impl ::buffa_descriptor::reflect::Reflectable for #ty {
fn reflect(&self) -> ::buffa_descriptor::reflect::ReflectCow<'_> {
let pool = #buffa_path::reflect::descriptor_pool();
let idx = pool
.message_index(<Self as ::buffa::MessageName>::FULL_NAME)
.unwrap_or_else(|| panic!(
"type {:?} not registered in this package's descriptor pool (cross-crate reflect()?)",
<Self as ::buffa::MessageName>::FULL_NAME,
));
::buffa_descriptor::reflect::ReflectCow::Owned(
::buffa::alloc::boxed::Box::new(
::buffa_descriptor::reflect::DynamicMessage::from_message(
self,
::buffa::alloc::sync::Arc::clone(pool),
idx,
),
),
)
}
}
}
}
pub(crate) fn reflect_element_impl_bridge(ty: &TokenStream) -> TokenStream {
quote! {
impl ::buffa_descriptor::reflect::ReflectElement for #ty {
fn as_value_ref(&self) -> ::buffa_descriptor::reflect::ValueRef<'_> {
::buffa_descriptor::reflect::ValueRef::Message(
::buffa_descriptor::reflect::Reflectable::reflect(self),
)
}
}
}
}
pub(crate) fn reflectable_impl_vtable(ty: &TokenStream) -> TokenStream {
quote! {
impl ::buffa_descriptor::reflect::Reflectable for #ty {
#[inline]
fn reflect(&self) -> ::buffa_descriptor::reflect::ReflectCow<'_> {
::buffa_descriptor::reflect::ReflectCow::Borrowed(self)
}
}
}
}
pub(crate) fn encode_fds_once(file_descriptors: &[FileDescriptorProto]) -> Vec<u8> {
use buffa::Message;
FileDescriptorSet {
file: file_descriptors.to_vec(),
..Default::default()
}
.encode_to_vec()
}
pub(crate) fn reflect_pool_module(fds_bytes: &[u8]) -> TokenStream {
let byte_literals = fds_bytes.iter().map(|b| quote! { #b });
quote! {
pub mod reflect {
pub const FILE_DESCRIPTOR_SET_BYTES: &[u8] = &[#(#byte_literals),*];
pub fn descriptor_pool() -> &'static ::buffa::alloc::sync::Arc<::buffa_descriptor::DescriptorPool> {
static POOL: ::std::sync::OnceLock<
::buffa::alloc::sync::Arc<::buffa_descriptor::DescriptorPool>,
> = ::std::sync::OnceLock::new();
POOL.get_or_init(|| {
::buffa::alloc::sync::Arc::new(
::buffa_descriptor::DescriptorPool::decode(FILE_DESCRIPTOR_SET_BYTES)
.expect("embedded FileDescriptorSet is well-formed"),
)
})
}
}
}
}
pub(crate) fn pool_accessor_reexport(buffa_path: &TokenStream) -> TokenStream {
quote! {
#[doc = "The lazily-built descriptor pool for this package's"]
#[doc = "`Reflectable` impls. Re-exported from `__buffa::reflect`."]
pub use #buffa_path::reflect::descriptor_pool;
}
}
const _: usize = {
0
};
#[cfg(test)]
mod tests {
use super::*;
use quote::format_ident;
#[test]
fn reflectable_impl_emits_well_formed_tokens() {
let ty = format_ident!("Person");
let ty_ts = quote! { #ty };
let buffa = quote! { __buffa };
let tokens = reflectable_impl(&ty_ts, &buffa);
let parsed = syn::parse2::<syn::ItemImpl>(tokens.clone());
assert!(parsed.is_ok(), "generated impl must parse: {tokens}");
}
#[test]
fn reflect_pool_module_emits_well_formed_tokens() {
let fd = FileDescriptorProto {
name: Some("test.proto".into()),
package: Some("test".into()),
..Default::default()
};
let bytes = encode_fds_once(&[fd]);
{
use buffa::Message;
let decoded =
FileDescriptorSet::decode_from_slice(&bytes).expect("encoded FDS round-trips");
assert_eq!(decoded.file.len(), 1);
assert_eq!(decoded.file[0].name.as_deref(), Some("test.proto"));
}
let tokens = reflect_pool_module(&bytes);
let parsed = syn::parse2::<syn::ItemMod>(tokens.clone());
assert!(parsed.is_ok(), "generated module must parse: {tokens}");
assert!(tokens.to_string().contains("FILE_DESCRIPTOR_SET_BYTES"));
}
#[test]
fn pool_accessor_reexport_emits_well_formed_tokens() {
let buffa = quote! { __buffa };
let tokens = pool_accessor_reexport(&buffa);
let parsed = syn::parse2::<syn::ItemUse>(tokens.clone());
assert!(parsed.is_ok(), "generated re-export must parse: {tokens}");
}
}