0

我正在编写一个派生过程宏,其中所有值都转换为Options. 问题是结构中的任何Option字段都可以包含在这些Option类型中。就其本身而言,在我开始使用 serde 序列化数据之前,这并不是什么大问题。我希望能够跳过任何值为 的值None,但在某些情况下它会变成类似Some(None)or的值Some(CustomOption::None)。这两种情况都比简单的没有任何意义None,但我不能只写#[serde(skip_serializing_if = "Option::is_none")]派生字段。当然,它们会输出nullJSON 格式的值。

基本上,我希望能够使用 syn 库来检查派生字段的内部值的类型是否将是 anOption并将其展平为Option<T>派生结构中的单数而不是Option<Option<T>>类型。我希望 Rust 在泛型上有基于类型的模式匹配,但这并不是真的。

我可以想到这个问题的两种解决方案,但我真的想不出如何实现它们。第一个是遍历所有字段并找到Options,然后打开这些选项并重新包装它们,以便它们Option在外面只有一个。此解决方案的一个潜在问题是,我可能必须Option在进行计算后将它们重新包装到另一个中。第二种解决方案是找到Option并相应地修改生成的代码,以便如果内部选项包含None整个内容,则变为None; 基本上只有一个辅助函数,如果字段是Option. 关于如何实施这些或更好的解决方案的任何想法?

这是一个代码示例:

#[derive(Macro)]
struct X {
    a: usize,
    b: SomeType<String>,
    c: Option<String>,
}
struct GeneratedX {
    a: Option<usize>,
    b: Option<SomeType<String>>,
    c: Option<Option<String>>,
}

使用这样的函数来包装选项中的所有值:

pub fn wrap_typ_in_options(&self) -> TokenStream {
    // self is a struct with the type Type in it along with some other items.
    let typ: syn::Type = self.typ();

    // attribute to check if should ignore a field.
    if self.should_ignore() {
        quote! { Option<#typ> }
    } else {
        quote! { Option<<#typ as module::Trait>::Type> }
    }
}
4

1 回答 1

0

根据我在原始帖子中的第二个想法,我找到了解决这个问题的方法。我使用这样的函数来判断令牌是否是Option

let idents_of_path = path
    .segments
    .iter()
    .fold(String::new(), |mut acc, v| {
        acc.push_str(&v.ident.to_string());
        acc.push(':');
        acc
    });
vec!["Option:", "std:option:Option:", "core:option:Option:"]
    .into_iter()
    .find(|s| idents_of_path == *s)
    .and_then(|_| path.segments.last())

然后我添加了一个名为的新方法,如果它是一个选项is_option,它会返回一个布尔值。Type::Path

pub fn is_option(&self) -> bool {
    let typ = self.typ();

    let opt = match typ {
        Type::Path(typepath) if typepath.qself.is_none() => Some(typepath.path.clone()),
        _ => None,
    };

    if let Some(o) = opt {
        check_for_option(&o).is_some()
    } else {
        false
    }
}

我根据此调用的结果修改了生成的代码,其方式类似于我处理各种属性的方式。所有这些都应该适用于我的特定用例,因为不会将别名Option引入这个生态系统。它有点乱,但现在可以完成工作。

于 2020-09-14T20:30:17.333 回答