简体   繁体   English

如何为 Clap Args 使用内部库枚举

[英]How to use an internal library Enum for Clap Args

I am currently working on a Rust port of a security tool .我目前正在研究安全工具Rust 端口 Inline with Rust's guides, I want to segregate the core library into its own crate, so that we can create various tools (CLI, API, streams etc.) that interface with with the core library without coupling them together.根据 Rust 的指南,我想将核心库分离到它自己的 crate 中,这样我们就可以创建与核心库交互而不将它们耦合在一起的各种工具(CLI、API、流等)。

The core library exposes two public Enums, one of them being the PermutationMode (truncated):核心库公开了两个公共枚举,其中之一是PermutationMode (截断):

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

When creating a CLI utility using Clap , I would like to extend this library Enum as part of the CLI like so:使用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,
}

So that when calling the CLI, we can pass the permutation mode from the user, to the CLI, to the library seamlessly and without having the CLI needing to be aware of the internal modes (in the event that the library adds more).因此,当调用 CLI 时,我们可以无缝地将置换模式从用户传递到 CLI,再传递到库,而 CLI 不需要了解内部模式(如果库添加了更多模式)。

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

Currently this does not seem to be possible (which makes sense).目前这似乎是不可能的(这是有道理的)。 One attempt was to use type aliasing:一种尝试是使用类型别名:

#[derive(Clap)]
type ArgPermutationMode = PermutationMode

However we cannot use derive macros for type-aliases.但是,我们不能将派生宏用于类型别名。 I tried also "cloning" the enum and trying to map to the libraries enum:我还尝试“克隆”枚举并尝试将 map 到库枚举:

enum ArgPermutationMode {
    PermutationMode::All,
}

Which does not compile.哪个不编译。


Question - Is it possible to extend an internal library Enum to use it as a Clap argument?问题- 是否可以扩展内部库 Enum 以将其用作 Clap 参数?

Unfortunately not.不幸的是没有。 You would have to redefine the enum so that the arg_enum!您必须重新定义枚举,以便arg_enum! macro can access the tokens.宏可以访问令牌。

If you add a conversion function between the two then you can make sure that upstream changes to the library enum force you to update your CLI, by giving you a compilation error:如果您在两者之间添加转换 function ,那么您可以通过给您一个编译错误来确保对库枚举的上游更改强制您更新您的 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,
         }
    }
}

You can reduce that boilerplate with a macro if you find yourself doing it a lot.如果您发现自己经常这样做,则可以使用宏来减少该样板。


Given that you have control over the other crate, you could compromise by trying one of a few other options for a workaround:鉴于您可以控制另一个板条箱,您可以通过尝试其他几种解决方法之一来妥协:

  • Define the actual enum variants in a separate file, and use include!在单独的文件中定义实际的枚举变量,并使用include! to use the same source in both crates.在两个 crate 中使用相同的源。 This assumes your crates are in the same workspace.这假设您的箱子在同一个工作区中。
  • Use a macro derive like EnumIter from strum_macros .使用从strum_macros EnumIter This will let you iterate over the enum's variants so you can supply them to Clap, without having a Clap dependency in that crate.这将允许您遍历枚举的变体,以便您可以将它们提供给 Clap,而无需在该板条箱中具有 Clap 依赖项。 You'll have a strum_macros dependency instead, so it's up to you if that is really better.相反,您将拥有一个strum_macros依赖项,因此这取决于您是否真的更好。
  • Add the clap_args!添加clap_args! call in the internal crate, but feature-gate it.调用内部板条箱,但对其进行功能门控。 Your application crate can enable this feature, but most users wouldn't.您的应用程序箱可以启用此功能,但大多数用户不会。

This is more of an extension to the above answer, incase it may help someone else.这更像是对上述答案的扩展,以防它可能对其他人有所帮助。 Ultimately what I ended up opting for was implementing implementing FromStr in the library as follows:最终我最终选择的是在库中实现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(),
        }
    }
}

To avoid having the client having to worry about the modes, we simply try to parse the string passed through the CLI into one of the permutation modes.为了避免让客户端担心模式,我们只是尝试将通过 CLI 传递的字符串解析为一种排列模式。

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

This way we don't need to couple the modes between the client and the library and overall makes the example CLI a lot more malleable.这样我们就不需要在客户端和库之间耦合模式,并且总体上使示例 CLI 更具可塑性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM