litext 1.2.1

Seamless proc-macro literal extraction.
Documentation
use crate::{LitBool, LitByte, LitByteStr, LitCStr, LitChar, LitFloat, LitInt, LitStr};
use proc_macro2::{Ident, Literal, TokenStream, TokenTree};

/// Converts a span-aware literal type back into a [`TokenStream`].
///
/// This is the counterpart to [`FromLit`](crate::FromLit). Together they enable an
/// extract, validate, emit pattern: pull a literal out of the input,
/// inspect or transform its value, then emit it back into the output
/// with the original source span preserved.
///
/// All implementations attach the original [`Span`](proc_macro2::Span) to the reconstructed
/// token so the compiler diagnostic system continues to point at the right
/// location in the user's source code.
///
/// # Example
///
/// ```ignore
/// use litext::{litext, TokenStream};
/// use litext::literal::{LitStr, ToTokens};
///
/// pub fn my_macro(input: TokenStream) -> TokenStream {
///     let lit: LitStr = litext!(input as LitStr);
///
///     if lit.value().is_empty() {
///         return comperr::error(lit.span(), "string cannot be empty");
///     }
///
///     lit.to_token_stream()
/// }
/// ```
///
/// # See Also
///
/// - [`FromLit`](crate::FromLit) for the extraction direction
pub trait ToTokens {
    /// Converts this literal into a [`TokenStream`].
    ///
    /// The resulting token stream contains a single literal token with the
    /// original span attached. When returned from a proc-macro, this token
    /// compiles as the literal value it represents.
    fn to_token_stream(&self) -> TokenStream;
}

impl ToTokens for LitStr {
    #[inline]
    fn to_token_stream(&self) -> TokenStream {
        let mut lit = Literal::string(&self.value);
        lit.set_span(self.span);
        TokenTree::Literal(lit).into()
    }
}

impl ToTokens for LitBool {
    /// `true` and `false` are `Ident` tokens, not `Literal` tokens.
    #[inline]
    fn to_token_stream(&self) -> TokenStream {
        let ident = Ident::new(if self.value { "true" } else { "false" }, self.span);
        TokenTree::Ident(ident).into()
    }
}

impl ToTokens for LitChar {
    #[inline]
    fn to_token_stream(&self) -> TokenStream {
        let mut lit = Literal::character(self.value);
        lit.set_span(self.span);
        TokenTree::Literal(lit).into()
    }
}

impl ToTokens for LitByte {
    /// Produces a `b'...'` literal, not a `u8` integer, to correctly round-trip
    /// the original byte literal form.
    #[inline]
    fn to_token_stream(&self) -> TokenStream {
        let mut lit = Literal::byte_character(self.value);
        lit.set_span(self.span);
        TokenTree::Literal(lit).into()
    }
}

impl ToTokens for LitByteStr {
    #[inline]
    fn to_token_stream(&self) -> TokenStream {
        let mut lit = Literal::byte_string(&self.value);
        lit.set_span(self.span);
        TokenTree::Literal(lit).into()
    }
}

impl ToTokens for LitCStr {
    #[inline]
    fn to_token_stream(&self) -> TokenStream {
        let mut lit = Literal::c_string(&self.value);
        lit.set_span(self.span);
        TokenTree::Literal(lit).into()
    }
}

/// Reconstructs integer literal tokens for all supported integer types.
///
/// If the original literal had an explicit type suffix (e.g. `42u8`), the
/// suffix is preserved in the output. If it had no suffix (e.g. `42`), the
/// output is also unsuffixed, allowing the compiler to infer the type.
macro_rules! impl_to_tokens_int {
    ($($t:ty => $suffixed:ident, $unsuffixed:ident),* $(,)?) => {
        $(
            impl ToTokens for LitInt<$t> {
                #[inline]
                fn to_token_stream(&self) -> TokenStream {
                    let mut lit = if self.suffix.is_some() {
                        Literal::$suffixed(self.value)
                    } else {
                        Literal::$unsuffixed(self.value)
                    };
                    lit.set_span(self.span);
                    TokenTree::Literal(lit).into()
                }
            }
        )*
    };
}

impl_to_tokens_int!(
    i8    => i8_suffixed,    i8_unsuffixed,
    i16   => i16_suffixed,   i16_unsuffixed,
    i32   => i32_suffixed,   i32_unsuffixed,
    i64   => i64_suffixed,   i64_unsuffixed,
    i128  => i128_suffixed,  i128_unsuffixed,
    isize => isize_suffixed, isize_unsuffixed,
    u8    => u8_suffixed,    u8_unsuffixed,
    u16   => u16_suffixed,   u16_unsuffixed,
    u32   => u32_suffixed,   u32_unsuffixed,
    u64   => u64_suffixed,   u64_unsuffixed,
    u128  => u128_suffixed,  u128_unsuffixed,
    usize => usize_suffixed, usize_unsuffixed,
);

/// Reconstructs float literal tokens for all supported float types.
///
/// If the original literal had an explicit type suffix (e.g. `3.14f32`), the
/// suffix is preserved. If it had no suffix (e.g. `3.14`), the output is
/// unsuffixed, allowing the compiler to infer the type.
macro_rules! impl_to_tokens_float {
    ($($t:ty => $suffixed:ident, $unsuffixed:ident),* $(,)?) => {
        $(
            impl ToTokens for LitFloat<$t> {
                #[inline]
                fn to_token_stream(&self) -> TokenStream {
                    let mut lit = if self.suffix.is_some() {
                        Literal::$suffixed(self.value)
                    } else {
                        Literal::$unsuffixed(self.value)
                    };
                    lit.set_span(self.span);
                    TokenTree::Literal(lit).into()
                }
            }
        )*
    };
}

impl_to_tokens_float!(
    f32 => f32_suffixed, f32_unsuffixed,
    f64 => f64_suffixed, f64_unsuffixed,
);