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) -> Result, 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, 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(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 { 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::(&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>, 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 { 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 }, BytesTy::Bytes => quote! { ::prost::bytes::Bytes }, } } } impl Ty { pub fn from_attr(attr: &Meta) -> Result, 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::(&l.value())?), Meta::List(ref meta_list) if meta_list.path.is_ident("enumeration") => { Ty::Enumeration(meta_list.parse_args::()?) } _ => return Ok(None), }; Ok(Some(ty)) } pub fn from_str(s: &str) -> Result { 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::(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), Enumeration(TokenStream), Path(Path), } impl DefaultValue { pub fn from_attr(attr: &Meta) -> Result, 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 { 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::( "::core::f32::INFINITY", )?)); } "-inf" => { return Ok(DefaultValue::Path(parse_str::( "::core::f32::NEG_INFINITY", )?)); } "nan" => { return Ok(DefaultValue::Path(parse_str::("::core::f32::NAN")?)); } _ => (), } } if *ty == Ty::Double { match value { "inf" => { return Ok(DefaultValue::Path(parse_str::( "::core::f64::INFINITY", )?)); } "-inf" => { return Ok(DefaultValue::Path(parse_str::( "::core::f64::NEG_INFINITY", )?)); } "nan" => { return Ok(DefaultValue::Path(parse_str::("::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::) { 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::(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), } } }