first iteration

This commit is contained in:
Matteo Bovetti 2025-03-10 16:21:12 +01:00
parent 2fd31217bd
commit 14a67240af
7 changed files with 76 additions and 43 deletions

17
Cargo.lock generated
View File

@ -230,7 +230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e4e8200b9a4a5801a769d50eeabc05670fec7e959a8cb7a63a93e4e519942ae" checksum = "5e4e8200b9a4a5801a769d50eeabc05670fec7e959a8cb7a63a93e4e519942ae"
dependencies = [ dependencies = [
"aws-lc-sys", "aws-lc-sys",
"paste 1.0.15", "paste",
"zeroize", "zeroize",
] ]
@ -245,7 +245,7 @@ dependencies = [
"cmake", "cmake",
"dunce", "dunce",
"fs_extra", "fs_extra",
"paste 1.0.15", "paste",
] ]
[[package]] [[package]]
@ -1334,16 +1334,21 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "paste"
version = "0.1.0"
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.15" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "paste-macro"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "pem" name = "pem"
version = "3.0.5" version = "3.0.5"

View File

@ -12,7 +12,7 @@ members = [
"bytestring", "bytestring",
"local-channel", "local-channel",
"local-waker", "local-waker",
"paste", "paste-macro",
] ]
[workspace.package] [workspace.package]
@ -32,6 +32,7 @@ actix-utils = { path = "actix-utils" }
bytestring = { path = "bytestring" } bytestring = { path = "bytestring" }
local-channel = { path = "local-channel" } local-channel = { path = "local-channel" }
local-waker = { path = "local-waker" } local-waker = { path = "local-waker" }
paste-macro = { path = "paste-macro" }
[profile.release] [profile.release]
lto = true lto = true

View File

@ -3,6 +3,8 @@
use alloc::{boxed::Box, rc::Rc}; use alloc::{boxed::Box, rc::Rc};
use core::{future::Future, pin::Pin}; use core::{future::Future, pin::Pin};
use paste_macro::paste;
use crate::{Service, ServiceFactory}; use crate::{Service, ServiceFactory};
/// A boxed future with no send bound or lifetime parameters. /// A boxed future with no send bound or lifetime parameters.

13
paste-macro/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "paste-macro"
version = "0.1.0"
edition = "2021"
description = "Minimal implementation of the paste crate for identifier concatenation"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }

3
paste-macro/src/lib.rs Normal file
View File

@ -0,0 +1,3 @@
//! A minimal implementation of the paste-macro crate, allowing identifier concatenation in macros.
mod paste;

View File

