summaryrefslogtreecommitdiffstats
path: root/third_party/rust/prost-derive/src/field/scalar.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/prost-derive/src/field/scalar.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/prost-derive/src/field/scalar.rs')
-rw-r--r--third_party/rust/prost-derive/src/field/scalar.rs828
1 files changed, 828 insertions, 0 deletions
diff --git a/third_party/rust/prost-derive/src/field/scalar.rs b/third_party/rust/prost-derive/src/field/scalar.rs
new file mode 100644
index 0000000000..5a3dfb2ec3
--- /dev/null
+++ b/third_party/rust/prost-derive/src/field/scalar.rs
@@ -0,0 +1,828 @@
+use std::convert::TryFrom;
+use std::fmt;
+
+use anyhow::{anyhow, bail, Error};
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, ToTokens, TokenStreamExt};
+use syn::{parse_str, Expr, ExprLit, Ident, Index, Lit, LitByteStr, Meta, MetaNameValue, Path};
+
+use crate::field::{bool_attr, set_option, tag_attr, Label};
+
+/// A scalar protobuf field.
+#[derive(Clone)]
+pub struct Field {
+ pub ty: Ty,
+ pub kind: Kind,
+ pub tag: u32,
+}
+
+impl Field {
+ pub fn new(attrs: &[Meta], inferred_tag: Option<u32>) -> Result<Option<Field>, Error> {
+ let mut ty = None;
+ let mut label = None;
+ let mut packed = None;
+ let mut default = None;
+ let mut tag = None;
+
+ let mut unknown_attrs = Vec::new();
+
+ for attr in attrs {
+ if let Some(t) = Ty::from_attr(attr)? {
+ set_option(&mut ty, t, "duplicate type attributes")?;
+ } else if let Some(p) = bool_attr("packed", attr)? {
+ set_option(&mut packed, p, "duplicate packed attributes")?;
+ } else if let Some(t) = tag_attr(attr)? {
+ set_option(&mut tag, t, "duplicate tag attributes")?;
+ } else if let Some(l) = Label::from_attr(attr) {
+ set_option(&mut label, l, "duplicate label attributes")?;
+ } else if let Some(d) = DefaultValue::from_attr(attr)? {
+ set_option(&mut default, d, "duplicate default attributes")?;
+ } else {
+ unknown_attrs.push(attr);
+ }
+ }
+
+ let ty = match ty {
+ Some(ty) => ty,
+ None => return Ok(None),
+ };
+
+ match unknown_attrs.len() {
+ 0 => (),
+ 1 => bail!("unknown attribute: {:?}", unknown_attrs[0]),
+ _ => bail!("unknown attributes: {:?}", unknown_attrs),
+ }
+
+ let tag = match tag.or(inferred_tag) {
+ Some(tag) => tag,
+ None => bail!("missing tag attribute"),
+ };
+
+ let has_default = default.is_some();
+ let default = default.map_or_else(
+ || Ok(DefaultValue::new(&ty)),
+ |lit| DefaultValue::from_lit(&ty, lit),
+ )?;
+
+ let kind = match (label, packed, has_default) {
+ (None, Some(true), _)
+ | (Some(Label::Optional), Some(true), _)
+ | (Some(Label::Required), Some(true), _) => {
+ bail!("packed attribute may only be applied to repeated fields");
+ }
+ (Some(Label::Repeated), Some(true), _) if !ty.is_numeric() => {
+ bail!("packed attribute may only be applied to numeric types");
+ }
+ (Some(Label::Repeated), _, true) => {
+ bail!("repeated fields may not have a default value");
+ }
+
+ (None, _, _) => Kind::Plain(default),
+ (Some(Label::Optional), _, _) => Kind::Optional(default),
+ (Some(Label::Required), _, _) => Kind::Required(default),
+ (Some(Label::Repeated), packed, false) if packed.unwrap_or_else(|| ty.is_numeric()) => {
+ Kind::Packed
+ }
+ (Some(Label::Repeated), _, false) => Kind::Repeated,
+ };
+
+ Ok(Some(Field { ty, kind, tag }))
+ }
+
+ pub fn new_oneof(attrs: &[Meta]) -> Result<Option<Field>, Error> {
+ if let Some(mut field) = Field::new(attrs, None)? {
+ match field.kind {
+ Kind::Plain(default) => {
+ field.kind = Kind::Required(default);
+ Ok(Some(field))
+ }
+ Kind::Optional(..) => bail!("invalid optional attribute on oneof field"),
+ Kind::Required(..) => bail!("invalid required attribute on oneof field"),
+ Kind::Packed | Kind::Repeated => bail!("invalid repeated attribute on oneof field"),
+ }
+ } else {
+ Ok(None)
+ }
+ }
+
+ pub fn encode(&self, ident: TokenStream) -> TokenStream {
+ let module = self.ty.module();
+ let encode_fn = match self.kind {
+ Kind::Plain(..) | Kind::Optional(..) | Kind::Required(..) => quote!(encode),
+ Kind::Repeated => quote!(encode_repeated),
+ Kind::Packed => quote!(encode_packed),
+ };
+ let encode_fn = quote!(::prost::encoding::#module::#encode_fn);
+ let tag = self.tag;
+
+ match self.kind {
+ Kind::Plain(ref default) => {
+ let default = default.typed();
+ quote! {
+ if #ident != #default {
+ #encode_fn(#tag, &#ident, buf);
+ }
+ }
+ }
+ Kind::Optional(..) => quote! {
+ if let ::core::option::Option::Some(ref value) = #ident {
+ #encode_fn(#tag, value, buf);
+ }
+ },
+ Kind::Required(..) | Kind::Repeated | Kind::Packed => quote! {
+ #encode_fn(#tag, &#ident, buf);
+ },
+ }
+ }
+
+ /// Returns an expression which evaluates to the result of merging a decoded
+ /// scalar value into the field.
+ pub fn merge(&self, ident: TokenStream) -> TokenStream {
+ let module = self.ty.module();
+ let merge_fn = match self.kind {
+ Kind::Plain(..) | Kind::Optional(..) | Kind::Required(..) => quote!(merge),
+ Kind::Repeated | Kind::Packed => quote!(merge_repeated),
+ };
+ let merge_fn = quote!(::prost::encoding::#module::#merge_fn);
+
+ match self.kind {
+ Kind::Plain(..) | Kind::Required(..) | Kind::Repeated | Kind::Packed => quote! {
+ #merge_fn(wire_type, #ident, buf, ctx)
+ },
+ Kind::Optional(..) => quote! {
+ #merge_fn(wire_type,
+ #ident.get_or_insert_with(::core::default::Default::default),
+ buf,
+ ctx)
+ },
+ }
+ }
+
+ /// Returns an expression which evaluates to the encoded length of the field.
+ pub fn encoded_len(&self, ident: TokenStream) -> TokenStream {
+ let module = self.ty.module();
+ let encoded_len_fn = match self.kind {
+ Kind::Plain(..) | Kind::Optional(..) | Kind::Required(..) => quote!(encoded_len),
+ Kind::Repeated => quote!(encoded_len_repeated),
+ Kind::Packed => quote!(encoded_len_packed),
+ };
+ let encoded_len_fn = quote!(::prost::encoding::#module::#encoded_len_fn);
+ let tag = self.tag;
+
+ match self.kind {
+ Kind::Plain(ref default) => {
+ let default = default.typed();
+ quote! {
+ if #ident != #default {
+ #encoded_len_fn(#tag, &#ident)
+ } else {
+ 0
+ }
+ }
+ }
+ Kind::Optional(..) => quote! {
+ #ident.as_ref().map_or(0, |value| #encoded_len_fn(#tag, value))
+ },
+ Kind::Required(..) | Kind::Repeated | Kind::Packed => quote! {
+ #encoded_len_fn(#tag, &#ident)
+ },
+ }
+ }
+
+ pub fn clear(&self, ident: TokenStream) -> TokenStream {
+ match self.kind {
+ Kind::Plain(ref default) | Kind::Required(ref default) => {
+ let default = default.typed();
+ match self.ty {
+ Ty::String | Ty::Bytes(..) => quote!(#ident.clear()),
+ _ => quote!(#ident = #default),
+ }
+ }
+ Kind::Optional(_) => quote!(#ident = ::core::option::Option::None),
+ Kind::Repeated | Kind::Packed => quote!(#ident.clear()),
+ }
+ }
+
+ /// Returns an expression which evaluates to the default value of the field.
+ pub fn default(&self) -> TokenStream {
+ match self.kind {
+ Kind::Plain(ref value) | Kind::Required(ref value) => value.owned(),
+ Kind::Optional(_) => quote!(::core::option::Option::None),
+ Kind::Repeated | Kind::Packed => quote!(::prost::alloc::vec::Vec::new()),
+ }
+ }
+
+ /// An inner debug wrapper, around the base type.
+ fn debug_inner(&self, wrap_name: TokenStream) -> TokenStream {
+ if let Ty::Enumeration(ref ty) = self.ty {
+ quote! {
+ struct #wrap_name<'a>(&'a i32);
+ impl<'a> ::core::fmt::Debug for #wrap_name<'a> {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ let res: ::core::result::Result<#ty, _> = ::core::convert::TryFrom::try_from(*self.0);
+ match res {
+ Err(_) => ::core::fmt::Debug::fmt(&self.0, f),
+ Ok(en) => ::core::fmt::Debug::fmt(&en, f),
+ }
+ }
+ }
+ }
+ } else {
+ quote! {
+ #[allow(non_snake_case)]
+ fn #wrap_name<T>(v: T) -> T { v }
+ }
+ }
+ }
+
+ /// Returns a fragment for formatting the field `ident` in `Debug`.
+ pub fn debug(&self, wrapper_name: TokenStream) -> TokenStream {
+ let wrapper = self.debug_inner(quote!(Inner));
+ let inner_ty = self.ty.rust_type();
+ match self.kind {
+ Kind::Plain(_) | Kind::Required(_) => self.debug_inner(wrapper_name),
+ Kind::Optional(_) => quote! {
+ struct #wrapper_name<'a>(&'a ::core::option::Option<#inner_ty>);
+ impl<'a> ::core::fmt::Debug for #wrapper_name<'a> {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ #wrapper
+ ::core::fmt::Debug::fmt(&self.0.as_ref().map(Inner), f)
+ }
+ }
+ },
+ Kind::Repeated | Kind::Packed => {
+ quote! {
+ struct #wrapper_name<'a>(&'a ::prost::alloc::vec::Vec<#inner_ty>);
+ impl<'a> ::core::fmt::Debug for #wrapper_name<'a> {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ let mut vec_builder = f.debug_list();
+ for v in self.0 {
+ #wrapper
+ vec_builder.entry(&Inner(v));
+ }
+ vec_builder.finish()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// Returns methods to embed in the message.
+ pub fn methods(&self, ident: &TokenStream) -> Option<TokenStream> {
+ let mut ident_str = ident.to_string();
+ if ident_str.starts_with("r#") {
+ ident_str = ident_str[2..].to_owned();
+ }
+
+ // Prepend `get_` for getter methods of tuple structs.
+ let get = match syn::parse_str::<Index>(&ident_str) {
+ Ok(index) => {
+ let get = Ident::new(&format!("get_{}", index.index), Span::call_site());
+ quote!(#get)
+ }
+ Err(_) => quote!(#ident),
+ };
+
+ if let Ty::Enumeration(ref ty) = self.ty {
+ let set = Ident::new(&format!("set_{}", ident_str), Span::call_site());
+ let set_doc = format!("Sets `{}` to the provided enum value.", ident_str);
+ Some(match self.kind {
+ Kind::Plain(ref default) | Kind::Required(ref default) => {
+ let get_doc = format!(
+ "Returns the enum value of `{}`, \
+ or the default if the field is set to an invalid enum value.",
+ ident_str,
+ );
+ quote! {
+ #[doc=#get_doc]
+ pub fn #get(&self) -> #ty {
+ ::core::convert::TryFrom::try_from(self.#ident).unwrap_or(#default)
+ }
+
+ #[doc=#set_doc]
+ pub fn #set(&mut self, value: #ty) {
+ self.#ident = value as i32;
+ }
+ }
+ }
+ Kind::Optional(ref default) => {
+ let get_doc = format!(
+ "Returns the enum value of `{}`, \
+ or the default if the field is unset or set to an invalid enum value.",
+ ident_str,
+ );
+ quote! {
+ #[doc=#get_doc]
+ pub fn #get(&self) -> #ty {
+ self.#ident.and_then(|x| {
+ let result: ::core::result::Result<#ty, _> = ::core::convert::TryFrom::try_from(x);
+ result.ok()
+ }).unwrap_or(#default)
+ }
+
+ #[doc=#set_doc]
+ pub fn #set(&mut self, value: #ty) {
+ self.#ident = ::core::option::Option::Some(value as i32);
+ }
+ }
+ }
+ Kind::Repeated | Kind::Packed => {
+ let iter_doc = format!(
+ "Returns an iterator which yields the valid enum values contained in `{}`.",
+ ident_str,
+ );
+ let push = Ident::new(&format!("push_{}", ident_str), Span::call_site());
+ let push_doc = format!("Appends the provided enum value to `{}`.", ident_str);
+ quote! {
+ #[doc=#iter_doc]
+ pub fn #get(&self) -> ::core::iter::FilterMap<
+ ::core::iter::Cloned<::core::slice::Iter<i32>>,
+ fn(i32) -> ::core::option::Option<#ty>,
+ > {
+ self.#ident.iter().cloned().filter_map(|x| {
+ let result: ::core::result::Result<#ty, _> = ::core::convert::TryFrom::try_from(x);
+ result.ok()
+ })
+ }
+ #[doc=#push_doc]
+ pub fn #push(&mut self, value: #ty) {
+ self.#ident.push(value as i32);
+ }
+ }
+ }
+ })
+ } else if let Kind::Optional(ref default) = self.kind {
+ let ty = self.ty.rust_ref_type();
+
+ let match_some = if self.ty.is_numeric() {
+ quote!(::core::option::Option::Some(val) => val,)
+ } else {
+ quote!(::core::option::Option::Some(ref val) => &val[..],)
+ };
+
+ let get_doc = format!(
+ "Returns the value of `{0}`, or the default value if `{0}` is unset.",
+ ident_str,
+ );
+
+ Some(quote! {
+ #[doc=#get_doc]
+ pub fn #get(&self) -> #ty {
+ match self.#ident {
+ #match_some
+ ::core::option::Option::None => #default,
+ }
+ }
+ })
+ } else {
+ None
+ }
+ }
+}
+
+/// A scalar protobuf field type.
+#[derive(Clone, PartialEq, Eq)]
+pub enum Ty {
+ Double,
+ Float,
+ Int32,
+ Int64,
+ Uint32,
+ Uint64,
+ Sint32,
+ Sint64,
+ Fixed32,
+ Fixed64,
+ Sfixed32,
+ Sfixed64,
+ Bool,
+ String,
+ Bytes(BytesTy),
+ Enumeration(Path),
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum BytesTy {
+ Vec,
+ Bytes,
+}
+
+impl BytesTy {
+ fn try_from_str(s: &str) -> Result<Self, Error> {
+ match s {
+ "vec" => Ok(BytesTy::Vec),
+ "bytes" => Ok(BytesTy::Bytes),
+ _ => bail!("Invalid bytes type: {}", s),
+ }
+ }
+
+ fn rust_type(&self) -> TokenStream {
+ match self {
+ BytesTy::Vec => quote! { ::prost::alloc::vec::Vec<u8> },
+ BytesTy::Bytes => quote! { ::prost::bytes::Bytes },
+ }
+ }
+}
+
+impl Ty {
+ pub fn from_attr(attr: &Meta) -> Result<Option<Ty>, Error> {
+ let ty = match *attr {
+ Meta::Path(ref name) if name.is_ident("float") => Ty::Float,
+ Meta::Path(ref name) if name.is_ident("double") => Ty::Double,
+ Meta::Path(ref name) if name.is_ident("int32") => Ty::Int32,
+ Meta::Path(ref name) if name.is_ident("int64") => Ty::Int64,
+ Meta::Path(ref name) if name.is_ident("uint32") => Ty::Uint32,
+ Meta::Path(ref name) if name.is_ident("uint64") => Ty::Uint64,
+ Meta::Path(ref name) if name.is_ident("sint32") => Ty::Sint32,
+ Meta::Path(ref name) if name.is_ident("sint64") => Ty::Sint64,
+ Meta::Path(ref name) if name.is_ident("fixed32") => Ty::Fixed32,
+ Meta::Path(ref name) if name.is_ident("fixed64") => Ty::Fixed64,
+ Meta::Path(ref name) if name.is_ident("sfixed32") => Ty::Sfixed32,
+ Meta::Path(ref name) if name.is_ident("sfixed64") => Ty::Sfixed64,
+ Meta::Path(ref name) if name.is_ident("bool") => Ty::Bool,
+ Meta::Path(ref name) if name.is_ident("string") => Ty::String,
+ Meta::Path(ref name) if name.is_ident("bytes") => Ty::Bytes(BytesTy::Vec),
+ Meta::NameValue(MetaNameValue {
+ ref path,
+ value:
+ Expr::Lit(ExprLit {
+ lit: Lit::Str(ref l),
+ ..
+ }),
+ ..
+ }) if path.is_ident("bytes") => Ty::Bytes(BytesTy::try_from_str(&l.value())?),
+ Meta::NameValue(MetaNameValue {
+ ref path,
+ value:
+ Expr::Lit(ExprLit {
+ lit: Lit::Str(ref l),
+ ..
+ }),
+ ..
+ }) if path.is_ident("enumeration") => Ty::Enumeration(parse_str::<Path>(&l.value())?),
+ Meta::List(ref meta_list) if meta_list.path.is_ident("enumeration") => {
+ Ty::Enumeration(meta_list.parse_args::<Path>()?)
+ }
+ _ => return Ok(None),
+ };
+ Ok(Some(ty))
+ }
+
+ pub fn from_str(s: &str) -> Result<Ty, Error> {
+ let enumeration_len = "enumeration".len();
+ let error = Err(anyhow!("invalid type: {}", s));
+ let ty = match s.trim() {
+ "float" => Ty::Float,
+ "double" => Ty::Double,
+ "int32" => Ty::Int32,
+ "int64" => Ty::Int64,
+ "uint32" => Ty::Uint32,
+ "uint64" => Ty::Uint64,
+ "sint32" => Ty::Sint32,
+ "sint64" => Ty::Sint64,
+ "fixed32" => Ty::Fixed32,
+ "fixed64" => Ty::Fixed64,
+ "sfixed32" => Ty::Sfixed32,
+ "sfixed64" => Ty::Sfixed64,
+ "bool" => Ty::Bool,
+ "string" => Ty::String,
+ "bytes" => Ty::Bytes(BytesTy::Vec),
+ s if s.len() > enumeration_len && &s[..enumeration_len] == "enumeration" => {
+ let s = &s[enumeration_len..].trim();
+ match s.chars().next() {
+ Some('<') | Some('(') => (),
+ _ => return error,
+ }
+ match s.chars().next_back() {
+ Some('>') | Some(')') => (),
+ _ => return error,
+ }
+
+ Ty::Enumeration(parse_str::<Path>(s[1..s.len() - 1].trim())?)
+ }
+ _ => return error,
+ };
+ Ok(ty)
+ }
+
+ /// Returns the type as it appears in protobuf field declarations.
+ pub fn as_str(&self) -> &'static str {
+ match *self {
+ Ty::Double => "double",
+ Ty::Float => "float",
+ Ty::Int32 => "int32",
+ Ty::Int64 => "int64",
+ Ty::Uint32 => "uint32",
+ Ty::Uint64 => "uint64",
+ Ty::Sint32 => "sint32",
+ Ty::Sint64 => "sint64",
+ Ty::Fixed32 => "fixed32",
+ Ty::Fixed64 => "fixed64",
+ Ty::Sfixed32 => "sfixed32",
+ Ty::Sfixed64 => "sfixed64",
+ Ty::Bool => "bool",
+ Ty::String => "string",
+ Ty::Bytes(..) => "bytes",
+ Ty::Enumeration(..) => "enum",
+ }
+ }
+
+ // TODO: rename to 'owned_type'.
+ pub fn rust_type(&self) -> TokenStream {
+ match self {
+ Ty::String => quote!(::prost::alloc::string::String),
+ Ty::Bytes(ty) => ty.rust_type(),
+ _ => self.rust_ref_type(),
+ }
+ }
+
+ // TODO: rename to 'ref_type'
+ pub fn rust_ref_type(&self) -> TokenStream {
+ match *self {
+ Ty::Double => quote!(f64),
+ Ty::Float => quote!(f32),
+ Ty::Int32 => quote!(i32),
+ Ty::Int64 => quote!(i64),
+ Ty::Uint32 => quote!(u32),
+ Ty::Uint64 => quote!(u64),
+ Ty::Sint32 => quote!(i32),
+ Ty::Sint64 => quote!(i64),
+ Ty::Fixed32 => quote!(u32),
+ Ty::Fixed64 => quote!(u64),
+ Ty::Sfixed32 => quote!(i32),
+ Ty::Sfixed64 => quote!(i64),
+ Ty::Bool => quote!(bool),
+ Ty::String => quote!(&str),
+ Ty::Bytes(..) => quote!(&[u8]),
+ Ty::Enumeration(..) => quote!(i32),
+ }
+ }
+
+ pub fn module(&self) -> Ident {
+ match *self {
+ Ty::Enumeration(..) => Ident::new("int32", Span::call_site()),
+ _ => Ident::new(self.as_str(), Span::call_site()),
+ }
+ }
+
+ /// Returns false if the scalar type is length delimited (i.e., `string` or `bytes`).
+ pub fn is_numeric(&self) -> bool {
+ !matches!(self, Ty::String | Ty::Bytes(..))
+ }
+}
+
+impl fmt::Debug for Ty {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+impl fmt::Display for Ty {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+/// Scalar Protobuf field types.
+#[derive(Clone)]
+pub enum Kind {
+ /// A plain proto3 scalar field.
+ Plain(DefaultValue),
+ /// An optional scalar field.
+ Optional(DefaultValue),
+ /// A required proto2 scalar field.
+ Required(DefaultValue),
+ /// A repeated scalar field.
+ Repeated,
+ /// A packed repeated scalar field.
+ Packed,
+}
+
+/// Scalar Protobuf field default value.
+#[derive(Clone, Debug)]
+pub enum DefaultValue {
+ F64(f64),
+ F32(f32),
+ I32(i32),
+ I64(i64),
+ U32(u32),
+ U64(u64),
+ Bool(bool),
+ String(String),
+ Bytes(Vec<u8>),
+ Enumeration(TokenStream),
+ Path(Path),
+}
+
+impl DefaultValue {
+ pub fn from_attr(attr: &Meta) -> Result<Option<Lit>, Error> {
+ if !attr.path().is_ident("default") {
+ Ok(None)
+ } else if let Meta::NameValue(MetaNameValue {
+ value: Expr::Lit(ExprLit { ref lit, .. }),
+ ..
+ }) = *attr
+ {
+ Ok(Some(lit.clone()))
+ } else {
+ bail!("invalid default value attribute: {:?}", attr)
+ }
+ }
+
+ pub fn from_lit(ty: &Ty, lit: Lit) -> Result<DefaultValue, Error> {
+ let is_i32 = *ty == Ty::Int32 || *ty == Ty::Sint32 || *ty == Ty::Sfixed32;
+ let is_i64 = *ty == Ty::Int64 || *ty == Ty::Sint64 || *ty == Ty::Sfixed64;
+
+ let is_u32 = *ty == Ty::Uint32 || *ty == Ty::Fixed32;
+ let is_u64 = *ty == Ty::Uint64 || *ty == Ty::Fixed64;
+
+ let empty_or_is = |expected, actual: &str| expected == actual || actual.is_empty();
+
+ let default = match lit {
+ Lit::Int(ref lit) if is_i32 && empty_or_is("i32", lit.suffix()) => {
+ DefaultValue::I32(lit.base10_parse()?)
+ }
+ Lit::Int(ref lit) if is_i64 && empty_or_is("i64", lit.suffix()) => {
+ DefaultValue::I64(lit.base10_parse()?)
+ }
+ Lit::Int(ref lit) if is_u32 && empty_or_is("u32", lit.suffix()) => {
+ DefaultValue::U32(lit.base10_parse()?)
+ }
+ Lit::Int(ref lit) if is_u64 && empty_or_is("u64", lit.suffix()) => {
+ DefaultValue::U64(lit.base10_parse()?)
+ }
+
+ Lit::Float(ref lit) if *ty == Ty::Float && empty_or_is("f32", lit.suffix()) => {
+ DefaultValue::F32(lit.base10_parse()?)
+ }
+ Lit::Int(ref lit) if *ty == Ty::Float => DefaultValue::F32(lit.base10_parse()?),
+
+ Lit::Float(ref lit) if *ty == Ty::Double && empty_or_is("f64", lit.suffix()) => {
+ DefaultValue::F64(lit.base10_parse()?)
+ }
+ Lit::Int(ref lit) if *ty == Ty::Double => DefaultValue::F64(lit.base10_parse()?),
+
+ Lit::Bool(ref lit) if *ty == Ty::Bool => DefaultValue::Bool(lit.value),
+ Lit::Str(ref lit) if *ty == Ty::String => DefaultValue::String(lit.value()),
+ Lit::ByteStr(ref lit)
+ if *ty == Ty::Bytes(BytesTy::Bytes) || *ty == Ty::Bytes(BytesTy::Vec) =>
+ {
+ DefaultValue::Bytes(lit.value())
+ }
+
+ Lit::Str(ref lit) => {
+ let value = lit.value();
+ let value = value.trim();
+
+ if let Ty::Enumeration(ref path) = *ty {
+ let variant = Ident::new(value, Span::call_site());
+ return Ok(DefaultValue::Enumeration(quote!(#path::#variant)));
+ }
+
+ // Parse special floating point values.
+ if *ty == Ty::Float {
+ match value {
+ "inf" => {
+ return Ok(DefaultValue::Path(parse_str::<Path>(
+ "::core::f32::INFINITY",
+ )?));
+ }
+ "-inf" => {
+ return Ok(DefaultValue::Path(parse_str::<Path>(
+ "::core::f32::NEG_INFINITY",
+ )?));
+ }
+ "nan" => {
+ return Ok(DefaultValue::Path(parse_str::<Path>("::core::f32::NAN")?));
+ }
+ _ => (),
+ }
+ }
+ if *ty == Ty::Double {
+ match value {
+ "inf" => {
+ return Ok(DefaultValue::Path(parse_str::<Path>(
+ "::core::f64::INFINITY",
+ )?));
+ }
+ "-inf" => {
+ return Ok(DefaultValue::Path(parse_str::<Path>(
+ "::core::f64::NEG_INFINITY",
+ )?));
+ }
+ "nan" => {
+ return Ok(DefaultValue::Path(parse_str::<Path>("::core::f64::NAN")?));
+ }
+ _ => (),
+ }
+ }
+
+ // Rust doesn't have a negative literals, so they have to be parsed specially.
+ if let Some(Ok(lit)) = value.strip_prefix('-').map(syn::parse_str::<Lit>) {
+ match lit {
+ Lit::Int(ref lit) if is_i32 && empty_or_is("i32", lit.suffix()) => {
+ // Initially parse into an i64, so that i32::MIN does not overflow.
+ let value: i64 = -lit.base10_parse()?;
+ return Ok(i32::try_from(value).map(DefaultValue::I32)?);
+ }
+ Lit::Int(ref lit) if is_i64 && empty_or_is("i64", lit.suffix()) => {
+ // Initially parse into an i128, so that i64::MIN does not overflow.
+ let value: i128 = -lit.base10_parse()?;
+ return Ok(i64::try_from(value).map(DefaultValue::I64)?);
+ }
+ Lit::Float(ref lit)
+ if *ty == Ty::Float && empty_or_is("f32", lit.suffix()) =>
+ {
+ return Ok(DefaultValue::F32(-lit.base10_parse()?));
+ }
+ Lit::Float(ref lit)
+ if *ty == Ty::Double && empty_or_is("f64", lit.suffix()) =>
+ {
+ return Ok(DefaultValue::F64(-lit.base10_parse()?));
+ }
+ Lit::Int(ref lit) if *ty == Ty::Float && lit.suffix().is_empty() => {
+ return Ok(DefaultValue::F32(-lit.base10_parse()?));
+ }
+ Lit::Int(ref lit) if *ty == Ty::Double && lit.suffix().is_empty() => {
+ return Ok(DefaultValue::F64(-lit.base10_parse()?));
+ }
+ _ => (),
+ }
+ }
+ match syn::parse_str::<Lit>(value) {
+ Ok(Lit::Str(_)) => (),
+ Ok(lit) => return DefaultValue::from_lit(ty, lit),
+ _ => (),
+ }
+ bail!("invalid default value: {}", quote!(#value));
+ }
+ _ => bail!("invalid default value: {}", quote!(#lit)),
+ };
+
+ Ok(default)
+ }
+
+ pub fn new(ty: &Ty) -> DefaultValue {
+ match *ty {
+ Ty::Float => DefaultValue::F32(0.0),
+ Ty::Double => DefaultValue::F64(0.0),
+ Ty::Int32 | Ty::Sint32 | Ty::Sfixed32 => DefaultValue::I32(0),
+ Ty::Int64 | Ty::Sint64 | Ty::Sfixed64 => DefaultValue::I64(0),
+ Ty::Uint32 | Ty::Fixed32 => DefaultValue::U32(0),
+ Ty::Uint64 | Ty::Fixed64 => DefaultValue::U64(0),
+
+ Ty::Bool => DefaultValue::Bool(false),
+ Ty::String => DefaultValue::String(String::new()),
+ Ty::Bytes(..) => DefaultValue::Bytes(Vec::new()),
+ Ty::Enumeration(ref path) => DefaultValue::Enumeration(quote!(#path::default())),
+ }
+ }
+
+ pub fn owned(&self) -> TokenStream {
+ match *self {
+ DefaultValue::String(ref value) if value.is_empty() => {
+ quote!(::prost::alloc::string::String::new())
+ }
+ DefaultValue::String(ref value) => quote!(#value.into()),
+ DefaultValue::Bytes(ref value) if value.is_empty() => {
+ quote!(::core::default::Default::default())
+ }
+ DefaultValue::Bytes(ref value) => {
+ let lit = LitByteStr::new(value, Span::call_site());
+ quote!(#lit.as_ref().into())
+ }
+
+ ref other => other.typed(),
+ }
+ }
+
+ pub fn typed(&self) -> TokenStream {
+ if let DefaultValue::Enumeration(_) = *self {
+ quote!(#self as i32)
+ } else {
+ quote!(#self)
+ }
+ }
+}
+
+impl ToTokens for DefaultValue {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ match *self {
+ DefaultValue::F64(value) => value.to_tokens(tokens),
+ DefaultValue::F32(value) => value.to_tokens(tokens),
+ DefaultValue::I32(value) => value.to_tokens(tokens),
+ DefaultValue::I64(value) => value.to_tokens(tokens),
+ DefaultValue::U32(value) => value.to_tokens(tokens),
+ DefaultValue::U64(value) => value.to_tokens(tokens),
+ DefaultValue::Bool(value) => value.to_tokens(tokens),
+ DefaultValue::String(ref value) => value.to_tokens(tokens),
+ DefaultValue::Bytes(ref value) => {
+ let byte_str = LitByteStr::new(value, Span::call_site());
+ tokens.append_all(quote!(#byte_str as &[u8]));
+ }
+ DefaultValue::Enumeration(ref value) => value.to_tokens(tokens),
+ DefaultValue::Path(ref value) => value.to_tokens(tokens),
+ }
+ }
+}