#![allow(dead_code)]
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use std::fmt::Display;
use std::marker::PhantomData;
use syn::parse::Parse;
#[derive(Debug, Copy, Clone)]
pub struct TokenStreamFromStr<'a, T> {
token_str: &'a str,
_marker: PhantomData<T>,
}
impl<'a, T> TokenStreamFromStr<'a, T> {
pub const fn new(token_str: &'a str) -> Self {
Self {
token_str,
_marker: PhantomData,
}
}
pub const fn as_str(&self) -> &str {
self.token_str
}
}
pub type PathTokenStreamFromStr<'a> = TokenStreamFromStr<'a, syn::Path>;
pub type IdentTokenStreamFromStr<'a> = TokenStreamFromStr<'a, syn::Ident>;
impl<'a, T> Display for TokenStreamFromStr<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<'a, T> From<TokenStreamFromStr<'a, T>> for &'a str {
fn from(ts: TokenStreamFromStr<'a, T>) -> Self {
ts.token_str
}
}
impl<T: Parse + ToTokens + 'static> ToTokens for TokenStreamFromStr<'_, T> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Ok(item) = syn::parse_str::<T>(self.token_str) else {
panic!(
"failed to parse {} from: {:?}",
std::any::type_name::<T>(),
self.token_str,
);
};
tokens.extend(quote!(#item));
}
}
pub type PathTokenStreamFromStrCollection<'a, const N: usize> =
TokenStreamFromStrCollection<'a, syn::Path, N>;
pub type IdentTokenStreamFromStrCollection<'a, const N: usize> =
TokenStreamFromStrCollection<'a, syn::Ident, N>;
#[derive(Debug, Copy, Clone)]
pub struct TokenStreamFromStrCollection<'a, T, const N: usize> {
token_strs: [TokenStreamFromStr<'a, T>; N],
_marker: PhantomData<T>,
}
impl<'a, T, const N: usize> TokenStreamFromStrCollection<'a, T, N> {
pub const fn new(token_strs: [TokenStreamFromStr<'a, T>; N]) -> Self {
Self {
token_strs,
_marker: PhantomData,
}
}
}
impl<'a, T, const N: usize> Display for TokenStreamFromStrCollection<'a, T, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", String::from(self))
}
}
impl<'a, T, const N: usize> From<TokenStreamFromStrCollection<'a, T, N>> for String {
fn from(ts: TokenStreamFromStrCollection<'a, T, N>) -> Self {
String::from(&ts)
}
}
impl<'a, T, const N: usize> From<&TokenStreamFromStrCollection<'a, T, N>> for String {
fn from(ts: &TokenStreamFromStrCollection<'a, T, N>) -> Self {
ts.token_strs
.iter()
.map(|x| format!("::{x}"))
.collect::<String>()
}
}
impl<T: Parse + ToTokens + 'static, const N: usize> ToTokens
for TokenStreamFromStrCollection<'_, T, N>
{
fn to_tokens(&self, tokens: &mut TokenStream) {
let path = self.to_string();
let Ok(item) = syn::parse_str::<T>(&path) else {
panic!(
"failed to parse {} from: {:?}",
std::any::type_name::<T>(),
path
);
};
tokens.extend(quote!(#item));
}
}
pub trait TokenStreamExt {
fn to_pretty_print_string(self) -> syn::Result<String>;
}
impl TokenStreamExt for TokenStream {
fn to_pretty_print_string(self) -> syn::Result<String> {
let abstract_file: syn::File = syn::parse2(self)?;
let pretty_rust = prettyplease::unparse(&abstract_file);
Ok(pretty_rust)
}
}
#[cfg(test)]
#[macro_export]
macro_rules! assert_eq_ts_pretty_print {
($left:expr, $right:expr $(,)?) => {{
use ::proc_macro2::TokenStream;
use $crate::extensions::token_stream::TokenStreamExt;
let left = <TokenStream as TokenStreamExt>::to_pretty_print_string($left)?;
let right = <TokenStream as TokenStreamExt>::to_pretty_print_string($right)?;
::core::assert_eq!(
left,
right,
"tokens don't match\n\n/* left */\n\n{left}\n/* right */\n\n{right}\n/* end */"
);
}};
}
#[cfg(test)]
mod tests {
use super::*;
use quote::quote;
#[test]
fn test_to_tokens() {
assert_eq!(
PathTokenStreamFromStr::new("foo")
.to_token_stream()
.to_string(),
quote!(foo).to_string()
);
}
}