use std::collections::HashMap;
use proc_macro2::TokenStream;
use quote::quote;
use crate::context::CodeGenContext;
use crate::features::{resolve_field, ResolvedFeatures};
use crate::generated::descriptor::field_descriptor_proto::{Label, Type};
use crate::generated::descriptor::DescriptorProto;
use crate::idents::make_field_ident;
use crate::impl_message::{
effective_type, is_explicit_presence_scalar, is_real_oneof_member, is_supported_field_type,
};
use crate::message::{is_closed_enum, is_map_field, rust_path_to_tokens};
use crate::oneof::oneof_variant_ident;
use crate::reflect_view::{scalar_default, scalar_variant};
use crate::CodeGenError;
pub(crate) struct OwnedReflectScope<'a> {
pub ctx: &'a CodeGenContext<'a>,
pub msg: &'a DescriptorProto,
pub name_ident: &'a proc_macro2::Ident,
pub buffa_path: &'a TokenStream,
pub current_package: &'a str,
pub proto_fqn: &'a str,
pub features: &'a ResolvedFeatures,
pub oneof_idents: &'a HashMap<usize, proc_macro2::Ident>,
pub oneof_prefix: &'a TokenStream,
pub nesting: usize,
}
pub(crate) fn reflect_owned_impls(
scope: &OwnedReflectScope<'_>,
) -> Result<TokenStream, CodeGenError> {
let ctx = scope.ctx;
let msg = scope.msg;
let name_ident = scope.name_ident;
let buffa_path = scope.buffa_path;
let current_package = scope.current_package;
let proto_fqn = scope.proto_fqn;
let features = scope.features;
let oneof_idents = scope.oneof_idents;
let oneof_prefix = scope.oneof_prefix;
let nesting = scope.nesting;
let vr = quote! { ::buffa_descriptor::reflect::ValueRef };
let cow = quote! { ::buffa_descriptor::reflect::ReflectCow };
let reflectable = quote! { ::buffa_descriptor::reflect::Reflectable };
let mut get_arms: Vec<TokenStream> = Vec::new();
let mut has_arms: Vec<TokenStream> = Vec::new();
for field in &msg.field {
if is_real_oneof_member(field) {
continue;
}
let ty = effective_type(ctx, field, features);
if !is_supported_field_type(ty) {
continue;
}
let name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let id = make_field_ident(name);
let number = field.number.unwrap_or(0) as u32;
let is_repeated = field.label.unwrap_or_default() == Label::LABEL_REPEATED;
if is_repeated && is_map_field(msg, field) {
get_arms.push(quote! { #number => #vr::Map(&self.#id), });
has_arms.push(quote! { #number => !self.#id.is_empty(), });
continue;
}
if is_repeated {
get_arms.push(quote! { #number => #vr::List(&self.#id), });
has_arms.push(quote! { #number => !self.#id.is_empty(), });
continue;
}
let f_features = resolve_field(ctx, field, features);
let (get_val, has_val) = if is_explicit_presence_scalar(field, ty, &f_features) {
match ty {
Type::TYPE_STRING => (
quote! { #vr::String(self.#id.as_deref().unwrap_or("")) },
quote! { self.#id.is_some() },
),
Type::TYPE_BYTES => (
quote! { #vr::Bytes(self.#id.as_deref().unwrap_or(&[])) },
quote! { self.#id.is_some() },
),
Type::TYPE_ENUM => (
quote! { #vr::EnumNumber(self.#id.map_or(0, |e| e.to_i32())) },
quote! { self.#id.is_some() },
),
_ => {
let variant = scalar_variant(ty);
let def = scalar_default(ty);
(
quote! { #vr::#variant(self.#id.unwrap_or(#def)) },
quote! { self.#id.is_some() },
)
}
}
} else {
match ty {
Type::TYPE_STRING => (
quote! { #vr::String(&self.#id) },
quote! { !self.#id.is_empty() },
),
Type::TYPE_BYTES => (
quote! { #vr::Bytes(&self.#id[..]) },
quote! { !self.#id.is_empty() },
),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => (
quote! { #vr::Message(#reflectable::reflect(&*self.#id)) },
quote! { self.#id.is_set() },
),
Type::TYPE_ENUM => {
let has_val = if is_closed_enum(&f_features) {
quote! { self.#id != ::core::default::Default::default() }
} else {
quote! { self.#id.to_i32() != 0 }
};
(quote! { #vr::EnumNumber(self.#id.to_i32()) }, has_val)
}
_ => {
let variant = scalar_variant(ty);
let has_val = match ty {
Type::TYPE_BOOL => quote! { self.#id },
Type::TYPE_FLOAT | Type::TYPE_DOUBLE => quote! { self.#id != 0.0 },
_ => quote! { self.#id != 0 },
};
(quote! { #vr::#variant(self.#id) }, has_val)
}
}
};
get_arms.push(quote! { #number => #get_val, });
has_arms.push(quote! { #number => #has_val, });
}
for (idx, oneof) in msg.oneof_decl.iter().enumerate() {
let Some(base_ident) = oneof_idents.get(&idx) else {
continue;
};
let oneof_name = oneof
.name
.as_deref()
.ok_or(CodeGenError::MissingField("oneof.name"))?;
let field_ident = make_field_ident(oneof_name);
let oneof_enum = quote! { #oneof_prefix #base_ident };
for field in msg
.field
.iter()
.filter(|f| is_real_oneof_member(f) && f.oneof_index == Some(idx as i32))
{
let name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let number = field.number.unwrap_or(0) as u32;
let variant = oneof_variant_ident(name);
let ty = effective_type(ctx, field, features);
let (active, default) = match ty {
Type::TYPE_STRING => (quote! { #vr::String(v) }, quote! { #vr::String("") }),
Type::TYPE_BYTES => (quote! { #vr::Bytes(&v[..]) }, quote! { #vr::Bytes(&[]) }),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
let owned_ty = resolve_owned_message_ty(ctx, field, current_package, nesting)?;
let borrowed = if crate::oneof::variant_boxed(
ctx,
ty,
&format!(".{proto_fqn}.{oneof_name}.{name}"),
) {
quote! { &**v }
} else {
quote! { v }
};
(
quote! { #vr::Message(#reflectable::reflect(#borrowed)) },
quote! {
#vr::Message(#reflectable::reflect(
<#owned_ty as ::buffa::DefaultInstance>::default_instance(),
))
},
)
}
Type::TYPE_ENUM => (
quote! { #vr::EnumNumber(v.to_i32()) },
quote! { #vr::EnumNumber(0) },
),
_ => {
let variant_v = scalar_variant(ty);
let def = scalar_default(ty);
(
quote! { #vr::#variant_v(*v) },
quote! { #vr::#variant_v(#def) },
)
}
};
get_arms.push(quote! {
#number => match &self.#field_ident {
::core::option::Option::Some(#oneof_enum::#variant(v)) => #active,
_ => #default,
},
});
has_arms.push(quote! {
#number => ::core::matches!(
&self.#field_ident,
::core::option::Option::Some(#oneof_enum::#variant(_))
),
});
}
}
let pool = quote! { #buffa_path::reflect::descriptor_pool() };
let unknown_fields_method = if ctx.config.preserve_unknown_fields {
quote! {
fn unknown_fields(&self) -> &::buffa::UnknownFields {
&self.__buffa_unknown_fields
}
}
} else {
quote! {}
};
Ok(quote! {
impl ::buffa_descriptor::reflect::ReflectMessage for #name_ident {
fn message_descriptor(&self) -> &::buffa_descriptor::MessageDescriptor {
#pool.message(Self::__buffa_reflect_message_index())
}
fn pool(&self) -> &::buffa::alloc::sync::Arc<::buffa_descriptor::DescriptorPool> {
#pool
}
#unknown_fields_method
fn get(&self, field: &::buffa_descriptor::FieldDescriptor) -> #vr<'_> {
#[allow(unused_imports)]
use ::buffa::Enumeration as _;
match field.number() {
#(#get_arms)*
_ => {
::core::debug_assert!(
false,
"field number {} is not a member of this message's reflect get()",
field.number(),
);
#vr::Bool(false)
}
}
}
fn has(&self, field: &::buffa_descriptor::FieldDescriptor) -> bool {
match field.number() {
#(#has_arms)*
_ => false,
}
}
fn for_each_set(
&self,
f: &mut dyn ::core::ops::FnMut(&::buffa_descriptor::FieldDescriptor, #vr<'_>),
) {
let md = ::buffa_descriptor::reflect::ReflectMessage::message_descriptor(self);
for fd in md.fields() {
if ::buffa_descriptor::reflect::ReflectMessage::has(self, fd) {
f(fd, ::buffa_descriptor::reflect::ReflectMessage::get(self, fd));
}
}
}
fn to_dynamic(&self) -> ::buffa_descriptor::reflect::DynamicMessage {
::buffa_descriptor::reflect::DynamicMessage::from_message(
self,
::buffa::alloc::sync::Arc::clone(#pool),
Self::__buffa_reflect_message_index(),
)
}
}
impl ::buffa_descriptor::reflect::ReflectElement for #name_ident {
#[inline]
fn as_value_ref(&self) -> #vr<'_> {
#vr::Message(#cow::Borrowed(self))
}
}
impl #name_ident {
#[doc(hidden)]
fn __buffa_reflect_message_index() -> ::buffa_descriptor::MessageIndex {
static IDX: ::std::sync::OnceLock<::buffa_descriptor::MessageIndex> =
::std::sync::OnceLock::new();
*IDX.get_or_init(|| {
#pool
.message_index(<Self as ::buffa::MessageName>::FULL_NAME)
.expect("generated message is registered in the embedded descriptor pool")
})
}
}
})
}
fn resolve_owned_message_ty(
ctx: &CodeGenContext,
field: &crate::generated::descriptor::FieldDescriptorProto,
current_package: &str,
nesting: usize,
) -> Result<TokenStream, CodeGenError> {
let dotted = field
.type_name
.as_deref()
.ok_or(CodeGenError::MissingField("field.type_name"))?;
let path = ctx
.rust_type_relative(dotted, current_package, nesting)
.ok_or_else(|| {
CodeGenError::Other(format!(
"owned type for oneof message '{dotted}' not resolvable"
))
})?;
Ok(rust_path_to_tokens(&path))
}