1

我目前正在开发一个安全工具的Rust 端口。根据 Rust 的指南,我想将核心库分离到它自己的 crate 中,这样我们就可以创建与核心库交互的各种工具(CLI、API、流等),而无需将它们耦合在一起。

核心库公开了两个公共枚举,其中一个是PermutationMode(截断的):

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PermutationMode {
    All,
    Addition,
    BitSquatting,
    Homoglyph,
}

使用Clap创建 CLI 实用程序时,我想将此库 Enum 扩展为 CLI 的一部分,如下所示:

use clap::Clap;

use twistrs::permutate::PermutationMode;

#[derive(Clap, PartialEq, Debug)]
#[clap(name = "twistrs-cli")]
struct Opts {
    #[clap(short, long)]
    registered_domains: bool,

    #[clap(arg_enum)]
    permutation_mode: PermutationMode,
}

因此,当调用 CLI 时,我们可以无缝地将置换模式从用户传递到 CLI,再传递到库,而 CLI 不需要了解内部模式(如果库添加了更多模式)。

./twist-cli --registered-domains --permutation_mode=all example.com

目前这似乎是不可能的(这是有道理的)。一种尝试是使用类型别名:

#[derive(Clap)]
type ArgPermutationMode = PermutationMode

但是,我们不能将派生宏用于类型别名。我还尝试“克隆”枚举并尝试映射到库枚举:

enum ArgPermutationMode {
    PermutationMode::All,
}

哪个不编译。


问题- 是否可以扩展内部库 Enum 以将其用作 Clap 参数?

4

2 回答 2

3

不幸的是没有。您必须重新定义枚举,以便arg_enum!宏可以访问令牌。

如果您在两者之间添加一个转换函数,那么您可以通过给您一个编译错误来确保对库枚举的上游更改强制您更新您的 CLI:

arg_enum! {
    enum ArgPermutationMode {
        All,
        Addition,
        BitSquatting,
        Homoglyph,
    }
}

impl From<ArgPermutationMode> for PermutationMode {
    fn from(other: ArgPermutationMode) -> PermutationMode {
         match other {
             ArgPermutationMode::All => PermutationMode::All,
             ArgPermutationMode::Addition => PermutationMode::Addition,
             ArgPermutationMode::BitSquatting => PermutationMode::BitSquatting,
             ArgPermutationMode::Homoglyph => PermutationMode::Homoglyph,
         }
    }
}

impl From<PermutationMode> for ArgPermutationMode {
    fn from(other: PermutationMode) -> ArgPermutationMode {
         match other {
             PermutationMode::All => ArgPermutationMode::All,
             PermutationMode::Addition => ArgPermutationMode::Addition,
             PermutationMode::BitSquatting => ArgPermutationMode::BitSquatting,
             xPermutationMode::Homoglyph => ArgPermutationMode::Homoglyph,
         }
    }
}

如果您发现自己经常这样做,则可以使用宏来减少该样板。


鉴于您可以控制另一个板条箱,您可以通过尝试其他几种解决方法之一来妥协:

  • 在单独的文件中定义实际的枚举变量,并用于include!在两个 crate 中使用相同的源。这假设您的箱子在同一个工作区中。
  • 使用类似EnumIterfrom的宏派生strum_macros。这将允许您遍历枚举的变体,以便您可以将它们提供给 Clap,而无需在该板条箱中具有 Clap 依赖项。相反,您将有一个strum_macros依赖项,所以这取决于您是否真的更好。
  • 在内部板条箱中添加clap_args!调用,但对其进行功能门控。您的应用程序箱可以启用此功能,但大多数用户不会。
于 2020-06-20T21:19:30.583 回答
1

这更像是对上述答案的扩展,以防它可能对其他人有所帮助。最终我最终选择的是在库中实现FromStr如下:

impl FromStr for PermutationMode {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_ascii_lowercase().as_str() {
            "all" => Ok(PermutationMode::All),
            "addition" => Ok(PermutationMode::Addition),
            "bitsquatting" => Ok(PermutationMode::BitSquatting),
            "homoglyph" => Ok(PermutationMode::Homoglyph),
            _ => Err(),
        }
    }
}

为了避免让客户端担心模式,我们只是尝试将通过 CLI 传递的字符串解析为一种排列模式。

let permutation_mode = matches
    .value_of("permutation_mode")
    .unwrap()
    .parse::<PermutationMode>()
    .unwrap();

这样我们就不需要在客户端和库之间耦合模式,并且总体上使示例 CLI 更具可塑性。

于 2020-06-23T16:50:08.607 回答