use std::convert::TryFrom; use crate::{Literal, err::{InvalidToken, TokenKind}}; /// Helper macro to call a `callback` macro four times for all combinations of /// `proc_macro`/`proc_macro2` and `&`/owned. macro_rules! helper { ($callback:ident, $($input:tt)*) => { $callback!([proc_macro::] => $($input)*); $callback!([&proc_macro::] => $($input)*); #[cfg(feature = "proc-macro2")] $callback!([proc_macro2::] => $($input)*); #[cfg(feature = "proc-macro2")] $callback!([&proc_macro2::] => $($input)*); }; } /// Like `helper!` but without reference types. macro_rules! helper_no_refs { ($callback:ident, $($input:tt)*) => { $callback!([proc_macro::] => $($input)*); #[cfg(feature = "proc-macro2")] $callback!([proc_macro2::] => $($input)*); }; } // ============================================================================================== // ===== `From<*Lit> for Literal` // ============================================================================================== macro_rules! impl_specific_lit_to_lit { ($ty:ty, $variant:ident) => { impl From<$ty> for Literal { fn from(src: $ty) -> Self { Literal::$variant(src) } } }; } impl_specific_lit_to_lit!(crate::BoolLit, Bool); impl_specific_lit_to_lit!(crate::IntegerLit, Integer); impl_specific_lit_to_lit!(crate::FloatLit, Float); impl_specific_lit_to_lit!(crate::CharLit, Char); impl_specific_lit_to_lit!(crate::StringLit, String); impl_specific_lit_to_lit!(crate::ByteLit, Byte); impl_specific_lit_to_lit!(crate::ByteStringLit, ByteString); // ============================================================================================== // ===== `From for Literal` // ============================================================================================== macro_rules! impl_tt_to_lit { ([$($prefix:tt)*] => ) => { impl From<$($prefix)* Literal> for Literal { fn from(src: $($prefix)* Literal) -> Self { // We call `expect` in all these impls: this library aims to implement exactly // the Rust grammar, so if we have a valid Rust literal, we should always be // able to parse it. Self::parse(src.to_string()) .expect("bug: failed to parse output of `Literal::to_string`") } } } } helper!(impl_tt_to_lit, ); // ============================================================================================== // ===== `TryFrom for Literal` // ============================================================================================== macro_rules! impl_tt_to_lit { ([$($prefix:tt)*] => ) => { impl TryFrom<$($prefix)* TokenTree> for Literal { type Error = InvalidToken; fn try_from(tt: $($prefix)* TokenTree) -> Result { let span = tt.span(); let res = match tt { $($prefix)* TokenTree::Group(_) => Err(TokenKind::Group), $($prefix)* TokenTree::Punct(_) => Err(TokenKind::Punct), $($prefix)* TokenTree::Ident(ref ident) if ident.to_string() == "true" => return Ok(Literal::Bool(crate::BoolLit::True)), $($prefix)* TokenTree::Ident(ref ident) if ident.to_string() == "false" => return Ok(Literal::Bool(crate::BoolLit::False)), $($prefix)* TokenTree::Ident(_) => Err(TokenKind::Ident), $($prefix)* TokenTree::Literal(ref lit) => Ok(lit), }; match res { Ok(lit) => Ok(From::from(lit)), Err(actual) => Err(InvalidToken { actual, expected: TokenKind::Literal, span: span.into(), }), } } } } } helper!(impl_tt_to_lit, ); // ============================================================================================== // ===== `TryFrom`, `TryFrom` for non-bool `*Lit` // ============================================================================================== fn kind_of(lit: &Literal) -> TokenKind { match lit { Literal::String(_) => TokenKind::StringLit, Literal::Bool(_) => TokenKind::BoolLit, Literal::Integer(_) => TokenKind::IntegerLit, Literal::Float(_) => TokenKind::FloatLit, Literal::Char(_) => TokenKind::CharLit, Literal::Byte(_) => TokenKind::ByteLit, Literal::ByteString(_) => TokenKind::ByteStringLit, } } macro_rules! impl_for_specific_lit { ([$($prefix:tt)*] => $ty:ty, $variant:ident, $kind:ident) => { impl TryFrom<$($prefix)* Literal> for $ty { type Error = InvalidToken; fn try_from(src: $($prefix)* Literal) -> Result { let span = src.span(); let lit: Literal = src.into(); match lit { Literal::$variant(s) => Ok(s), other => Err(InvalidToken { expected: TokenKind::$kind, actual: kind_of(&other), span: span.into(), }), } } } impl TryFrom<$($prefix)* TokenTree> for $ty { type Error = InvalidToken; fn try_from(tt: $($prefix)* TokenTree) -> Result { let span = tt.span(); let res = match tt { $($prefix)* TokenTree::Group(_) => Err(TokenKind::Group), $($prefix)* TokenTree::Punct(_) => Err(TokenKind::Punct), $($prefix)* TokenTree::Ident(_) => Err(TokenKind::Ident), $($prefix)* TokenTree::Literal(ref lit) => Ok(lit), }; match res { Ok(lit) => <$ty>::try_from(lit), Err(actual) => Err(InvalidToken { actual, expected: TokenKind::$kind, span: span.into(), }), } } } }; } helper!(impl_for_specific_lit, crate::IntegerLit, Integer, IntegerLit); helper!(impl_for_specific_lit, crate::FloatLit, Float, FloatLit); helper!(impl_for_specific_lit, crate::CharLit, Char, CharLit); helper!(impl_for_specific_lit, crate::StringLit, String, StringLit); helper!(impl_for_specific_lit, crate::ByteLit, Byte, ByteLit); helper!(impl_for_specific_lit, crate::ByteStringLit, ByteString, ByteStringLit); // ============================================================================================== // ===== `From<*Lit> for pm::Literal` // ============================================================================================== macro_rules! impl_specific_lit_to_pm_lit { ([$($prefix:tt)*] => $ty:ident, $variant:ident, $kind:ident) => { impl From> for $($prefix)* Literal { fn from(l: crate::$ty) -> Self { // This should never fail: an input that is parsed successfuly // as one of our literal types should always parse as a // proc_macro literal as well! l.raw_input().parse().unwrap_or_else(|e| { panic!( "failed to parse `{}` as `{}`: {}", l.raw_input(), std::any::type_name::(), e, ) }) } } }; } helper_no_refs!(impl_specific_lit_to_pm_lit, IntegerLit, Integer, IntegerLit); helper_no_refs!(impl_specific_lit_to_pm_lit, FloatLit, Float, FloatLit); helper_no_refs!(impl_specific_lit_to_pm_lit, CharLit, Char, CharLit); helper_no_refs!(impl_specific_lit_to_pm_lit, StringLit, String, StringLit); helper_no_refs!(impl_specific_lit_to_pm_lit, ByteLit, Byte, ByteLit); helper_no_refs!(impl_specific_lit_to_pm_lit, ByteStringLit, ByteString, ByteStringLit); // ============================================================================================== // ===== `TryFrom for BoolLit` // ============================================================================================== macro_rules! impl_from_tt_for_bool { ([$($prefix:tt)*] => ) => { impl TryFrom<$($prefix)* TokenTree> for crate::BoolLit { type Error = InvalidToken; fn try_from(tt: $($prefix)* TokenTree) -> Result { let span = tt.span(); let actual = match tt { $($prefix)* TokenTree::Ident(ref ident) if ident.to_string() == "true" => return Ok(crate::BoolLit::True), $($prefix)* TokenTree::Ident(ref ident) if ident.to_string() == "false" => return Ok(crate::BoolLit::False), $($prefix)* TokenTree::Group(_) => TokenKind::Group, $($prefix)* TokenTree::Punct(_) => TokenKind::Punct, $($prefix)* TokenTree::Ident(_) => TokenKind::Ident, $($prefix)* TokenTree::Literal(ref lit) => kind_of(&Literal::from(lit)), }; Err(InvalidToken { actual, expected: TokenKind::BoolLit, span: span.into(), }) } } }; } helper!(impl_from_tt_for_bool, ); // ============================================================================================== // ===== `From for pm::Ident` // ============================================================================================== macro_rules! impl_bool_lit_to_pm_lit { ([$($prefix:tt)*] => ) => { impl From for $($prefix)* Ident { fn from(l: crate::BoolLit) -> Self { Self::new(l.as_str(), $($prefix)* Span::call_site()) } } }; } helper_no_refs!(impl_bool_lit_to_pm_lit, ); mod tests { //! # Tests //! //! ```no_run //! extern crate proc_macro; //! //! use std::convert::TryFrom; //! use litrs::Literal; //! //! fn give() -> T { //! panic!() //! } //! //! let _ = litrs::Literal::::from(give::()); //! let _ = litrs::Literal::::from(give::>()); //! let _ = litrs::Literal::::from(give::>()); //! let _ = litrs::Literal::::from(give::>()); //! let _ = litrs::Literal::::from(give::>()); //! let _ = litrs::Literal::::from(give::>()); //! let _ = litrs::Literal::::from(give::>()); //! //! let _ = litrs::Literal::<&'static str>::from(give::()); //! let _ = litrs::Literal::<&'static str>::from(give::>()); //! let _ = litrs::Literal::<&'static str>::from(give::>()); //! let _ = litrs::Literal::<&'static str>::from(give::>()); //! let _ = litrs::Literal::<&'static str>::from(give::>()); //! let _ = litrs::Literal::<&'static str>::from(give::>()); //! let _ = litrs::Literal::<&'static str>::from(give::>()); //! //! //! let _ = litrs::Literal::from(give::()); //! let _ = litrs::Literal::from(give::<&proc_macro::Literal>()); //! //! let _ = litrs::Literal::try_from(give::()); //! let _ = litrs::Literal::try_from(give::<&proc_macro::TokenTree>()); //! //! //! let _ = litrs::IntegerLit::try_from(give::()); //! let _ = litrs::IntegerLit::try_from(give::<&proc_macro::Literal>()); //! //! let _ = litrs::FloatLit::try_from(give::()); //! let _ = litrs::FloatLit::try_from(give::<&proc_macro::Literal>()); //! //! let _ = litrs::CharLit::try_from(give::()); //! let _ = litrs::CharLit::try_from(give::<&proc_macro::Literal>()); //! //! let _ = litrs::StringLit::try_from(give::()); //! let _ = litrs::StringLit::try_from(give::<&proc_macro::Literal>()); //! //! let _ = litrs::ByteLit::try_from(give::()); //! let _ = litrs::ByteLit::try_from(give::<&proc_macro::Literal>()); //! //! let _ = litrs::ByteStringLit::try_from(give::()); //! let _ = litrs::ByteStringLit::try_from(give::<&proc_macro::Literal>()); //! //! //! let _ = litrs::BoolLit::try_from(give::()); //! let _ = litrs::BoolLit::try_from(give::<&proc_macro::TokenTree>()); //! //! let _ = litrs::IntegerLit::try_from(give::()); //! let _ = litrs::IntegerLit::try_from(give::<&proc_macro::TokenTree>()); //! //! let _ = litrs::FloatLit::try_from(give::()); //! let _ = litrs::FloatLit::try_from(give::<&proc_macro::TokenTree>()); //! //! let _ = litrs::CharLit::try_from(give::()); //! let _ = litrs::CharLit::try_from(give::<&proc_macro::TokenTree>()); //! //! let _ = litrs::StringLit::try_from(give::()); //! let _ = litrs::StringLit::try_from(give::<&proc_macro::TokenTree>()); //! //! let _ = litrs::ByteLit::try_from(give::()); //! let _ = litrs::ByteLit::try_from(give::<&proc_macro::TokenTree>()); //! //! let _ = litrs::ByteStringLit::try_from(give::()); //! let _ = litrs::ByteStringLit::try_from(give::<&proc_macro::TokenTree>()); //! ``` } #[cfg(feature = "proc-macro2")] mod tests_proc_macro2 { //! # Tests //! //! ```no_run //! extern crate proc_macro; //! //! use std::convert::TryFrom; //! use litrs::Literal; //! //! fn give() -> T { //! panic!() //! } //! //! let _ = litrs::Literal::from(give::()); //! let _ = litrs::Literal::from(give::<&proc_macro2::Literal>()); //! //! let _ = litrs::Literal::try_from(give::()); //! let _ = litrs::Literal::try_from(give::<&proc_macro2::TokenTree>()); //! //! //! let _ = litrs::IntegerLit::try_from(give::()); //! let _ = litrs::IntegerLit::try_from(give::<&proc_macro2::Literal>()); //! //! let _ = litrs::FloatLit::try_from(give::()); //! let _ = litrs::FloatLit::try_from(give::<&proc_macro2::Literal>()); //! //! let _ = litrs::CharLit::try_from(give::()); //! let _ = litrs::CharLit::try_from(give::<&proc_macro2::Literal>()); //! //! let _ = litrs::StringLit::try_from(give::()); //! let _ = litrs::StringLit::try_from(give::<&proc_macro2::Literal>()); //! //! let _ = litrs::ByteLit::try_from(give::()); //! let _ = litrs::ByteLit::try_from(give::<&proc_macro2::Literal>()); //! //! let _ = litrs::ByteStringLit::try_from(give::()); //! let _ = litrs::ByteStringLit::try_from(give::<&proc_macro2::Literal>()); //! //! //! let _ = litrs::BoolLit::try_from(give::()); //! let _ = litrs::BoolLit::try_from(give::<&proc_macro2::TokenTree>()); //! //! let _ = litrs::IntegerLit::try_from(give::()); //! let _ = litrs::IntegerLit::try_from(give::<&proc_macro2::TokenTree>()); //! //! let _ = litrs::FloatLit::try_from(give::()); //! let _ = litrs::FloatLit::try_from(give::<&proc_macro2::TokenTree>()); //! //! let _ = litrs::CharLit::try_from(give::()); //! let _ = litrs::CharLit::try_from(give::<&proc_macro2::TokenTree>()); //! //! let _ = litrs::StringLit::try_from(give::()); //! let _ = litrs::StringLit::try_from(give::<&proc_macro2::TokenTree>()); //! //! let _ = litrs::ByteLit::try_from(give::()); //! let _ = litrs::ByteLit::try_from(give::<&proc_macro2::TokenTree>()); //! //! let _ = litrs::ByteStringLit::try_from(give::()); //! let _ = litrs::ByteStringLit::try_from(give::<&proc_macro2::TokenTree>()); //! ``` }