litext 1.0.0

Just what you need for extracting string literal contents at compile time
Documentation
use crate::{
    LitBool, LitByte, LitByteStr, LitCStr, LitChar, LitFloat, LitInt, LitStr, Literal, TokenTree,
};
use proc_macro2::{Ident, TokenStream};

/// Converts a span-aware literal type back into a [`TokenStream`].
///
/// This trait is the counterpart to [`FromLit`] -- where `FromLit` extracts
/// a value from a token stream, `ToTokens` reconstructs a token stream from
/// a value. Together they allow round-tripping: extract a literal, inspect
/// or validate it, then emit it back as tokens in your macro output.
///
/// # Span Preservation
///
/// All implementations preserve the original [`Span`] of the literal. This
/// means the reconstructed token points back at the original source location,
/// which is important for correct diagnostic reporting in the compiler.
///
/// # 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");
///     }
///
///     // Round-trip the literal back into a TokenStream
///     lit.to_token_stream()
/// }
/// ```
///
/// # See Also
///
/// - [`FromLit`] for the extraction direction
/// - [`extract`](crate::extract) for extracting from a `TokenStream`
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 {
    /// Reconstructs a string literal token from this [`LitStr`].
    ///
    /// The resulting token is equivalent to writing the string literal
    /// verbatim in source code, with the original span preserved.
    #[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 {
    /// Reconstructs a boolean literal token from this [`LitBool`].
    ///
    /// Note that `true` and `false` are `Ident` tokens in the token stream,
    /// not `Literal` tokens. This impl correctly reconstructs them as idents
    /// with the original span preserved.
    #[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 {
    /// Reconstructs a character literal token from this [`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 {
    /// Reconstructs a byte literal token from this [`LitByte`].
    ///
    /// The resulting token is a `b'...'` literal, not a `u8` integer.
    /// This correctly round-trips 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 {
    /// Reconstructs a byte string literal token from this [`LitByteStr`].
    ///
    /// The resulting token is a `b"..."` literal with the original span
    /// preserved.
    #[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 {
    /// Reconstructs a C string literal token from this [`LitCStr`].
    ///
    /// The resulting token is a `c"..."` literal with the original span
    /// preserved.
    #[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,
    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,
);

/// 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,
);