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_uses_bytes, find_map_entry_fields,
is_explicit_presence_scalar, is_packed_type, is_real_oneof_member, is_supported_field_type,
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::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! { ::bytes::Bytes::copy_from_slice(#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 direct_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 oneof_struct_fields =
oneof_view_struct_fields(ctx, msg, &view_oneof_prefix, features, &oneof_idents)?;
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 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(ctx.comment(proto_fqn));
let top_level = quote! {
#view_doc
#[derive(Clone, Debug, Default)]
pub struct #view_ident<'a> {
#(#direct_fields)*
#(#oneof_struct_fields)*
#unknown_fields_field
#phantom_field
}
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)
}
#[allow(clippy::redundant_closure, clippy::useless_conversion)]
#[allow(clippy::needless_update)]
fn to_owned_message(&self) -> #owned_path {
#[allow(unused_imports)]
use ::buffa::alloc::string::ToString as _;
#owned_path {
#(#owned_fields)*
..::core::default::Default::default()
}
}
}
#view_encode_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(),
))
}
}
};
Ok((top_level, mod_items))
}
fn view_struct_field(
scope: MessageScope<'_>,
msg: &DescriptorProto,
field: &FieldDescriptorProto,
) -> Result<Option<TokenStream>, 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(proto_comment, &tag_line);
let map_ty = view_map_type(scope, msg, field)?;
return Ok(Some(quote! {
#doc
pub #ident: #map_ty,
}));
}
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(proto_comment, &tag_line);
let rust_type = if is_repeated {
view_repeated_type(scope, field)?
} else {
view_singular_type(scope, field)?
};
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
};
Ok(Some(quote! {
#doc
pub #ident: #struct_ty,
}))
}
fn view_singular_type(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
) -> 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<&'a str> },
Type::TYPE_BYTES => quote! { ::core::option::Option<&'a [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! { &'a str }),
Type::TYPE_BYTES => Ok(quote! { &'a [u8] }),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
let view_ty = resolve_view_ty_tokens(scope, field)?;
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)),
}
}
fn view_repeated_type(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
) -> 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<'a, &'a str> }),
Type::TYPE_BYTES => Ok(quote! { ::buffa::RepeatedView<'a, &'a [u8]> }),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
let view_ty = resolve_view_ty_tokens(scope, field)?;
Ok(quote! { ::buffa::RepeatedView<'a, #view_ty> })
}
Type::TYPE_ENUM => {
let et = resolve_enum_ty(scope, field)?;
if is_closed_enum(features) {
Ok(quote! { ::buffa::RepeatedView<'a, #et> })
} else {
Ok(quote! { ::buffa::RepeatedView<'a, ::buffa::EnumValue<#et>> })
}
}
_ => {
let st = scalar_ty(ty);
Ok(quote! { ::buffa::RepeatedView<'a, #st> })
}
}
}
fn view_map_type(
scope: MessageScope<'_>,
msg: &DescriptorProto,
field: &FieldDescriptorProto,
) -> 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! { &'a str },
Type::TYPE_BYTES => quote! { &'a [u8] },
ty => scalar_ty(ty),
};
let val_ty = match effective_type_in_map_entry(ctx, val_fd, features) {
Type::TYPE_STRING => quote! { &'a str },
Type::TYPE_BYTES => quote! { &'a [u8] },
Type::TYPE_MESSAGE => {
let view_ty = resolve_view_ty_tokens(scope, val_fd)?;
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<'a, #key_ty, #val_ty> })
}
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>, 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! {}
};
out.push(quote! {
pub #field_ident: ::core::option::Option<#view_oneof_prefix #enum_ident #generics>,
});
}
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)?;
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! {}
};
Ok(quote! {
#[derive(Clone, Debug)]
pub enum #base_ident #generics {
#(#variants,)*
}
})
}
#[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)? },
_ => unreachable!(),
};
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 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)?;
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, 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 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 => quote! { self.#ident.map(|s| s.to_string()) },
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 => quote! { self.#ident.to_string() },
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_message()),
None => ::buffa::MessageField::none(),
}
}
}
_ => quote! { self.#ident },
})
}
fn repeated_to_owned(
scope: MessageScope<'_>,
ty: Type,
ident: &proc_macro2::Ident,
field_name: &str,
) -> Result<TokenStream, CodeGenError> {
let MessageScope { ctx, proto_fqn, .. } = scope;
Ok(match ty {
Type::TYPE_STRING => quote! { self.#ident.iter().map(|s| s.to_string()).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_message()).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, features, .. } = scope;
let (key_fd, val_fd) = find_map_entry_fields(msg, field)?;
let key_conv = match effective_type_in_map_entry(ctx, key_fd, features) {
Type::TYPE_STRING => quote! { k.to_string() },
Type::TYPE_BYTES => quote! { k.to_vec() },
_ => quote! { *k },
};
let val_conv = match effective_type_in_map_entry(ctx, val_fd, features) {
Type::TYPE_STRING => quote! { v.to_string() },
Type::TYPE_BYTES => quote! { v.to_vec() },
Type::TYPE_MESSAGE => {
let _owned_path = resolve_owned_path(scope, val_fd)?;
quote! { v.to_owned_message() }
}
_ => quote! { *v },
};
Ok(quote! {
self.#ident.iter().map(|(k, v)| (#key_conv, #val_conv)).collect()
})
}
fn oneof_variant_to_owned(scope: MessageScope<'_>, ty: Type, field_name: &str) -> TokenStream {
let MessageScope { ctx, proto_fqn, .. } = scope;
match ty {
Type::TYPE_STRING => quote! { v.to_string() },
Type::TYPE_BYTES => bytes_to_owned(ctx, proto_fqn, field_name, quote! { v }),
Type::TYPE_MESSAGE | Type::TYPE_GROUP => {
quote! { ::buffa::alloc::boxed::Box::new(v.to_owned_message()) }
}
_ => quote! { *v },
}
}
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 },
_ => 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))
}
fn resolve_view_ty_tokens(
scope: MessageScope<'_>,
field: &FieldDescriptorProto,
) -> Result<TokenStream, CodeGenError> {
let path = resolve_view_path(scope, field)?;
Ok(quote! { #path <'a> })
}
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")))
}