use crate::generated::descriptor::field_descriptor_proto::{Label, Type};
use crate::generated::descriptor::{DescriptorProto, FieldDescriptorProto, OneofDescriptorProto};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::context::{ancillary_prefix, AncillaryKind, CodeGenContext, MessageScope, SENTINEL_MOD};
use crate::features::ResolvedFeatures;
use crate::impl_message::{
closed_enum_decode, closed_enum_decode_with_unknown, decode_fn_token, effective_type,
effective_type_in_map_entry, field_string_repr, field_uses_bytes, find_map_entry_fields,
is_explicit_presence_scalar, is_packed_type, is_real_oneof_member, is_required_field,
is_supported_field_type, map_value_use_bytes, validated_field_number, wire_type_byte,
wire_type_check, wire_type_token,
};
use crate::message::{is_closed_enum, is_map_field, make_field_ident, rust_path_to_tokens};
use crate::oneof::{is_null_value_field, serde_helper_path};
use crate::CodeGenError;
fn closed_enum_view_unknown_route(preserve_unknown_fields: bool) -> TokenStream {
if preserve_unknown_fields {
quote! {
let __span_len = before_tag.len() - cur.len();
view.__buffa_unknown_fields.push_raw(&before_tag[..__span_len]);
}
} else {
quote! {}
}
}
fn bytes_to_owned(
ctx: &CodeGenContext,
proto_fqn: &str,
field_name: &str,
expr: TokenStream,
) -> TokenStream {
if field_uses_bytes(ctx, proto_fqn, field_name) {
quote! { ::buffa::view::bytes_from_source(__buffa_src, #expr) }
} else {
quote! { (#expr).to_vec() }
}
}
pub(crate) fn generate_view_with_nesting(
scope: MessageScope<'_>,
msg: &DescriptorProto,
rust_name: &str,
) -> Result<(TokenStream, TokenStream), CodeGenError> {
let MessageScope {
ctx,
current_package,
proto_fqn,
features,
nesting,
} = scope;
let oneof_idents = crate::oneof::resolve_oneof_idents(msg);
let view_ident = format_ident!("{}View", rust_name);
let view_depth = nesting + 2;
let view_scope = MessageScope {
nesting: view_depth,
..scope
};
let view_oneof_prefix = ancillary_prefix(
AncillaryKind::ViewOneof,
current_package,
proto_fqn,
view_depth,
);
let owned_oneof_prefix =
ancillary_prefix(AncillaryKind::Oneof, current_package, proto_fqn, view_depth);
let view_fields = msg
.field
.iter()
.filter(|f| is_supported_field_type(f.r#type.unwrap_or_default()))
.map(|f| view_struct_field(view_scope, msg, f))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect::<Vec<_>>();
let direct_fields: Vec<&TokenStream> =
view_fields.iter().map(|(tokens, _, _)| tokens).collect();
let oneof_view_fields =
oneof_view_struct_fields(ctx, msg, &view_oneof_prefix, features, &oneof_idents)?;
let oneof_struct_fields: Vec<&TokenStream> =
oneof_view_fields.iter().map(|(tokens, _)| tokens).collect();
let oneof_view_enums = msg
.oneof_decl
.iter()
.enumerate()
.map(|(idx, oneof)| generate_oneof_view_enum(scope, msg, idx, oneof, &oneof_idents))
.collect::<Result<Vec<_>, _>>()?;
let (scalar_arms, repeated_arms, oneof_arms) =
build_decode_arms(view_scope, msg, &view_oneof_prefix, &oneof_idents)?;
let owned_fields = build_to_owned_fields(
view_scope,
msg,
&view_oneof_prefix,
&owned_oneof_prefix,
&oneof_idents,
)?;
let unknown_fields_field = if ctx.config.preserve_unknown_fields {
quote! { pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, }
} else {
quote! {}
};
let view_encode_methods = crate::impl_message::build_view_encode_methods(
ctx,
msg,
ctx.config.preserve_unknown_fields,
features,
&oneof_idents,
&view_oneof_prefix,
)?;
let view_encode_impl = quote! {
impl<'a> ::buffa::ViewEncode<'a> for #view_ident<'a> {
#view_encode_methods
}
};
let message_name_impl = crate::impl_message::message_name_impl(
current_package,
proto_fqn,
"e! { <'a> },
"e! { #view_ident<'a> },
);
let serialize_impl = if ctx.config.generate_json {
crate::feature_gates::cfg_block(
generate_view_serialize(
view_scope,
msg,
&view_ident,
&view_oneof_prefix,
&oneof_idents,
)?,
ctx.config.feature_gates().json,
)
} else {
quote! {}
};
let is_map_entry = msg
.options
.as_option()
.is_some_and(|o| o.map_entry.unwrap_or(false));
let reflect_view_impls =
if ctx.config.generate_reflection && ctx.config.generate_reflection_vtable && !is_map_entry
{
crate::feature_gates::cfg_const_block(
crate::reflect_view::reflect_view_impls(
view_scope,
msg,
&view_ident,
view_depth,
&view_oneof_prefix,
&oneof_idents,
)?,
ctx.config.feature_gates().reflect,
)
} else {
quote! {}
};
let before_tag_capture = if ctx.config.preserve_unknown_fields {
quote! { let before_tag = cur; }
} else {
quote! {}
};
let unknown_field_handling = if ctx.config.preserve_unknown_fields {
quote! {
let span_len = before_tag.len() - cur.len();
view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]);
}
} else {
quote! {}
};
let phantom_field =
if message_view_has_borrowing_field(ctx, msg, features, ctx.config.preserve_unknown_fields)
{
quote! {}
} else {
quote! { #[doc(hidden)] pub __buffa_phantom: ::core::marker::PhantomData<&'a ()>, }
};
let mod_items = quote! {
#(#oneof_view_enums)*
};
let owned_path: TokenStream = {
let dotted = format!(".{proto_fqn}");
let p = ctx
.rust_type_relative(&dotted, current_package, view_depth)
.ok_or_else(|| {
CodeGenError::Other(format!(
"owned type for '{proto_fqn}' not resolvable from view tree"
))
})?;
rust_path_to_tokens(&p)
};
let view_doc =
crate::comments::doc_attrs_resolved(ctx.comment(proto_fqn), proto_fqn, &ctx.type_map);
let owned_view_wrapper = if is_map_entry {
quote! {}
} else {
crate::owned_view::generate_owned_view_wrapper(
view_scope,
msg,
rust_name,
&view_ident,
&owned_path,
&view_oneof_prefix,
&oneof_idents,
)?
};
let any_redacted = view_fields.iter().any(|(_, _, redacted)| *redacted);
let (view_debug_derive, view_debug_impl) = if any_redacted {
let placeholder = crate::message::DEBUG_REDACT_PLACEHOLDER;
let view_name_str = view_ident.to_string();
let mut debug_field_names: Vec<String> = Vec::new();
let mut debug_field_values: Vec<TokenStream> = Vec::new();
for (_, ident, redacted) in &view_fields {
debug_field_names.push(ident.to_string().trim_start_matches("r#").to_string());
debug_field_values.push(if *redacted {
quote! { &::core::format_args!(#placeholder) }
} else {
quote! { &self.#ident }
});
}
for (_, ident) in &oneof_view_fields {
debug_field_names.push(ident.to_string().trim_start_matches("r#").to_string());
debug_field_values.push(quote! { &self.#ident });
}
(
quote! { #[derive(Clone, Default)] },
quote! {
impl<'a> ::core::fmt::Debug for #view_ident<'a> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct(#view_name_str)
#(.field(#debug_field_names, #debug_field_values))*
.finish()
}
}
},
)
} else {
(quote! { #[derive(Clone, Debug, Default)] }, quote! {})
};
let top_level = quote! {
#view_doc
#view_debug_derive
pub struct #view_ident<'a> {
#(#direct_fields)*
#(#oneof_struct_fields)*
#unknown_fields_field
#phantom_field
}
#view_debug_impl
impl<'a> #view_ident<'a> {
#[doc(hidden)]
pub fn _decode_depth(
buf: &'a [u8],
depth: u32,
) -> ::core::result::Result<Self, ::buffa::DecodeError> {
let mut view = Self::default();
view._merge_into_view(buf, depth)?;
::core::result::Result::Ok(view)
}
#[doc(hidden)]
pub fn _merge_into_view(
&mut self,
buf: &'a [u8],
depth: u32,
) -> ::core::result::Result<(), ::buffa::DecodeError> {
let _ = depth;
#[allow(unused_variables)]
let view = self;
let mut cur: &'a [u8] = buf;
while !cur.is_empty() {
#before_tag_capture
let tag = ::buffa::encoding::Tag::decode(&mut cur)?;
match tag.field_number() {
#(#scalar_arms)*
#(#repeated_arms)*
#(#oneof_arms)*
_ => {
::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?;
#unknown_field_handling
}
}
}
::core::result::Result::Ok(())
}
}
impl<'a> ::buffa::MessageView<'a> for #view_ident<'a> {
type Owned = #owned_path;
fn decode_view(
buf: &'a [u8],
) -> ::core::result::Result<Self, ::buffa::DecodeError> {
Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT)
}
fn decode_view_with_limit(
buf: &'a [u8],
depth: u32,
) -> ::core::result::Result<Self, ::buffa::DecodeError> {
Self::_decode_depth(buf, depth)
}
fn to_owned_message(&self) -> #owned_path {
self.to_owned_from_source(None)
}
#[allow(clippy::useless_conversion, clippy::needless_update)]
fn to_owned_from_source(
&self,
__buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>,
) -> #owned_path {
#[allow(unused_imports)]
use ::buffa::alloc::string::ToString as _;
let _ = __buffa_src;
#owned_path {
#(#owned_fields)*
..::core::default::Default::default()
}
}
}
#view_encode_impl
#serialize_impl
#message_name_impl
impl<'v> ::buffa::DefaultViewInstance for #view_ident<'v> {
fn default_view_instance<'a>() -> &'a Self
where
Self: 'a,
{
static VALUE: ::buffa::__private::OnceBox<#view_ident<'static>>
= ::buffa::__private::OnceBox::new();
VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(
<#view_ident<'static>>::default(),
))
}
}
impl ::buffa::ViewReborrow for #view_ident<'static> {
type Reborrowed<'b> = #view_ident<'b>;
fn reborrow<'b>(this: &'b Self) -> &'b Self::Reborrowed<'b> {
this
}
}
#owned_view_wrapper
#reflect_view_impls
};
Ok((top_level, mod_items))
}
fn view_struct_field(
scope: MessageScope<'_>,
msg: &DescriptorProto,
field: &FieldDescriptorProto,
) -> Result<Option<(TokenStream, proc_macro2::Ident, bool)>, CodeGenError> {
let MessageScope { ctx, proto_fqn, .. } = scope;
if is_real_oneof_member(field) {
return Ok(None);
}
let field_name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let label = field.label.unwrap_or_default();
let is_repeated = label == Label::LABEL_REPEATED;
let field_fqn = format!("{}.{}", proto_fqn, field_name);
let proto_comment = ctx.comment(&field_fqn);
if is_repeated && is_map_field(msg, field) {
let ident = make_field_ident(field_name);
let number = field.number.unwrap_or(0);
let tag_line = format!("Field {number}: `{field_name}` (map)");
let doc = crate::comments::doc_attrs_with_tag_resolved(
proto_comment,
&tag_line,
proto_fqn,
&ctx.type_map,
);
let map_ty = view_map_type(scope, msg, field, "e! { 'a })?;
let tokens = quote! {
#doc
pub #ident: #map_ty,
};
return Ok(Some((
tokens,
ident,
crate::message::is_debug_redacted(field),
)));
}
let ident = make_field_ident(field_name);
let number = field.number.unwrap_or(0);
let tag_line = format!("Field {number}: `{field_name}`");
let doc = crate::comments::doc_attrs_with_tag_resolved(
proto_comment,
&tag_line,
proto_fqn,
&ctx.type_map,
);
let rust_type = if is_repeated {
view_repeated_type(scope, field, "e! { 'a })?
} else {
view_singular_type(scope, field, "e! { 'a })?
};
let self_fqn = format!(".{proto_fqn}");
let struct_ty = if field.type_name.as_deref() == Some(self_fqn.as_str()) {
if is_repeated {
quote! { ::buffa::RepeatedView<'a, Self> }
} else {
quote! { ::buffa::MessageFieldView<Self> }
}
} else {
rust_type
};
let tokens = quote! {
#doc
pub #ident: #struct_ty,
};
Ok(Some((
tokens,
ident,
crate::message::is_debug_redacted(field),
)))
}
pub(crate) fn view_singular_type(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
lt: &TokenStream,
) -> Result<TokenStream, CodeGenError> {
let MessageScope {
ctx,
features: parent_features,
..
} = scope;
let features = &crate::features::resolve_field(ctx, field, parent_features);
let ty = effective_type(ctx, field, features);
if is_explicit_presence_scalar(field, ty, features) {
return Ok(match ty {
Type::TYPE_STRING => quote! { ::core::option::Option<&#lt str> },
Type::TYPE_BYTES => quote! { ::core::option::Option<&#lt [u8]> },
Type::TYPE_ENUM => {
let et = resolve_enum_ty(scope, field)?;
if is_closed_enum(features) {
quote! { ::core::option::Option<#et> }
} else {
quote! { ::core::option::Option<::buffa::EnumValue<#et>> }
}
}
_ => {
let st = scalar_ty(ty);
quote! { ::core::option::Option<#st> }
}
});
}
match ty {
Type::TYPE_STRING => Ok(quote! { &#lt str }),
Type::TYPE_BYTES => Ok(quote! { &#lt [u8] }),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
let view_ty = resolve_view_ty_tokens(scope, field, lt)?;
Ok(quote! { ::buffa::MessageFieldView<#view_ty> })
}
Type::TYPE_ENUM => {
let et = resolve_enum_ty(scope, field)?;
if is_closed_enum(features) {
Ok(quote! { #et })
} else {
Ok(quote! { ::buffa::EnumValue<#et> })
}
}
_ => Ok(scalar_ty(ty)),
}
}
pub(crate) fn view_repeated_type(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
lt: &TokenStream,
) -> Result<TokenStream, CodeGenError> {
let MessageScope {
ctx,
features: parent_features,
..
} = scope;
let features = &crate::features::resolve_field(ctx, field, parent_features);
let ty = effective_type(ctx, field, features);
match ty {
Type::TYPE_STRING => Ok(quote! { ::buffa::RepeatedView<#lt, &#lt str> }),
Type::TYPE_BYTES => Ok(quote! { ::buffa::RepeatedView<#lt, &#lt [u8]> }),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
let view_ty = resolve_view_ty_tokens(scope, field, lt)?;
Ok(quote! { ::buffa::RepeatedView<#lt, #view_ty> })
}
Type::TYPE_ENUM => {
let et = resolve_enum_ty(scope, field)?;
if is_closed_enum(features) {
Ok(quote! { ::buffa::RepeatedView<#lt, #et> })
} else {
Ok(quote! { ::buffa::RepeatedView<#lt, ::buffa::EnumValue<#et>> })
}
}
_ => {
let st = scalar_ty(ty);
Ok(quote! { ::buffa::RepeatedView<#lt, #st> })
}
}
}
pub(crate) fn view_map_type(
scope: MessageScope<'_>,
msg: &DescriptorProto,
field: &FieldDescriptorProto,
lt: &TokenStream,
) -> Result<TokenStream, CodeGenError> {
let MessageScope { ctx, features, .. } = scope;
let (key_fd, val_fd) = find_map_entry_fields(msg, field)?;
let key_ty = match effective_type_in_map_entry(ctx, key_fd, features) {
Type::TYPE_STRING => quote! { &#lt str },
Type::TYPE_BYTES => quote! { &#lt [u8] },
ty => scalar_ty(ty),
};
let val_ty = match effective_type_in_map_entry(ctx, val_fd, features) {
Type::TYPE_STRING => quote! { &#lt str },
Type::TYPE_BYTES => quote! { &#lt [u8] },
Type::TYPE_MESSAGE => {
let view_ty = resolve_view_ty_tokens(scope, val_fd, lt)?;
quote! { #view_ty }
}
Type::TYPE_ENUM => {
let et = resolve_enum_ty(scope, val_fd)?;
let val_features = crate::features::resolve_field(ctx, val_fd, features);
if is_closed_enum(&val_features) {
quote! { #et }
} else {
quote! { ::buffa::EnumValue<#et> }
}
}
ty => scalar_ty(ty),
};
Ok(quote! { ::buffa::MapView<#lt, #key_ty, #val_ty> })
}
pub(crate) fn oneof_view_needs_lifetime(
ctx: &CodeGenContext,
fields: &[&FieldDescriptorProto],
features: &ResolvedFeatures,
) -> bool {
fields.iter().any(|f| {
matches!(
effective_type(ctx, f, features),
Type::TYPE_STRING | Type::TYPE_BYTES | Type::TYPE_MESSAGE | Type::TYPE_GROUP
)
})
}
fn message_view_has_borrowing_field(
ctx: &CodeGenContext,
msg: &DescriptorProto,
features: &ResolvedFeatures,
preserve_unknown_fields: bool,
) -> bool {
if preserve_unknown_fields {
return true;
}
for f in &msg.field {
if is_real_oneof_member(f) {
continue; }
if f.label.unwrap_or_default()
== crate::generated::descriptor::field_descriptor_proto::Label::LABEL_REPEATED
{
return true;
}
if matches!(
effective_type(ctx, f, features),
Type::TYPE_STRING | Type::TYPE_BYTES | Type::TYPE_MESSAGE | Type::TYPE_GROUP
) {
return true;
}
}
for (idx, _) in msg.oneof_decl.iter().enumerate() {
let fields: Vec<_> = msg
.field
.iter()
.filter(|f| is_real_oneof_member(f) && f.oneof_index == Some(idx as i32))
.collect();
if oneof_view_needs_lifetime(ctx, &fields, features) {
return true;
}
}
false
}
fn oneof_view_struct_fields(
ctx: &CodeGenContext,
msg: &DescriptorProto,
view_oneof_prefix: &TokenStream,
features: &ResolvedFeatures,
oneof_idents: &std::collections::HashMap<usize, proc_macro2::Ident>,
) -> Result<Vec<(TokenStream, proc_macro2::Ident)>, CodeGenError> {
let mut out = Vec::new();
for (idx, oneof) in msg.oneof_decl.iter().enumerate() {
let enum_ident = match oneof_idents.get(&idx) {
Some(id) => id,
None => continue,
};
let fields: Vec<_> = msg
.field
.iter()
.filter(|f| is_real_oneof_member(f) && f.oneof_index == Some(idx as i32))
.collect();
if fields.is_empty() {
continue;
}
let oneof_name = oneof
.name
.as_deref()
.ok_or(CodeGenError::MissingField("oneof.name"))?;
let field_ident = make_field_ident(oneof_name);
let generics = if oneof_view_needs_lifetime(ctx, &fields, features) {
quote! { <'a> }
} else {
quote! {}
};
let tokens = quote! {
pub #field_ident: ::core::option::Option<#view_oneof_prefix #enum_ident #generics>,
};
out.push((tokens, field_ident));
}
Ok(out)
}
fn generate_oneof_view_enum(
scope: MessageScope<'_>,
msg: &DescriptorProto,
idx: usize,
_oneof: &OneofDescriptorProto,
oneof_idents: &std::collections::HashMap<usize, proc_macro2::Ident>,
) -> Result<TokenStream, CodeGenError> {
let MessageScope { ctx, features, .. } = scope;
let base_ident = match oneof_idents.get(&idx) {
Some(id) => id,
None => return Ok(TokenStream::new()),
};
let fields: Vec<_> = msg
.field
.iter()
.filter(|f| is_real_oneof_member(f) && f.oneof_index == Some(idx as i32))
.collect();
if fields.is_empty() {
return Ok(TokenStream::new());
}
let enum_body_depth = scope.nesting + 4;
let body_scope = MessageScope {
nesting: enum_body_depth,
..scope
};
let variants = fields
.iter()
.map(|f| {
let name = f
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let variant = crate::oneof::oneof_variant_ident(name);
let ty = effective_type(ctx, f, features);
let f_features = crate::features::resolve_field(ctx, f, features);
let vty = match ty {
Type::TYPE_STRING => quote! { &'a str },
Type::TYPE_BYTES => quote! { &'a [u8] },
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
let view_ty = resolve_view_ty_tokens(body_scope, f, "e! { 'a })?;
quote! { ::buffa::alloc::boxed::Box<#view_ty> }
}
Type::TYPE_ENUM => {
let et = resolve_enum_ty(body_scope, f)?;
if is_closed_enum(&f_features) {
quote! { #et }
} else {
quote! { ::buffa::EnumValue<#et> }
}
}
_ => scalar_ty(ty),
};
Ok(quote! { #variant(#vty) })
})
.collect::<Result<Vec<_>, CodeGenError>>()?;
let generics = if oneof_view_needs_lifetime(ctx, &fields, features) {
quote! { <'a> }
} else {
quote! {}
};
let any_redacted = fields.iter().any(|f| crate::message::is_debug_redacted(f));
let (debug_derive, debug_impl) = if any_redacted {
let placeholder = crate::message::DEBUG_REDACT_PLACEHOLDER;
let arms = fields
.iter()
.map(|field| {
let name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let ident = crate::oneof::oneof_variant_ident(name);
let label = ident.to_string();
Ok(if crate::message::is_debug_redacted(field) {
quote! {
Self::#ident(_) => f
.debug_tuple(#label)
.field(&::core::format_args!(#placeholder))
.finish(),
}
} else {
quote! {
Self::#ident(value) => f.debug_tuple(#label).field(value).finish(),
}
})
})
.collect::<Result<Vec<_>, CodeGenError>>()?;
(
quote! { #[derive(Clone)] },
quote! {
impl #generics ::core::fmt::Debug for #base_ident #generics {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
#(#arms)*
}
}
}
},
)
} else {
(quote! { #[derive(Clone, Debug)] }, quote! {})
};
Ok(quote! {
#debug_derive
pub enum #base_ident #generics {
#(#variants,)*
}
#debug_impl
})
}
#[allow(clippy::type_complexity)]
fn build_decode_arms(
scope: MessageScope<'_>,
msg: &DescriptorProto,
view_oneof_prefix: &TokenStream,
oneof_idents: &std::collections::HashMap<usize, proc_macro2::Ident>,
) -> Result<(Vec<TokenStream>, Vec<TokenStream>, Vec<TokenStream>), CodeGenError> {
let scalar_fields: Vec<_> = msg
.field
.iter()
.filter(|f| {
if is_real_oneof_member(f) {
return false;
}
f.label.unwrap_or_default() != Label::LABEL_REPEATED
&& is_supported_field_type(f.r#type.unwrap_or_default())
})
.collect();
let scalar_arms = scalar_fields
.iter()
.map(|f| scalar_decode_arm(scope, f))
.collect::<Result<Vec<_>, _>>()?;
let repeated_fields: Vec<_> = msg
.field
.iter()
.filter(|f| {
f.label.unwrap_or_default() == Label::LABEL_REPEATED
&& !is_map_field(msg, f)
&& is_supported_field_type(f.r#type.unwrap_or_default())
})
.collect();
let mut repeated_arms: Vec<_> = repeated_fields
.iter()
.map(|f| repeated_decode_arm(scope, f))
.collect::<Result<Vec<_>, _>>()?;
let map_fields: Vec<_> = msg
.field
.iter()
.filter(|f| f.label.unwrap_or_default() == Label::LABEL_REPEATED && is_map_field(msg, f))
.collect();
let map_arms = map_fields
.iter()
.map(|f| map_decode_arm(scope, msg, f))
.collect::<Result<Vec<_>, _>>()?;
repeated_arms.extend(map_arms);
let mut oneof_arms: Vec<TokenStream> = Vec::new();
for (idx, oneof) in msg.oneof_decl.iter().enumerate() {
let base_ident = match oneof_idents.get(&idx) {
Some(id) => id,
None => continue,
};
let oneof_name = oneof
.name
.as_deref()
.ok_or(CodeGenError::MissingField("oneof.name"))?;
let fields: Vec<_> = msg
.field
.iter()
.filter(|f| is_real_oneof_member(f) && f.oneof_index == Some(idx as i32))
.collect();
oneof_arms.extend(oneof_decode_arms(
scope,
base_ident,
oneof_name,
&fields,
view_oneof_prefix,
)?);
}
Ok((scalar_arms, repeated_arms, oneof_arms))
}
fn scalar_decode_arm(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
) -> Result<TokenStream, CodeGenError> {
let MessageScope {
ctx,
features: parent_features,
..
} = scope;
let preserve_unknown_fields = ctx.config.preserve_unknown_fields;
let features = &crate::features::resolve_field(ctx, field, parent_features);
let field_name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let field_number = validated_field_number(field)?;
let ty = effective_type(ctx, field, features);
let ident = make_field_ident(field_name);
let wire_type = wire_type_token(ty);
let expected_byte = wire_type_byte(ty);
let wire_check = wire_type_check(field_number, &wire_type, expected_byte);
if is_explicit_presence_scalar(field, ty, features) {
let assign = match ty {
Type::TYPE_STRING => {
quote! { view.#ident = Some(::buffa::types::borrow_str(&mut cur)?); }
}
Type::TYPE_BYTES => {
quote! { view.#ident = Some(::buffa::types::borrow_bytes(&mut cur)?); }
}
Type::TYPE_ENUM => {
if is_closed_enum(features) {
let unknown_route = closed_enum_view_unknown_route(preserve_unknown_fields);
closed_enum_decode_with_unknown(
"e! { &mut cur },
quote! { view.#ident = Some(__v); },
unknown_route,
)
} else {
quote! {
view.#ident = Some(::buffa::EnumValue::from(::buffa::types::decode_int32(&mut cur)?));
}
}
}
_ => {
let dfn = decode_fn_token(ty);
quote! { view.#ident = Some(#dfn(&mut cur)?); }
}
};
return Ok(quote! { #field_number => { #wire_check #assign } });
}
let assign = match ty {
Type::TYPE_STRING => quote! { view.#ident = ::buffa::types::borrow_str(&mut cur)?; },
Type::TYPE_BYTES => quote! { view.#ident = ::buffa::types::borrow_bytes(&mut cur)?; },
Type::TYPE_ENUM => {
if is_closed_enum(features) {
let unknown_route = closed_enum_view_unknown_route(preserve_unknown_fields);
closed_enum_decode_with_unknown(
"e! { &mut cur },
quote! { view.#ident = __v; },
unknown_route,
)
} else {
quote! { view.#ident = ::buffa::EnumValue::from(::buffa::types::decode_int32(&mut cur)?); }
}
}
Type::TYPE_MESSAGE => {
let vt = resolve_view_decode_tokens(scope, field)?;
quote! {
if depth == 0 {
return Err(::buffa::DecodeError::RecursionLimitExceeded);
}
let sub = ::buffa::types::borrow_bytes(&mut cur)?;
match view.#ident.as_mut() {
Some(existing) => existing._merge_into_view(sub, depth - 1)?,
None => view.#ident = ::buffa::MessageFieldView::set(
#vt::_decode_depth(sub, depth - 1)?
),
}
}
}
Type::TYPE_GROUP => {
let vt = resolve_view_decode_tokens(scope, field)?;
quote! {
if depth == 0 {
return Err(::buffa::DecodeError::RecursionLimitExceeded);
}
let sub = ::buffa::types::borrow_group(&mut cur, #field_number, depth - 1)?;
match view.#ident.as_mut() {
Some(existing) => existing._merge_into_view(sub, depth - 1)?,
None => view.#ident = ::buffa::MessageFieldView::set(
#vt::_decode_depth(sub, depth - 1)?
),
}
}
}
_ => {
let dfn = decode_fn_token(ty);
quote! { view.#ident = #dfn(&mut cur)?; }
}
};
Ok(quote! { #field_number => { #wire_check #assign } })
}
fn repeated_decode_arm(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
) -> Result<TokenStream, CodeGenError> {
let MessageScope {
ctx,
features: parent_features,
..
} = scope;
let preserve_unknown_fields = ctx.config.preserve_unknown_fields;
let features = &crate::features::resolve_field(ctx, field, parent_features);
let field_name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let field_number = validated_field_number(field)?;
let ty = effective_type(ctx, field, features);
let ident = make_field_ident(field_name);
if ty == Type::TYPE_MESSAGE {
let ld_check = wire_type_check(
field_number,
"e! { ::buffa::encoding::WireType::LengthDelimited },
2u8,
);
let vt = resolve_view_decode_tokens(scope, field)?;
return Ok(quote! {
#field_number => {
#ld_check
if depth == 0 {
return Err(::buffa::DecodeError::RecursionLimitExceeded);
}
let sub = ::buffa::types::borrow_bytes(&mut cur)?;
view.#ident.push(#vt::_decode_depth(sub, depth - 1)?);
}
});
}
if ty == Type::TYPE_GROUP {
let sg_check = wire_type_check(
field_number,
"e! { ::buffa::encoding::WireType::StartGroup },
3u8,
);
let vt = resolve_view_decode_tokens(scope, field)?;
return Ok(quote! {
#field_number => {
#sg_check
if depth == 0 {
return Err(::buffa::DecodeError::RecursionLimitExceeded);
}
let sub = ::buffa::types::borrow_group(&mut cur, #field_number, depth - 1)?;
view.#ident.push(#vt::_decode_depth(sub, depth - 1)?);
}
});
}
if !is_packed_type(ty) {
let ld_check = wire_type_check(
field_number,
"e! { ::buffa::encoding::WireType::LengthDelimited },
2u8,
);
let borrow = match ty {
Type::TYPE_STRING => quote! { ::buffa::types::borrow_str(&mut cur)? },
Type::TYPE_BYTES => quote! { ::buffa::types::borrow_bytes(&mut cur)? },
Type::TYPE_MESSAGE
| Type::TYPE_GROUP
| Type::TYPE_ENUM
| Type::TYPE_BOOL
| Type::TYPE_INT32
| Type::TYPE_INT64
| Type::TYPE_UINT32
| Type::TYPE_UINT64
| Type::TYPE_SINT32
| Type::TYPE_SINT64
| Type::TYPE_FIXED32
| Type::TYPE_FIXED64
| Type::TYPE_SFIXED32
| Type::TYPE_SFIXED64
| Type::TYPE_FLOAT
| Type::TYPE_DOUBLE => {
unreachable!("view repeated decode arm: unhandled unpacked type {:?}", ty)
}
};
return Ok(quote! {
#field_number => {
#ld_check
view.#ident.push(#borrow);
}
});
}
let elem_wire_type = wire_type_token(ty);
let closed = is_closed_enum(features);
let push_known = quote! { view.#ident.push(__v); };
let packed_elem = if ty == Type::TYPE_ENUM {
if closed {
closed_enum_decode("e! { &mut pcur }, push_known.clone())
} else {
quote! { view.#ident.push(::buffa::EnumValue::from(::buffa::types::decode_int32(&mut pcur)?)); }
}
} else {
let dfn = decode_fn_token(ty);
quote! { view.#ident.push(#dfn(&mut pcur)?); }
};
let reserve_divisor: usize = match ty {
Type::TYPE_FIXED32 | Type::TYPE_SFIXED32 | Type::TYPE_FLOAT => 4,
Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 | Type::TYPE_DOUBLE => 8,
_ => 1,
};
let reserve_stmt = if reserve_divisor > 1 {
quote! { view.#ident.reserve(payload.len() / #reserve_divisor); }
} else {
quote! { view.#ident.reserve(payload.len()); }
};
let unpacked_elem = if ty == Type::TYPE_ENUM {
if closed {
let unknown_route = closed_enum_view_unknown_route(preserve_unknown_fields);
closed_enum_decode_with_unknown("e! { &mut cur }, push_known, unknown_route)
} else {
quote! { view.#ident.push(::buffa::EnumValue::from(::buffa::types::decode_int32(&mut cur)?)); }
}
} else {
let dfn = decode_fn_token(ty);
quote! { view.#ident.push(#dfn(&mut cur)?); }
};
Ok(quote! {
#field_number => {
if tag.wire_type() == ::buffa::encoding::WireType::LengthDelimited {
let payload = ::buffa::types::borrow_bytes(&mut cur)?;
#reserve_stmt
let mut pcur: &[u8] = payload;
while !pcur.is_empty() { #packed_elem }
} else if tag.wire_type() == #elem_wire_type {
#unpacked_elem
} else {
return Err(::buffa::DecodeError::WireTypeMismatch {
field_number: #field_number,
expected: 2u8,
actual: tag.wire_type() as u8,
});
}
}
})
}
fn map_decode_arm(
scope: MessageScope<'_>,
msg: &DescriptorProto,
field: &FieldDescriptorProto,
) -> Result<TokenStream, CodeGenError> {
let MessageScope { ctx, features, .. } = scope;
let field_name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let field_number = validated_field_number(field)?;
let ident = make_field_ident(field_name);
let (key_fd, val_fd) = find_map_entry_fields(msg, field)?;
let ld_check = wire_type_check(
field_number,
"e! { ::buffa::encoding::WireType::LengthDelimited },
2u8,
);
let key_default = match effective_type_in_map_entry(ctx, key_fd, features) {
Type::TYPE_STRING => quote! { "" },
Type::TYPE_BYTES => quote! { &[][..] },
_ => quote! { ::core::default::Default::default() },
};
let val_default = match effective_type_in_map_entry(ctx, val_fd, features) {
Type::TYPE_STRING => quote! { "" },
Type::TYPE_BYTES => quote! { &[][..] },
_ => quote! { ::core::default::Default::default() },
};
let decode_key = map_view_entry_decode(scope, key_fd, &format_ident!("key"))?;
let decode_val = map_view_entry_decode(scope, val_fd, &format_ident!("val"))?;
Ok(quote! {
#field_number => {
#ld_check
let entry_bytes = ::buffa::types::borrow_bytes(&mut cur)?;
let mut entry_cur: &'a [u8] = entry_bytes;
let mut key = #key_default;
let mut val = #val_default;
while !entry_cur.is_empty() {
let entry_tag = ::buffa::encoding::Tag::decode(&mut entry_cur)?;
match entry_tag.field_number() {
1 => { #decode_key }
2 => { #decode_val }
_ => { ::buffa::encoding::skip_field_depth(entry_tag, &mut entry_cur, depth)?; }
}
}
view.#ident.push(key, val);
}
})
}
fn map_view_entry_decode(
scope: MessageScope<'_>,
fd: &FieldDescriptorProto,
var: &proc_macro2::Ident,
) -> Result<TokenStream, CodeGenError> {
let MessageScope {
ctx,
features: parent_features,
..
} = scope;
let features = &crate::features::resolve_field(ctx, fd, parent_features);
let ty = effective_type_in_map_entry(ctx, fd, features);
let wire_type = wire_type_token(ty);
let wire_byte = wire_type_byte(ty);
let tag_check = quote! {
if entry_tag.wire_type() != #wire_type {
return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch {
field_number: entry_tag.field_number(),
expected: #wire_byte,
actual: entry_tag.wire_type() as u8,
});
}
};
let assign = match ty {
Type::TYPE_STRING => quote! { #var = ::buffa::types::borrow_str(&mut entry_cur)?; },
Type::TYPE_BYTES => quote! { #var = ::buffa::types::borrow_bytes(&mut entry_cur)?; },
Type::TYPE_ENUM => {
if is_closed_enum(features) {
closed_enum_decode("e! { &mut entry_cur }, quote! { #var = __v; })
} else {
quote! { #var = ::buffa::EnumValue::from(::buffa::types::decode_int32(&mut entry_cur)?); }
}
}
Type::TYPE_MESSAGE => {
let vt = resolve_view_decode_tokens(scope, fd)?;
quote! {
if depth == 0 {
return Err(::buffa::DecodeError::RecursionLimitExceeded);
}
let sub = ::buffa::types::borrow_bytes(&mut entry_cur)?;
#var = #vt::_decode_depth(sub, depth - 1)?;
}
}
_ => {
let dfn = decode_fn_token(ty);
quote! { #var = #dfn(&mut entry_cur)?; }
}
};
Ok(quote! { #tag_check #assign })
}
fn oneof_decode_arms(
scope: MessageScope<'_>,
base_ident: &proc_macro2::Ident,
oneof_name: &str,
fields: &[&FieldDescriptorProto],
view_oneof_prefix: &TokenStream,
) -> Result<Vec<TokenStream>, CodeGenError> {
let MessageScope { ctx, features, .. } = scope;
let preserve_unknown_fields = ctx.config.preserve_unknown_fields;
let field_ident = make_field_ident(oneof_name);
let view_enum: TokenStream = quote! { #view_oneof_prefix #base_ident };
fields
.iter()
.map(|field| {
let name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let field_number = validated_field_number(field)?;
let ty = effective_type(ctx, field, features);
let field_features = crate::features::resolve_field(ctx, field, features);
let variant = crate::oneof::oneof_variant_ident(name);
let wire_type = wire_type_token(ty);
let expected_byte = wire_type_byte(ty);
let wire_check = wire_type_check(field_number, &wire_type, expected_byte);
let value = match ty {
Type::TYPE_STRING => quote! { ::buffa::types::borrow_str(&mut cur)? },
Type::TYPE_BYTES => quote! { ::buffa::types::borrow_bytes(&mut cur)? },
Type::TYPE_MESSAGE => {
let vt = resolve_view_decode_tokens(scope, field)?;
return Ok(quote! {
#field_number => {
#wire_check
if depth == 0 {
return Err(::buffa::DecodeError::RecursionLimitExceeded);
}
let sub = ::buffa::types::borrow_bytes(&mut cur)?;
if let Some(#view_enum::#variant(ref mut existing)) = view.#field_ident {
existing._merge_into_view(sub, depth - 1)?;
} else {
view.#field_ident = Some(#view_enum::#variant(
::buffa::alloc::boxed::Box::new(
#vt::_decode_depth(sub, depth - 1)?
)
));
}
}
});
}
Type::TYPE_GROUP => {
let vt = resolve_view_decode_tokens(scope, field)?;
return Ok(quote! {
#field_number => {
#wire_check
if depth == 0 {
return Err(::buffa::DecodeError::RecursionLimitExceeded);
}
let sub = ::buffa::types::borrow_group(&mut cur, #field_number, depth - 1)?;
if let Some(#view_enum::#variant(ref mut existing)) = view.#field_ident {
existing._merge_into_view(sub, depth - 1)?;
} else {
view.#field_ident = Some(#view_enum::#variant(
::buffa::alloc::boxed::Box::new(
#vt::_decode_depth(sub, depth - 1)?
)
));
}
}
});
}
Type::TYPE_ENUM => {
if is_closed_enum(&field_features) {
let unknown_route =
closed_enum_view_unknown_route(preserve_unknown_fields);
let decode = closed_enum_decode_with_unknown(
"e! { &mut cur },
quote! { view.#field_ident = Some(#view_enum::#variant(__v)); },
unknown_route,
);
return Ok(quote! {
#field_number => {
#wire_check
#decode
}
});
}
quote! { ::buffa::EnumValue::from(::buffa::types::decode_int32(&mut cur)?) }
}
_ => {
let dfn = decode_fn_token(ty);
quote! { #dfn(&mut cur)? }
}
};
Ok(quote! {
#field_number => {
#wire_check
view.#field_ident = Some(#view_enum::#variant(#value));
}
})
})
.collect()
}
fn build_to_owned_fields(
scope: MessageScope<'_>,
msg: &DescriptorProto,
view_oneof_prefix: &TokenStream,
owned_oneof_prefix: &TokenStream,
oneof_idents: &std::collections::HashMap<usize, proc_macro2::Ident>,
) -> Result<Vec<TokenStream>, CodeGenError> {
let MessageScope { ctx, features, .. } = scope;
let preserve_unknown_fields = ctx.config.preserve_unknown_fields;
let mut out = Vec::new();
for field in &msg.field {
if is_real_oneof_member(field) {
continue;
}
let name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let ident = make_field_ident(name);
let is_repeated = field.label.unwrap_or_default() == Label::LABEL_REPEATED;
if is_repeated && is_map_field(msg, field) {
let expr = map_to_owned_expr(scope, msg, field, &ident)?;
out.push(quote! { #ident: #expr, });
continue;
}
let ty = effective_type(ctx, field, features);
let init = if is_repeated {
repeated_to_owned(scope, ty, &ident, name)
} else {
singular_to_owned(scope, field, ty, &ident, name)?
};
out.push(quote! { #ident: #init, });
}
for (idx, oneof) in msg.oneof_decl.iter().enumerate() {
let base_ident = match oneof_idents.get(&idx) {
Some(id) => id,
None => continue,
};
let oneof_name = oneof
.name
.as_deref()
.ok_or(CodeGenError::MissingField("oneof.name"))?;
let group: Vec<_> = msg
.field
.iter()
.filter(|f| is_real_oneof_member(f) && f.oneof_index == Some(idx as i32))
.collect();
if group.is_empty() {
continue;
}
let field_ident = make_field_ident(oneof_name);
let view_enum: TokenStream = quote! { #view_oneof_prefix #base_ident };
let owned_enum: TokenStream = quote! { #owned_oneof_prefix #base_ident };
let match_arms = group
.iter()
.map(|f| {
let fname = f
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let variant = crate::oneof::oneof_variant_ident(fname);
let ty = effective_type(ctx, f, features);
let conv = oneof_variant_to_owned(scope, ty, oneof_name, fname);
Ok(quote! {
#view_enum::#variant(v) => #owned_enum::#variant(#conv),
})
})
.collect::<Result<Vec<_>, CodeGenError>>()?;
out.push(quote! {
#field_ident: self.#field_ident.as_ref().map(|v| match v { #(#match_arms)* }),
});
}
if preserve_unknown_fields {
out.push(quote! {
__buffa_unknown_fields: self
.__buffa_unknown_fields
.to_owned()
.unwrap_or_default()
.into(),
});
}
Ok(out)
}
fn str_view_to_owned(
repr: crate::StringRepr,
binding: TokenStream,
double_ref: bool,
) -> TokenStream {
if repr.is_default() {
quote! { #binding.to_string() }
} else if double_ref {
quote! { ::core::convert::Into::into(*#binding) }
} else {
quote! { ::core::convert::Into::into(#binding) }
}
}
fn singular_to_owned(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
ty: Type,
ident: &proc_macro2::Ident,
field_name: &str,
) -> Result<TokenStream, CodeGenError> {
let MessageScope {
ctx,
proto_fqn,
features,
..
} = scope;
if is_explicit_presence_scalar(field, ty, features) {
return Ok(match ty {
Type::TYPE_STRING => {
if field_string_repr(ctx, proto_fqn, field_name).is_default() {
quote! { self.#ident.map(|s| s.to_string()) }
} else {
quote! { self.#ident.map(::core::convert::Into::into) }
}
}
Type::TYPE_BYTES => {
let conv = bytes_to_owned(ctx, proto_fqn, field_name, quote! { b });
quote! { self.#ident.map(|b| #conv) }
}
_ => quote! { self.#ident },
});
}
Ok(match ty {
Type::TYPE_STRING => str_view_to_owned(
field_string_repr(ctx, proto_fqn, field_name),
quote! { self.#ident },
false,
),
Type::TYPE_BYTES => bytes_to_owned(ctx, proto_fqn, field_name, quote! { self.#ident }),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
let owned_path = resolve_owned_path(scope, field)?;
let owned_ty = crate::message::rust_path_to_tokens(&owned_path);
quote! {
match self.#ident.as_option() {
Some(v) => ::buffa::MessageField::<#owned_ty>::some(
v.to_owned_from_source(__buffa_src),
),
None => ::buffa::MessageField::none(),
}
}
}
_ => quote! { self.#ident },
})
}
fn repeated_to_owned(
scope: MessageScope<'_>,
ty: Type,
ident: &proc_macro2::Ident,
field_name: &str,
) -> TokenStream {
let MessageScope { ctx, proto_fqn, .. } = scope;
match ty {
Type::TYPE_STRING => {
let conv = str_view_to_owned(
field_string_repr(ctx, proto_fqn, field_name),
quote! { s },
true,
);
quote! { self.#ident.iter().map(|s| #conv).collect() }
}
Type::TYPE_BYTES => {
let conv = bytes_to_owned(ctx, proto_fqn, field_name, quote! { b });
quote! { self.#ident.iter().map(|b| #conv).collect() }
}
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
quote! { self.#ident.iter().map(|v| v.to_owned_from_source(__buffa_src)).collect() }
}
_ => quote! { self.#ident.to_vec() },
}
}
fn map_to_owned_expr(
scope: MessageScope<'_>,
msg: &DescriptorProto,
field: &FieldDescriptorProto,
ident: &proc_macro2::Ident,
) -> Result<TokenStream, CodeGenError> {
let MessageScope {
ctx,
proto_fqn,
features,
..
} = scope;
let (key_fd, val_fd) = find_map_entry_fields(msg, field)?;
let field_name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let key_ty = effective_type_in_map_entry(ctx, key_fd, features);
let val_ty = effective_type_in_map_entry(ctx, val_fd, features);
let key_conv = match key_ty {
Type::TYPE_STRING => quote! { k.to_string() },
Type::TYPE_BYTES => quote! { k.to_vec() },
_ => quote! { *k },
};
let value_use_bytes =
map_value_use_bytes(ctx, Some(key_ty), Some(val_ty), proto_fqn, field_name);
let val_conv = match val_ty {
Type::TYPE_STRING => quote! { v.to_string() },
Type::TYPE_BYTES if value_use_bytes => {
quote! { ::buffa::view::bytes_from_source(__buffa_src, v) }
}
Type::TYPE_BYTES => quote! { v.to_vec() },
Type::TYPE_MESSAGE => {
let _owned_path = resolve_owned_path(scope, val_fd)?;
quote! { v.to_owned_from_source(__buffa_src) }
}
_ => quote! { *v },
};
Ok(quote! {
self.#ident.iter().map(|(k, v)| (#key_conv, #val_conv)).collect()
})
}
fn oneof_variant_to_owned(
scope: MessageScope<'_>,
ty: Type,
oneof_name: &str,
field_name: &str,
) -> TokenStream {
let MessageScope { ctx, proto_fqn, .. } = scope;
match ty {
Type::TYPE_STRING => {
str_view_to_owned(
field_string_repr(ctx, proto_fqn, field_name),
quote! { v },
true,
)
}
Type::TYPE_BYTES => bytes_to_owned(ctx, proto_fqn, field_name, quote! { v }),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
let owned = quote! { v.to_owned_from_source(__buffa_src) };
if crate::oneof::variant_boxed(
ctx,
ty,
&format!(".{proto_fqn}.{oneof_name}.{field_name}"),
) {
quote! { ::buffa::alloc::boxed::Box::new(#owned) }
} else {
owned
}
}
_ => quote! { *v },
}
}
fn generate_view_serialize(
scope: MessageScope<'_>,
msg: &DescriptorProto,
view_ident: &proc_macro2::Ident,
view_oneof_prefix: &TokenStream,
oneof_idents: &std::collections::HashMap<usize, proc_macro2::Ident>,
) -> Result<TokenStream, CodeGenError> {
let mut stmts: Vec<TokenStream> = Vec::new();
for field in &msg.field {
if is_real_oneof_member(field) {
continue;
}
if !is_supported_field_type(field.r#type.unwrap_or_default()) {
continue;
}
stmts.push(view_field_serialize_stmt(scope, msg, field)?);
}
for (idx, oneof) in msg.oneof_decl.iter().enumerate() {
let base_ident = match oneof_idents.get(&idx) {
Some(id) => id,
None => continue,
};
let oneof_name = oneof
.name
.as_deref()
.ok_or(CodeGenError::MissingField("oneof.name"))?;
let field_ident = make_field_ident(oneof_name);
let view_enum = quote! { #view_oneof_prefix #base_ident };
let fields: Vec<_> = msg
.field
.iter()
.filter(|f| is_real_oneof_member(f) && f.oneof_index == Some(idx as i32))
.collect();
if fields.is_empty() {
continue;
}
let arms = fields
.iter()
.map(|f| view_oneof_serialize_arm(scope, f, &view_enum))
.collect::<Result<Vec<_>, _>>()?;
stmts.push(quote! {
if let ::core::option::Option::Some(ref __ov) = self.#field_ident {
match __ov { #(#arms)* }
}
});
}
Ok(quote! {
impl<'__a> ::serde::Serialize for #view_ident<'__a> {
fn serialize<__S: ::serde::Serializer>(
&self,
__s: __S,
) -> ::core::result::Result<__S::Ok, __S::Error> {
use ::serde::ser::SerializeMap as _;
let mut __map = __s.serialize_map(::core::option::Option::None)?;
#(#stmts)*
__map.end()
}
}
})
}
fn view_field_serialize_stmt(
scope: MessageScope<'_>,
msg: &DescriptorProto,
field: &FieldDescriptorProto,
) -> Result<TokenStream, CodeGenError> {
let MessageScope {
ctx,
features: parent_features,
..
} = scope;
let f_features = crate::features::resolve_field(ctx, field, parent_features);
let field_name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let json_name = field.json_name.as_deref().unwrap_or(field_name);
let ident = make_field_ident(field_name);
let label = field.label.unwrap_or_default();
let is_repeated = label == Label::LABEL_REPEATED;
let is_required = is_required_field(field, parent_features);
let ty = effective_type(ctx, field, parent_features);
if is_repeated && is_map_field(msg, field) {
let (key_fd, val_fd) = find_map_entry_fields(msg, field)?;
let key_raw = effective_type_in_map_entry(ctx, key_fd, parent_features);
let val_raw = effective_type_in_map_entry(ctx, val_fd, parent_features);
let val_f = crate::features::resolve_field(ctx, val_fd, parent_features);
let key_ty = match key_raw {
Type::TYPE_STRING => quote! { &'__a str },
Type::TYPE_BYTES => quote! { &'__a [u8] },
kt => scalar_ty(kt),
};
let val_ty = match val_raw {
Type::TYPE_STRING => quote! { &'__a str },
Type::TYPE_BYTES => quote! { &'__a [u8] },
Type::TYPE_MESSAGE => {
let path = resolve_view_path(scope, val_fd)?;
quote! { #path <'__a> }
}
Type::TYPE_ENUM => {
let et = resolve_enum_ty(scope, val_fd)?;
if is_closed_enum(&val_f) {
quote! { #et }
} else {
quote! { ::buffa::EnumValue<#et> }
}
}
vt => scalar_ty(vt),
};
let (key_wrapper, key_expr) = match key_raw {
Type::TYPE_STRING => (quote! {}, quote! { k }),
Type::TYPE_BYTES => (
quote! {
struct _WK<'__x>(&'__x [u8]);
impl ::serde::Serialize for _WK<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::bytes::serialize(self.0, __s)
}
}
},
quote! { &_WK(k) },
),
_ => (
quote! {
struct _WK(#key_ty);
impl ::serde::Serialize for _WK {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
__s.collect_str(&self.0)
}
}
},
quote! { &_WK(*k) },
),
};
let (val_wrapper, val_expr) = match val_raw {
Type::TYPE_BYTES => (
quote! {
struct _WV<'__x>(&'__x [u8]);
impl ::serde::Serialize for _WV<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::bytes::serialize(self.0, __s)
}
}
},
quote! { &_WV(v) },
),
Type::TYPE_ENUM if is_closed_enum(&val_f) => {
let et = resolve_enum_ty(scope, val_fd)?;
(
quote! {
struct _WV(#et);
impl ::serde::Serialize for _WV {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::closed_enum::serialize(&self.0, __s)
}
}
},
quote! { &_WV(*v) },
)
}
vt if serde_helper_path(vt).is_some() => {
let helper = serde_helper_path(vt).unwrap();
(
quote! {
struct _WV(#val_ty);
impl ::serde::Serialize for _WV {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
#helper::serialize(&self.0, __s)
}
}
},
quote! { &_WV(*v) },
)
}
_ => (quote! {}, quote! { v }),
};
let key_uses_a = matches!(key_raw, Type::TYPE_STRING | Type::TYPE_BYTES);
let val_uses_a = matches!(
val_raw,
Type::TYPE_STRING | Type::TYPE_BYTES | Type::TYPE_MESSAGE
);
let (wm_struct_decl, wm_impl_hdr, wm_impl_ty) = if key_uses_a || val_uses_a {
(
quote! { struct _WM<'__a, '__x>(&'__x ::buffa::MapView<'__x, #key_ty, #val_ty>); },
quote! { impl<'__a> },
quote! { _WM<'__a, '_> },
)
} else {
(
quote! { struct _WM<'__x>(&'__x ::buffa::MapView<'__x, #key_ty, #val_ty>); },
quote! { impl },
quote! { _WM<'_> },
)
};
return Ok(quote! {
if !self.#ident.is_empty() {
#wm_struct_decl
#wm_impl_hdr ::serde::Serialize for #wm_impl_ty {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
use ::serde::ser::SerializeMap as _;
#key_wrapper
#val_wrapper
let mut __m = __s.serialize_map(::core::option::Option::Some(self.0.len()))?;
for (k, v) in self.0.iter_unique() {
__m.serialize_entry(#key_expr, #val_expr)?;
}
__m.end()
}
}
__map.serialize_entry(#json_name, &_WM(&self.#ident))?;
}
});
}
if is_repeated {
let seq_wrapper = match ty {
Type::TYPE_BYTES => quote! {
struct _WSeq<'__x>(&'__x [&'__x [u8]]);
impl ::serde::Serialize for _WSeq<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
use ::serde::ser::SerializeSeq as _;
let mut __seq = __s.serialize_seq(::core::option::Option::Some(self.0.len()))?;
for v in self.0 {
struct _WE<'__x>(&'__x [u8]);
impl ::serde::Serialize for _WE<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::bytes::serialize(self.0, __s)
}
}
__seq.serialize_element(&_WE(v))?;
}
__seq.end()
}
}
},
Type::TYPE_ENUM if is_closed_enum(&f_features) => {
let et = resolve_enum_ty(scope, field)?;
quote! {
struct _WSeq<'__x>(&'__x [#et]);
impl ::serde::Serialize for _WSeq<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::repeated_closed_enum::serialize(self.0, __s)
}
}
}
}
Type::TYPE_ENUM => {
let et = resolve_enum_ty(scope, field)?;
quote! {
struct _WSeq<'__x>(&'__x [::buffa::EnumValue<#et>]);
impl ::serde::Serialize for _WSeq<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::repeated_enum::serialize(self.0, __s)
}
}
}
}
scalar_ty_val if serde_helper_path(scalar_ty_val).is_some() => {
let elem_ty = scalar_ty(scalar_ty_val);
quote! {
struct _WSeq<'__x>(&'__x [#elem_ty]);
impl ::serde::Serialize for _WSeq<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::proto_seq::serialize(self.0, __s)
}
}
}
}
_ => quote! {},
};
let seq_val = if seq_wrapper.is_empty() {
quote! { &*self.#ident }
} else {
quote! { &_WSeq(&self.#ident) }
};
return Ok(quote! {
if !self.#ident.is_empty() {
#seq_wrapper
__map.serialize_entry(#json_name, #seq_val)?;
}
});
}
if is_explicit_presence_scalar(field, ty, &f_features) {
let entry = match ty {
Type::TYPE_STRING => quote! {
if let ::core::option::Option::Some(__v) = self.#ident {
__map.serialize_entry(#json_name, __v)?;
}
},
Type::TYPE_BYTES => quote! {
if let ::core::option::Option::Some(__v) = self.#ident {
struct _W<'__x>(&'__x [u8]);
impl ::serde::Serialize for _W<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::bytes::serialize(self.0, __s)
}
}
__map.serialize_entry(#json_name, &_W(__v))?;
}
},
Type::TYPE_ENUM if is_closed_enum(&f_features) => {
let et = resolve_enum_ty(scope, field)?;
quote! {
if let ::core::option::Option::Some(__v) = self.#ident {
struct _W(#et);
impl ::serde::Serialize for _W {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::closed_enum::serialize(&self.0, __s)
}
}
__map.serialize_entry(#json_name, &_W(__v))?;
}
}
}
Type::TYPE_ENUM => quote! {
if let ::core::option::Option::Some(ref __v) = self.#ident {
__map.serialize_entry(#json_name, __v)?;
}
},
scalar if serde_helper_path(scalar).is_some() => {
let helper = serde_helper_path(scalar).unwrap();
let elem_ty = scalar_ty(scalar);
quote! {
if let ::core::option::Option::Some(__v) = self.#ident {
struct _W(#elem_ty);
impl ::serde::Serialize for _W {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
#helper::serialize(&self.0, __s)
}
}
__map.serialize_entry(#json_name, &_W(__v))?;
}
}
}
_ => quote! {
if let ::core::option::Option::Some(__v) = self.#ident {
__map.serialize_entry(#json_name, &__v)?;
}
},
};
return Ok(entry);
}
let (skip_cond, serialize_stmt) = match ty {
Type::TYPE_STRING => (
quote! { !::buffa::json_helpers::skip_if::is_empty_str(self.#ident) },
quote! { __map.serialize_entry(#json_name, self.#ident)?; },
),
Type::TYPE_BYTES => (
quote! { !::buffa::json_helpers::skip_if::is_empty_bytes(self.#ident) },
quote! {
struct _W<'__x>(&'__x [u8]);
impl ::serde::Serialize for _W<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::bytes::serialize(self.0, __s)
}
}
__map.serialize_entry(#json_name, &_W(self.#ident))?;
},
),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => (
quote! { self.#ident.is_set() },
quote! {
if let ::core::option::Option::Some(__v) = self.#ident.as_option() {
__map.serialize_entry(#json_name, __v)?;
}
},
),
Type::TYPE_ENUM if is_closed_enum(&f_features) => {
let et = resolve_enum_ty(scope, field)?;
let skip_fn = quote! { ::buffa::json_helpers::skip_if::is_default_closed_enum };
(
quote! { !#skip_fn(&self.#ident) },
quote! {
struct _W(#et);
impl ::serde::Serialize for _W {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::closed_enum::serialize(&self.0, __s)
}
}
__map.serialize_entry(#json_name, &_W(self.#ident))?;
},
)
}
Type::TYPE_ENUM => (
quote! { !::buffa::json_helpers::skip_if::is_default_enum_value(&self.#ident) },
quote! { __map.serialize_entry(#json_name, &self.#ident)?; },
),
scalar if serde_helper_path(scalar).is_some() => {
let helper = serde_helper_path(scalar).unwrap();
let skip_path = scalar_skip_predicate(scalar);
let elem_ty = scalar_ty(scalar);
(
quote! { !#skip_path(&self.#ident) },
quote! {
struct _W(#elem_ty);
impl ::serde::Serialize for _W {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
#helper::serialize(&self.0, __s)
}
}
__map.serialize_entry(#json_name, &_W(self.#ident))?;
},
)
}
Type::TYPE_BOOL => (
quote! { self.#ident },
quote! { __map.serialize_entry(#json_name, &self.#ident)?; },
),
_ => (
quote! { self.#ident != ::core::default::Default::default() },
quote! { __map.serialize_entry(#json_name, &self.#ident)?; },
),
};
if matches!(ty, Type::TYPE_MESSAGE | Type::TYPE_GROUP) || is_required {
return Ok(quote! { { #serialize_stmt } });
}
Ok(quote! {
if #skip_cond {
#serialize_stmt
}
})
}
fn scalar_skip_predicate(ty: Type) -> TokenStream {
match ty {
Type::TYPE_BOOL => quote! { ::buffa::json_helpers::skip_if::is_false },
Type::TYPE_INT32 | Type::TYPE_SINT32 | Type::TYPE_SFIXED32 => {
quote! { ::buffa::json_helpers::skip_if::is_zero_i32 }
}
Type::TYPE_UINT32 | Type::TYPE_FIXED32 => {
quote! { ::buffa::json_helpers::skip_if::is_zero_u32 }
}
Type::TYPE_INT64 | Type::TYPE_SINT64 | Type::TYPE_SFIXED64 => {
quote! { ::buffa::json_helpers::skip_if::is_zero_i64 }
}
Type::TYPE_UINT64 | Type::TYPE_FIXED64 => {
quote! { ::buffa::json_helpers::skip_if::is_zero_u64 }
}
Type::TYPE_FLOAT => quote! { ::buffa::json_helpers::skip_if::is_zero_f32 },
Type::TYPE_DOUBLE => quote! { ::buffa::json_helpers::skip_if::is_zero_f64 },
Type::TYPE_STRING
| Type::TYPE_BYTES
| Type::TYPE_ENUM
| Type::TYPE_MESSAGE
| Type::TYPE_GROUP => {
unreachable!("scalar_skip_predicate called for non-scalar {:?}", ty)
}
}
}
fn view_oneof_serialize_arm(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
view_enum: &TokenStream,
) -> Result<TokenStream, CodeGenError> {
let MessageScope { ctx, features, .. } = scope;
let f_features = crate::features::resolve_field(ctx, field, features);
let name = field
.name
.as_deref()
.ok_or(CodeGenError::MissingField("field.name"))?;
let json_name = field.json_name.as_deref().unwrap_or(name);
let variant = crate::oneof::oneof_variant_ident(name);
let ty = effective_type(ctx, field, features);
if is_null_value_field(field) {
return Ok(quote! {
#view_enum::#variant(_) => {
__map.serialize_entry(#json_name, &())?;
}
});
}
let arm_body = match ty {
Type::TYPE_STRING => {
quote! { __map.serialize_entry(#json_name, v)?; }
}
Type::TYPE_BYTES => {
quote! {
struct _W<'__x>(&'__x [u8]);
impl ::serde::Serialize for _W<'_> {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::bytes::serialize(self.0, __s)
}
}
__map.serialize_entry(#json_name, &_W(v))?;
}
}
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
quote! { __map.serialize_entry(#json_name, v)?; }
}
Type::TYPE_ENUM if is_closed_enum(&f_features) => {
let et = resolve_enum_ty(scope, field)?;
quote! {
struct _W(#et);
impl ::serde::Serialize for _W {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
::buffa::json_helpers::closed_enum::serialize(&self.0, __s)
}
}
__map.serialize_entry(#json_name, &_W(*v))?;
}
}
Type::TYPE_ENUM => {
quote! { __map.serialize_entry(#json_name, v)?; }
}
scalar if serde_helper_path(scalar).is_some() => {
let helper = serde_helper_path(scalar).unwrap();
let sty = scalar_ty(scalar);
quote! {
struct _W(#sty);
impl ::serde::Serialize for _W {
fn serialize<__S: ::serde::Serializer>(&self, __s: __S) -> ::core::result::Result<__S::Ok, __S::Error> {
#helper::serialize(&self.0, __s)
}
}
__map.serialize_entry(#json_name, &_W(*v))?;
}
}
_ => {
quote! { __map.serialize_entry(#json_name, v)?; }
}
};
Ok(quote! {
#view_enum::#variant(v) => { #arm_body }
})
}
fn scalar_ty(ty: Type) -> TokenStream {
match ty {
Type::TYPE_DOUBLE => quote! { f64 },
Type::TYPE_FLOAT => quote! { f32 },
Type::TYPE_INT64 | Type::TYPE_SINT64 | Type::TYPE_SFIXED64 => quote! { i64 },
Type::TYPE_UINT64 | Type::TYPE_FIXED64 => quote! { u64 },
Type::TYPE_INT32 | Type::TYPE_SINT32 | Type::TYPE_SFIXED32 => quote! { i32 },
Type::TYPE_UINT32 | Type::TYPE_FIXED32 => quote! { u32 },
Type::TYPE_BOOL => quote! { bool },
Type::TYPE_STRING
| Type::TYPE_BYTES
| Type::TYPE_ENUM
| Type::TYPE_MESSAGE
| Type::TYPE_GROUP => unreachable!("scalar_ty called for non-scalar {:?}", ty),
}
}
fn resolve_enum_ty(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
) -> Result<TokenStream, CodeGenError> {
let type_name = field
.type_name
.as_deref()
.ok_or(CodeGenError::MissingField("field.type_name"))?;
let path = scope
.ctx
.rust_type_relative(type_name, scope.current_package, scope.nesting)
.ok_or_else(|| CodeGenError::Other(format!("enum type '{type_name}' not found")))?;
Ok(rust_path_to_tokens(&path))
}
pub(crate) fn resolve_view_ty_tokens(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
lt: &TokenStream,
) -> Result<TokenStream, CodeGenError> {
let path = resolve_view_path(scope, field)?;
Ok(quote! { #path <#lt> })
}
fn resolve_view_decode_tokens(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
) -> Result<TokenStream, CodeGenError> {
resolve_view_path(scope, field)
}
fn resolve_view_path(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
) -> Result<TokenStream, CodeGenError> {
let type_name = field
.type_name
.as_deref()
.ok_or(CodeGenError::MissingField("field.type_name"))?;
let split = scope
.ctx
.rust_type_relative_split(type_name, scope.current_package, scope.nesting)
.ok_or_else(|| CodeGenError::Other(format!("message type '{type_name}' not found")))?;
let to_pkg = if split.to_package.is_empty() {
TokenStream::new()
} else {
let p = rust_path_to_tokens(&split.to_package);
quote! { #p :: }
};
let sentinel = make_field_ident(SENTINEL_MOD);
let (within_prefix, last) = match split.within_package.rsplit_once("::") {
Some((prefix, last)) => {
let p = rust_path_to_tokens(prefix);
(quote! { #p :: }, last.to_string())
}
None => (TokenStream::new(), split.within_package.clone()),
};
let view_ident = make_field_ident(&format!("{last}View"));
Ok(quote! { #to_pkg #sentinel :: view :: #within_prefix #view_ident })
}
fn resolve_owned_path(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
) -> Result<String, CodeGenError> {
let type_name = field
.type_name
.as_deref()
.ok_or(CodeGenError::MissingField("field.type_name"))?;
scope
.ctx
.rust_type_relative(type_name, scope.current_package, scope.nesting)
.ok_or_else(|| CodeGenError::Other(format!("message type '{type_name}' not found")))
}