@ -1,4 +1,4 @@
//! A minimal implementation of the paste crate, allowing identifier concatenation in macros. //! A minimal implementation of the paste-macro crate, allowing identifier concatenation in macros.
use proc_macro::{ use proc_macro::{
Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree
@ -6,6 +6,21 @@ use proc_macro::{
use std::{iter, str::FromStr}; use std::{iter, str::FromStr};
use std::panic; use std::panic;
// Pastes identifiers together to form a single identifier.
//
// Within the `paste!` macro, identifiers inside `[<`...`>]` are pasted
// together to form a single identifier.
//
// # Examples
//
// ```
// use paste_macro::paste;
//
// paste! {
// // Defines a const called `QRST`.
// const [<Q R S T>]: &str = "success!";
// }
// ```
#[proc_macro] #[proc_macro]
pub fn paste(input: TokenStream) -> TokenStream { pub fn paste(input: TokenStream) -> TokenStream {
let mut expanded = TokenStream::new(); let mut expanded = TokenStream::new();
@ -19,7 +34,7 @@ pub fn paste(input: TokenStream) -> TokenStream {
let span = group.span(); let span = group.span();
if delimiter == Delimiter::Bracket && is_paste_operation(&content) { if delimiter == Delimiter::Bracket && is_paste_operation(&content) {
// Process [< ... >] paste operation // Process [< ... >] paste-macro operation
if let Ok(pasted) = process_paste_operation(content, span) { if let Ok(pasted) = process_paste_operation(content, span) {
expanded.extend(pasted); expanded.extend(pasted);
} else { } else {
@ -42,7 +57,7 @@ pub fn paste(input: TokenStream) -> TokenStream {
expanded expanded
} }
// Check if a token stream is a paste operation: [< ... >] // Check if a token stream is a paste-macro operation: [< ... >]
fn is_paste_operation(input: &TokenStream) -> bool { fn is_paste_operation(input: &TokenStream) -> bool {
let mut tokens = input.clone().into_iter(); let mut tokens = input.clone().into_iter();
@ -77,6 +92,8 @@ fn process_paste_operation(input: TokenStream, span: Span) -> Result<TokenStream
// Collect and process segments // Collect and process segments
let mut segments = Vec::new(); let mut segments = Vec::new();
let mut has_lifetime = false;
while let Some(token) = tokens.next() { while let Some(token) = tokens.next() {
match &token { match &token {
TokenTree::Punct(punct) if punct.as_char() == '>' => break, TokenTree::Punct(punct) if punct.as_char() == '>' => break,
@ -84,12 +101,15 @@ fn process_paste_operation(input: TokenStream, span: Span) -> Result<TokenStream
TokenTree::Literal(lit) => { TokenTree::Literal(lit) => {
let lit_str = lit.to_string(); let lit_str = lit.to_string();
if lit_str.starts_with('"') && lit_str.ends_with('"') && lit_str.len() >= 2 { if lit_str.starts_with('"') && lit_str.ends_with('"') && lit_str.len() >= 2 {
segments.push(lit_str[1..lit_str.len() - 1].to_owned()); segments.push(lit_str[1..lit_str.len() - 1].to_owned().replace('-', "_"));
} else { } else {
segments.push(lit_str); segments.push(lit_str.replace('-', "_"));
} }
}, },
TokenTree::Punct(punct) if punct.as_char() == '_' => segments.push("_".to_owned()), TokenTree::Punct(punct) if punct.as_char() == '_' => segments.push("_".to_owned()),
TokenTree::Punct(punct) if punct.as_char() == '\'' => {
has_lifetime = true;
},
TokenTree::Punct(punct) if punct.as_char() == ':' => { TokenTree::Punct(punct) if punct.as_char() == ':' => {
if segments.is_empty() { if segments.is_empty() {
return Err(()); return Err(());
@ -118,28 +138,31 @@ fn process_paste_operation(input: TokenStream, span: Span) -> Result<TokenStream
} }
// Create identifier from the concatenated segments // Create identifier from the concatenated segments
let pasted = segments.join(""); let mut pasted = segments.join("");
// Convert to a valid Rust identifier // Add lifetime symbol if needed
let ident = match panic::catch_unwind(|| Ident::new(&pasted, span)) { if has_lifetime {
Ok(ident) => TokenTree::Ident(ident), pasted.insert(0, '\'');
Err(_) => { }
// If it starts with a number, try to create a literal
if pasted.starts_with(|c: char| c.is_ascii_digit()) { // Convert to a valid Rust identifier or literal
match TokenStream::from_str(&pasted) { if pasted.starts_with(|c: char| c.is_ascii_digit()) {
Ok(ts) => { // If it starts with a number, try to create a literal
if let Some(token) = ts.into_iter().next() { match TokenStream::from_str(&pasted) {
return Ok(iter::once(token).collect()); Ok(ts) => {
} if let Some(token) = ts.into_iter().next() {
} return Ok(iter::once(token).collect());
Err(_) => {}
} }
} }
return Err(()); Err(_) => return Err(()),
} }
}; }
Ok(iter::once(ident).collect()) // Try to create an identifier
match panic::catch_unwind(|| Ident::new(&pasted, span)) {
Ok(ident) => Ok(iter::once(TokenTree::Ident(ident)).collect()),
Err(_) => Err(()),
}
} }
// Helper function to convert CamelCase to snake_case // Helper function to convert CamelCase to snake_case

View File

@ -1,14 +0,0 @@
[package]
name = "paste"
version = "0.1.0"
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
[lints]
workspace = true
[lib]
proc-macro = true