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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!