valuable-derive-0.1.1/.cargo_vcs_info.json0000644000000001550000000000100141030ustar { "git": { "sha1": "9efc29b6e58cef28f6566a47aa7e142a55fead77" }, "path_in_vcs": "valuable-derive" }valuable-derive-0.1.1/Cargo.lock0000644000000021370000000000100120600ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "valuable-derive" version = "0.1.1" dependencies = [ "proc-macro2", "quote", "syn", ] valuable-derive-0.1.1/Cargo.toml0000644000000022440000000000100121020ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.56" name = "valuable-derive" version = "0.1.1" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Macros for the `valuable` crate." readme = false keywords = [ "valuable", "serialization", "debugging", "no_std", ] categories = [ "development-tools::debugging", "encoding", ] license = "MIT" repository = "https://github.com/tokio-rs/valuable" [lib] name = "valuable_derive" path = "src/lib.rs" proc-macro = true [dependencies.proc-macro2] version = "1.0.60" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0" features = ["extra-traits"] [dev-dependencies] valuable-derive-0.1.1/Cargo.toml.orig000064400000000000000000000010741046102023000155630ustar 00000000000000[package] name = "valuable-derive" version = "0.1.1" edition = "2021" license = "MIT" rust-version = "1.56" description = "Macros for the `valuable` crate." repository = "https://github.com/tokio-rs/valuable" categories = [ "development-tools::debugging", "encoding", ] keywords = [ "valuable", "serialization", "debugging", "no_std", ] [lib] proc-macro = true [dependencies] proc-macro2 = "1.0.60" quote = "1.0" syn = { version = "2.0", features = ["extra-traits"] } [dev-dependencies] valuable = { path = "../valuable", features = ["derive"] } valuable-derive-0.1.1/src/attr.rs000064400000000000000000000257351046102023000150150ustar 00000000000000use std::{cell::RefCell, fmt::Write as _, thread}; use proc_macro2::Span; use syn::{punctuated::Punctuated, spanned::Spanned, Error, Fields, Ident, Meta}; const ATTR_NAME: &str = "valuable"; // All #[valuable] attributes. static ATTRS: &[AttrDef] = &[ // #[valuable(rename = "...")] AttrDef { name: "rename", conflicts_with: &[], position: &[ Position::Struct, Position::Enum, Position::Variant, Position::NamedField, ], style: &[MetaStyle::NameValue], }, // #[valuable(transparent)] AttrDef { name: "transparent", conflicts_with: &["rename"], position: &[ Position::Struct, // TODO: We can probably support single-variant enum that has a single field // Position::Enum, ], style: &[MetaStyle::Ident], }, // #[valuable(skip)] AttrDef { name: "skip", conflicts_with: &["rename"], position: &[ // TODO: How do we implement Enumerable::variant and Valuable::as_value if a variant is skipped? // Position::Variant, Position::NamedField, Position::UnnamedField, ], style: &[MetaStyle::Ident], }, ]; pub(crate) struct Attrs { rename: Option<(syn::MetaNameValue, syn::LitStr)>, transparent: Option, skip: Option, } impl Attrs { pub(crate) fn rename(&self, original: &Ident) -> syn::LitStr { self.rename.as_ref().map_or_else( || syn::LitStr::new(&original.to_string(), original.span()), |(_, l)| l.clone(), ) } pub(crate) fn transparent(&self) -> bool { self.transparent.is_some() } pub(crate) fn skip(&self) -> bool { self.skip.is_some() } } pub(crate) fn parse_attrs(cx: &Context, attrs: &[syn::Attribute], pos: Position) -> Attrs { let mut rename = None; let mut transparent = None; let mut skip = None; let attrs = filter_attrs(cx, attrs, pos); for (def, meta) in &attrs { macro_rules! lit_str { ($field:ident) => {{ let m = match meta { Meta::NameValue(m) => m, _ => unreachable!(), }; let lit = match &m.value { syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(l), .. }) => l, l => { cx.error(format_err!(l, "expected string literal")); continue; } }; $field = Some((m.clone(), lit.clone())); }}; } if def.late_check(cx, &attrs) { continue; } match def.name { // #[valuable(rename = "...")] "rename" => lit_str!(rename), // #[valuable(transparent)] "transparent" => transparent = Some(meta.span()), // #[valuable(skip)] "skip" => skip = Some(meta.span()), _ => unreachable!("{}", def.name), } } Attrs { rename, transparent, skip, } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum Position { // TODO: kind: struct, tuple, or unit Struct, Enum, // TODO: kind of variants: struct, tuple, or unit Variant, NamedField, UnnamedField, } impl Position { #[allow(clippy::trivially_copy_pass_by_ref)] fn as_str(&self) -> &'static str { match self { Position::Struct => "struct", Position::Enum => "enum", Position::Variant => "variant", Position::NamedField => "named field", Position::UnnamedField => "unnamed field", } } fn is_field(self) -> bool { self == Position::NamedField || self == Position::UnnamedField } } impl From<&Fields> for Position { fn from(meta: &Fields) -> Self { match meta { Fields::Named(..) => Position::NamedField, Fields::Unnamed(..) | Fields::Unit => Position::UnnamedField, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum MetaStyle { // #[attr()] Ident, // #[attr( = ...)] NameValue, // #[attr((...))] List, } impl MetaStyle { pub(crate) fn format(self, name: &str) -> String { match self { MetaStyle::Ident => name.to_owned(), MetaStyle::List => format!("{}(...)", name), MetaStyle::NameValue => format!("{} = ...", name), } } } impl From<&Meta> for MetaStyle { fn from(meta: &Meta) -> Self { match meta { Meta::Path(..) => MetaStyle::Ident, Meta::List(..) => MetaStyle::List, Meta::NameValue(..) => MetaStyle::NameValue, } } } #[derive(Debug)] struct AttrDef { name: &'static str, conflicts_with: &'static [&'static str], // allowed positions. position: &'static [Position], // allowed styles. style: &'static [MetaStyle], } impl AttrDef { /// Performs checks that can be performed without parsing other attributes, /// and returns `true` if at least one error occurs. fn early_check(&self, cx: &Context, pos: Position, meta: &Meta) -> bool { let mut has_error = false; if let Err(msg) = self.check_position(pos) { cx.error(format_err!(meta, msg)); has_error = true; } if let Err(msg) = self.check_style(meta) { cx.error(format_err!(meta, msg)); has_error = true; } has_error } fn check_position(&self, pos: Position) -> Result<(), String> { if self.position.contains(&pos) { return Ok(()); } let mut msg = format!("#[{}({})] may only be used on ", ATTR_NAME, self.name); // TODO: simplify if possible // len == 1: a // len == 2: a and b // len > 2: a, b, and c let position = if self.position.contains(&Position::NamedField) && self.position.contains(&Position::UnnamedField) { let mut position: Vec<_> = self .position .iter() .filter(|p| !p.is_field()) .map(Position::as_str) .collect(); position.push("field"); position } else { self.position.iter().map(Position::as_str).collect() }; let len = position.len(); for (i, p) in position.iter().enumerate() { if i != 0 { if len == 2 { msg.push_str(" and "); } else { msg.push_str(", "); if i == len - 1 { msg.push_str("and "); } } } msg.push_str(p); msg.push('s'); } Err(msg) } fn check_style(&self, meta: &Meta) -> Result<(), String> { let meta = MetaStyle::from(meta); if self.style.contains(&meta) { return Ok(()); } let mut msg = "expected ".to_owned(); let mut first = true; for style in self.style { if first { first = false; } else { msg.push_str(" or "); } let _ = write!(msg, "`#[{}({})]`", ATTR_NAME, style.format(self.name)); } msg.push_str(", found "); let _ = write!(msg, "`#[{}({})]`", ATTR_NAME, meta.format(self.name)); Err(msg) } /// Performs checks that can be performed after parsing all attributes in /// the same scope and parent scopes, and returns `true` if at least one /// error occurs. fn late_check(&self, cx: &Context, attrs: &[(&AttrDef, Meta)]) -> bool { let mut has_error = false; for (def, meta) in attrs { if def.name != self.name && self.conflicts_with.contains(&def.name) { let msg = format!( "#[{0}({1})] may not be used together with #[{0}({2})]", ATTR_NAME, self.name, def.name ); cx.error(format_err!(meta.path(), msg)); has_error = true; } } has_error } } #[derive(Debug)] pub(crate) struct Context { // - `None`: during checking. // - `Some(None)`: there are no errors. // - `Some(Some)`: there are errors. #[allow(clippy::option_option)] error: RefCell>>, } impl Context { pub(crate) fn error(&self, e: Error) { match self.error.borrow_mut().as_mut().unwrap() { Some(base) => base.combine(e), error @ None => *error = Some(e), } } pub(crate) fn check(self) -> Result<(), Error> { match self.error.borrow_mut().take().unwrap() { Some(e) => Err(e), None => Ok(()), } } } impl Default for Context { fn default() -> Self { Self { error: RefCell::new(Some(None)), } } } impl Drop for Context { fn drop(&mut self) { if !thread::panicking() && self.error.borrow().is_some() { panic!("context need to be checked"); } } } fn filter_attrs<'a>( cx: &'a Context, attrs: &'a [syn::Attribute], pos: Position, ) -> Vec<(&'static AttrDef, Meta)> { let mut counter = vec![0; ATTRS.len()]; attrs .iter() .filter(|attr| attr.path().is_ident(ATTR_NAME)) .filter_map(move |attr| match &attr.meta { Meta::List(list) => match list .parse_args_with(Punctuated::::parse_terminated) { Ok(list) => Some(list), Err(e) => { cx.error(e); None } }, m => { cx.error(format_err!(m, "expected `#[{}(...)]`", ATTR_NAME)); None } }) .flatten() .filter_map(move |m| match m.path().get_ident() { Some(p) => match ATTRS.iter().position(|a| p == a.name) { Some(pos) => { counter[pos] += 1; if counter[pos] == 1 { Some((&ATTRS[pos], m)) } else { cx.error(format_err!( &m, "duplicate #[{}({})] attribute", ATTR_NAME, p )); None } } None => { cx.error(format_err!(p, "unknown {} attribute `{}`", ATTR_NAME, p)); None } }, None => { cx.error(format_err!(m, "expected identifier, found path")); None } }) .filter(|(def, meta)| !def.early_check(cx, pos, meta)) .collect() } valuable-derive-0.1.1/src/error.rs000064400000000000000000000004011046102023000151530ustar 00000000000000macro_rules! format_err { ($span:expr, $msg:expr $(,)?) => { syn::Error::new_spanned(&$span as &dyn quote::ToTokens, &$msg as &dyn std::fmt::Display) }; ($span:expr, $($tt:tt)*) => { format_err!($span, format!($($tt)*)) }; } valuable-derive-0.1.1/src/expand.rs000064400000000000000000000360771046102023000153230ustar 00000000000000use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, ToTokens}; use syn::{Error, Ident, Result}; use crate::attr::{parse_attrs, Attrs, Context, Position}; pub(crate) fn derive_valuable(input: &mut syn::DeriveInput) -> TokenStream { let cx = Context::default(); match &input.data { syn::Data::Struct(data) => derive_struct(cx, input, data), syn::Data::Enum(data) => derive_enum(cx, input, data), syn::Data::Union(data) => { // It's impossible to derive union because we cannot safely reference the field. Err(Error::new( data.union_token.span, "#[derive(Valuable)] may only be used on structs and enums", )) } } .unwrap_or_else(Error::into_compile_error) } fn derive_struct( cx: Context, input: &syn::DeriveInput, data: &syn::DataStruct, ) -> Result { let struct_attrs = parse_attrs(&cx, &input.attrs, Position::Struct); let field_attrs: Vec<_> = data .fields .iter() .map(|f| parse_attrs(&cx, &f.attrs, Position::from(&data.fields))) .collect(); if struct_attrs.transparent() && data.fields.len() != 1 { cx.error(Error::new_spanned( input, format!( "#[valuable(transparent)] struct needs exactly one field, but has {}", data.fields.len() ), )) } cx.check()?; let name = &input.ident; let name_literal = struct_attrs.rename(name); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let allowed_lints = allowed_lints(); if struct_attrs.transparent() { let field = data.fields.iter().next().unwrap(); let access = field.ident.as_ref().map_or_else( || syn::Index::from(0).to_token_stream(), ToTokens::to_token_stream, ); let access = respan(quote! { &self.#access }, &field.ty); let valuable_impl = quote! { #[automatically_derived] impl #impl_generics ::valuable::Valuable for #name #ty_generics #where_clause { fn as_value(&self) -> ::valuable::Value<'_> { ::valuable::Valuable::as_value(#access) } fn visit(&self, visitor: &mut dyn ::valuable::Visit) { ::valuable::Valuable::visit(#access, visitor); } } }; return Ok(quote! { #allowed_lints const _: () = { #valuable_impl }; }); } let visit_fields; let struct_def; let mut named_fields_statics = None; match &data.fields { syn::Fields::Named(_) => { // _FIELDS let named_fields_static_name = format_ident!("{}_FIELDS", input.ident); named_fields_statics = Some(named_fields_static( &named_fields_static_name, &data.fields, &field_attrs, )); struct_def = quote! { ::valuable::StructDef::new_static( #name_literal, ::valuable::Fields::Named(#named_fields_static_name), ) }; let fields = data .fields .iter() .enumerate() .filter(|(i, _)| !field_attrs[*i].skip()) .map(|(_, field)| { let f = field.ident.as_ref(); let tokens = quote! { &self.#f }; respan(tokens, &field.ty) }); visit_fields = quote! { visitor.visit_named_fields(&::valuable::NamedValues::new( #named_fields_static_name, &[ #(::valuable::Valuable::as_value(#fields),)* ], )); } } syn::Fields::Unnamed(_) | syn::Fields::Unit => { let indices: Vec<_> = data .fields .iter() .enumerate() .filter(|(i, _)| !field_attrs[*i].skip()) .map(|(i, field)| { let index = syn::Index::from(i); let tokens = quote! { &self.#index }; respan(tokens, &field.ty) }) .collect(); let len = indices.len(); struct_def = quote! { ::valuable::StructDef::new_static( #name_literal, ::valuable::Fields::Unnamed(#len), ) }; visit_fields = quote! { visitor.visit_unnamed_fields( &[ #(::valuable::Valuable::as_value(#indices),)* ], ); }; } } let structable_impl = quote! { #[automatically_derived] impl #impl_generics ::valuable::Structable for #name #ty_generics #where_clause { fn definition(&self) -> ::valuable::StructDef<'_> { #struct_def } } }; let valuable_impl = quote! { #[automatically_derived] impl #impl_generics ::valuable::Valuable for #name #ty_generics #where_clause { fn as_value(&self) -> ::valuable::Value<'_> { ::valuable::Value::Structable(self) } fn visit(&self, visitor: &mut dyn ::valuable::Visit) { #visit_fields } } }; Ok(quote! { #allowed_lints const _: () = { #named_fields_statics #structable_impl #valuable_impl }; }) } fn derive_enum(cx: Context, input: &syn::DeriveInput, data: &syn::DataEnum) -> Result { let enum_attrs = parse_attrs(&cx, &input.attrs, Position::Enum); let variant_attrs: Vec<_> = data .variants .iter() .map(|v| parse_attrs(&cx, &v.attrs, Position::Variant)) .collect(); let field_attrs: Vec> = data .variants .iter() .map(|v| { v.fields .iter() .map(|f| parse_attrs(&cx, &f.attrs, Position::from(&v.fields))) .collect() }) .collect(); cx.check()?; let name = &input.ident; let name_literal = enum_attrs.rename(name); // _VARIANTS let variants_static_name = format_ident!("{}_VARIANTS", input.ident); // `static FIELDS: &[NamedField<'static>]` for variant with named fields let mut named_fields_statics = vec![]; let mut variant_defs = vec![]; let mut variant_fn = vec![]; let mut visit_variants = vec![]; for (variant_index, variant) in data.variants.iter().enumerate() { let variant_name = &variant.ident; let variant_name_literal = variant_attrs[variant_index].rename(variant_name); match &variant.fields { syn::Fields::Named(_) => { // __FIELDS let named_fields_static_name = format_ident!("{}_{}_FIELDS", input.ident, variant.ident); named_fields_statics.push(named_fields_static( &named_fields_static_name, &variant.fields, &field_attrs[variant_index], )); variant_defs.push(quote! { ::valuable::VariantDef::new( #variant_name_literal, ::valuable::Fields::Named(#named_fields_static_name), ), }); variant_fn.push(quote! { Self::#variant_name { .. } => { ::valuable::Variant::Static(&#variants_static_name[#variant_index]) } }); let mut fields = Vec::with_capacity(variant.fields.len()); let mut as_value = Vec::with_capacity(variant.fields.len()); for (_, field) in variant .fields .iter() .enumerate() .filter(|(i, _)| !field_attrs[variant_index][*i].skip()) { let f = field.ident.as_ref(); fields.push(f); let tokens = quote! { // HACK(taiki-e): This `&` is not actually needed to calling as_value, // but is needed to emulate multi-token span on stable Rust. &#f }; as_value.push(respan(tokens, &field.ty)); } let skipped = if fields.len() == variant.fields.len() { quote! {} } else { quote!(..) }; visit_variants.push(quote! { Self::#variant_name { #(#fields,)* #skipped } => { visitor.visit_named_fields( &::valuable::NamedValues::new( #named_fields_static_name, &[ #(::valuable::Valuable::as_value(#as_value),)* ], ), ); } }); } syn::Fields::Unnamed(_) => { variant_fn.push(quote! { Self::#variant_name(..) => { ::valuable::Variant::Static(&#variants_static_name[#variant_index]) } }); let bindings: Vec<_> = (0..variant.fields.len()) .map(|i| format_ident!("__binding_{}", i)) .collect(); let as_value: Vec<_> = bindings .iter() .zip(&variant.fields) .enumerate() .filter(|(i, _)| !field_attrs[variant_index][*i].skip()) .map(|(_, (binding, field))| { let tokens = quote! { // HACK(taiki-e): This `&` is not actually needed to calling as_value, // but is needed to emulate multi-token span on stable Rust. &#binding }; respan(tokens, &field.ty) }) .collect(); let len = as_value.len(); variant_defs.push(quote! { ::valuable::VariantDef::new( #variant_name_literal, ::valuable::Fields::Unnamed(#len), ), }); visit_variants.push(quote! { Self::#variant_name(#(#bindings),*) => { visitor.visit_unnamed_fields( &[ #(::valuable::Valuable::as_value(#as_value),)* ], ); } }); } syn::Fields::Unit => { variant_defs.push(quote! { ::valuable::VariantDef::new( #variant_name_literal, ::valuable::Fields::Unnamed(0), ), }); variant_fn.push(quote! { Self::#variant_name => { ::valuable::Variant::Static(&#variants_static_name[#variant_index]) } }); visit_variants.push(quote! { Self::#variant_name => { visitor.visit_unnamed_fields( &[], ); } }); } } } let variants_static = quote! { static #variants_static_name: &[::valuable::VariantDef<'static>] = &[ #(#variant_defs)* ]; }; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let enumerable_impl = quote! { #[automatically_derived] impl #impl_generics ::valuable::Enumerable for #name #ty_generics #where_clause { fn definition(&self) -> ::valuable::EnumDef<'_> { ::valuable::EnumDef::new_static( #name_literal, #variants_static_name, ) } fn variant(&self) -> ::valuable::Variant<'_> { match self { #(#variant_fn)* } } } }; let valuable_impl = quote! { #[automatically_derived] impl #impl_generics ::valuable::Valuable for #name #ty_generics #where_clause { fn as_value(&self) -> ::valuable::Value<'_> { ::valuable::Value::Enumerable(self) } fn visit(&self, visitor: &mut dyn ::valuable::Visit) { match self { #(#visit_variants)* } } } }; let allowed_lints = allowed_lints(); Ok(quote! { #allowed_lints const _: () = { #(#named_fields_statics)* #variants_static #enumerable_impl #valuable_impl }; }) } // `static : &[NamedField<'static>] = &[ ... ];` fn named_fields_static(name: &Ident, fields: &syn::Fields, field_attrs: &[Attrs]) -> TokenStream { debug_assert!(matches!(fields, syn::Fields::Named(..))); let named_fields = fields .iter() .enumerate() .filter(|(i, _)| !field_attrs[*i].skip()) .map(|(i, field)| { let field_name_literal = field_attrs[i].rename(field.ident.as_ref().unwrap()); quote! { ::valuable::NamedField::new(#field_name_literal), } }); quote! { static #name: &[::valuable::NamedField<'static>] = &[ #(#named_fields)* ]; } } // Returns attributes that should be applied to generated code. fn allowed_lints() -> TokenStream { quote! { #[allow(non_upper_case_globals)] #[allow(clippy::unknown_clippy_lints)] #[allow(clippy::used_underscore_binding)] #[allow(clippy::indexing_slicing)] } } fn respan(tokens: TokenStream, span: &impl ToTokens) -> TokenStream { let mut iter = span.to_token_stream().into_iter(); // `Span` on stable Rust has a limitation that only points to the first // token, not the whole tokens. We can work around this limitation by // using the first/last span of the tokens like `syn::Error::new_spanned` does. let start_span = iter.next().map_or_else(Span::call_site, |t| t.span()); let end_span = iter.last().map_or(start_span, |t| t.span()); let mut tokens = tokens.into_iter().collect::>(); if let Some(tt) = tokens.first_mut() { tt.set_span(start_span); } for tt in tokens.iter_mut().skip(1) { tt.set_span(end_span); } tokens.into_iter().collect() } valuable-derive-0.1.1/src/lib.rs000064400000000000000000000020061046102023000145730ustar 00000000000000#![warn(rust_2018_idioms, unreachable_pub)] #[macro_use] mod error; mod attr; mod expand; use proc_macro::TokenStream; use syn::parse_macro_input; /// Derive a `Valuable` implementation for a struct or enum. /// /// # Attributes /// /// ## `#[valuable(rename = "...")]` /// /// Use the given name instead of its Rust name. /// /// ## `#[valuable(transparent)]` /// /// Delegate the trait implementation to the field. /// /// This attribute can only be used on a struct that has a single field. /// /// ## `#[valuable(skip)]` /// /// Skip the field. /// /// # Examples /// /// ``` /// use valuable::Valuable; /// /// #[derive(Valuable)] /// struct HelloWorld { /// message: Message, /// } /// /// #[derive(Valuable)] /// enum Message { /// HelloWorld, /// Custom(String), /// } /// ``` #[proc_macro_derive(Valuable, attributes(valuable))] pub fn derive_valuable(input: TokenStream) -> TokenStream { let mut input = parse_macro_input!(input as syn::DeriveInput); expand::derive_valuable(&mut input).into() }