use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{spanned::Spanned, LitStr}; /// Contains tokens representing different kinds of string. #[derive(Clone)] pub(crate) enum StringExpr { /// A string that is valid at compile time. /// /// This is either a string literal like `"mystring"`, /// or a string created by a macro like [`module_path`] /// or [`concat`]. Const(TokenStream), /// A [string slice](str) that is borrowed for a `'static` lifetime. Borrowed(TokenStream), /// An [owned string](String). Owned(TokenStream), } impl From for StringExpr { fn from(value: T) -> Self { Self::from_lit(&LitStr::new(&value.to_string(), value.span())) } } impl StringExpr { /// Creates a [constant] [`StringExpr`] from a [`struct@LitStr`]. /// /// [constant]: StringExpr::Const pub fn from_lit(lit: &LitStr) -> Self { Self::Const(lit.to_token_stream()) } /// Creates a [constant] [`StringExpr`] by interpreting a [string slice][str] as a [`struct@LitStr`]. /// /// [constant]: StringExpr::Const pub fn from_str(string: &str) -> Self { Self::Const(string.into_token_stream()) } /// Returns tokens for an [owned string](String). /// /// The returned expression will allocate unless the [`StringExpr`] is [already owned]. /// /// [already owned]: StringExpr::Owned pub fn into_owned(self) -> TokenStream { let bevy_reflect_path = crate::meta::get_bevy_reflect_path(); match self { Self::Const(tokens) | Self::Borrowed(tokens) => quote! { #bevy_reflect_path::__macro_exports::alloc_utils::ToString::to_string(#tokens) }, Self::Owned(owned) => owned, } } /// Returns tokens for a statically borrowed [string slice](str). pub fn into_borrowed(self) -> TokenStream { match self { Self::Const(tokens) | Self::Borrowed(tokens) => tokens, Self::Owned(owned) => quote! { &#owned }, } } /// Appends a [`StringExpr`] to another. /// /// If both expressions are [`StringExpr::Const`] this will use [`concat`] to merge them. pub fn appended_by(mut self, other: StringExpr) -> Self { if let Self::Const(tokens) = self { if let Self::Const(more) = other { return Self::Const(quote! { ::core::concat!(#tokens, #more) }); } self = Self::Const(tokens); } let owned = self.into_owned(); let borrowed = other.into_borrowed(); Self::Owned(quote! { #owned + #borrowed }) } } impl Default for StringExpr { fn default() -> Self { StringExpr::from_str("") } } impl FromIterator for StringExpr { fn from_iter>(iter: T) -> Self { let mut iter = iter.into_iter(); match iter.next() { Some(mut expr) => { for next in iter { expr = expr.appended_by(next); } expr } None => Default::default(), } } }