proc-macro-workshop:sorted-3
题解
// At this point we have an enum and we need to check whether the variants
// appear in sorted order!
//
// When your implementation notices a variant that compares lexicographically
// less than one of the earlier variants, you'll want to construct a syn::Error
// that gets converted to TokenStream by the already existing code that handled
// this conversion during the previous test case.
//
// The "span" of your error, which determines where the compiler places the
// resulting error message, will be the span of whichever variant name that is
// not in the right place. Ideally the error message should also identify which
// other variant the user needs to move this one to be in front of.
use sorted::sorted;
#[sorted]
pub enum Error {
ThatFailed,
ThisFailed,
SomethingFailed,
WhoKnowsWhatFailed,
}
fn main() {}
这里我们终于接触到sorted
的核心功能了,那就是排序检查。
参考错误
error: SomethingFailed should sort before ThatFailed
--> tests/03-out-of-order.rs:20:5
|
20 | SomethingFailed,
| ^^^^^^^^^^^^^^^
我们需要找出枚举字段中的名称,然后对比排序结果,不符合顺序就要提示。
抽取
pub(crate) fn check_order(
names: Vec<(String, &dyn quote::ToTokens)>,
) -> std::option::Option<syn::Error> {
let origin_names = names;
let mut sorted_names = origin_names.clone();
// 排序
sorted_names.sort_by(|a, b| a.0.cmp(&b.0));
for (a, b) in origin_names.iter().zip(sorted_names.iter()) {
// 顺序不对就报错
if a.0 != b.0 {
return std::option::Option::Some(syn::Error::new_spanned(
b.1,
format!("{} should sort before {}", b.0, a.0),
));
}
}
std::option::Option::None
}
这里我们直接使用的是ToTokens
,虽然可以直接使用ident
获取span
,但是后续的一些情况就得重写,干脆用顶级的ToTokens
,兼容两者,避免重复劳动。
题解
pub(crate) fn solution(item: &syn::ItemEnum) -> syn::Result<proc_macro2::TokenStream> {
let mut names: Vec<(String, &dyn quote::ToTokens)> = Vec::new();
for i in item.variants.iter() {
names.push((i.ident.to_string(), &i.ident));
}
match crate::common::check_order(names) {
Some(e) => syn::Result::Err(e),
None => syn::Result::Ok(crate::common::to_token_stream(item)),
}
}
获取(string, ToTokens)
之后,通过排序检查即可。
因为上层会自动添加错误时候的代码解析,这里直接报错没啥问题。
正常来说,涉及错误时候都应该使用
Result
,但是我们并没有想得到任何结果。
因此这里我使用的是Option<Error>
,正规一点应该是Result<(), Error>
。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: