如何实现类Warp的Rust路径宏以生成API端点路径
实现支持表达式的Rust路径生成宏
要实现类似warp::path!、支持/分隔且能正确处理带点表达式的路径宏,核心是利用Rust宏的表达式捕获器(expr)替代单个token捕获,避免拆分带点的字段访问或方法调用。以下是完整实现:
宏定义
#[macro_export] macro_rules! path { ($($segments:tt)+) => { $crate::path_internal!($($segments)+) }; } #[macro_export] macro_rules! path_internal { // 递归终止:无剩余段,返回空字符串 () => { String::new() }; // 处理「静态段 + / + 剩余路径」 ($static:literal / $($rest:tt)*) => { let mut path = String::from($static); path.push('/'); path += &$crate::path_internal!($($rest)*); path }; // 处理「动态表达式段 + / + 剩余路径」 (<$expr:expr> / $($rest:tt)*) => { let mut path = format!("{}", $expr); path.push('/'); path += &$crate::path_internal!($($rest)*); path }; // 最后一段是静态段 ($static:literal) => { String::from($static) }; // 最后一段是动态表达式段 (<$expr:expr>) => { format!("{}", $expr) }; }
关键设计点
- 递归解析路径段:通过
path_internal!辅助宏递归处理每个/分隔的路径片段,逐个拼接结果。 - 表达式捕获器:动态段使用
<$expr:expr>捕获完整的Rust表达式,能正确识别带点的字段访问(如data.event)、方法调用(如user.get_id())等,不会像tt捕获器那样拆分单个token。 - 通用字符串转换:用
format!("{}", $expr)替代to_string(),支持所有实现Displaytrait的类型(包括整数、浮点数、自定义结构体等)。
使用示例
use std::fmt; fn main() { // 带点字段访问的场景 let data = ApiData { event: "user_login".to_string() }; let stats_path = path!("/api/v2/stats/" <data.event>); println!("{}", stats_path); // 输出:/api/v2/stats/user_login // 混合静态段与多动态段 let user_id = 1001; let post_slug = "rust-macro-guide"; let post_path = path!("/users/" <user_id> "/posts/" <post_slug>); println!("{}", post_path); // 输出:/users/1001/posts/rust-macro-guide // 自定义结构体(需实现Display) let config = AppConfig { version: "v3".to_string() }; let health_path = path!("/health/" <config>); println!("{}", health_path); // 输出:/health/AppConfig(version: v3) } #[derive(Debug)] struct ApiData { event: String, } #[derive(Debug)] struct AppConfig { version: String, } impl fmt::Display for AppConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AppConfig(version: {})", self.version) } }
边界情况处理
- 路径开头的
/:直接作为静态段处理,如path!("/api")生成"/api"。 - 路径结尾的
/:如path!("/api/")会生成"/api/",符合直观预期。 - 纯动态路径:
path!(<user.id>)直接返回user.id的字符串表示。
内容的提问来源于stack exchange,提问作者rsalmei
相关产品推荐
相关产品推荐

