Как использовать внутреннюю библиотеку Enum для аргументов Clap - PullRequest
1 голос
/ 20 июня 2020

В настоящее время я работаю над портом Rust инструмента безопасности . В соответствии с руководствами Rust, я хочу выделить базовую библиотеку в отдельный ящик, чтобы мы могли создавать различные инструменты (CLI, API, потоки и т. Д. c.), Которые взаимодействуют с основной библиотекой, не связывая их вместе.

Базовая библиотека предоставляет два publi c Enum, одно из которых - PermutationMode (усеченное):

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

При создании утилиты CLI с использованием Clap , Я хотел бы расширить эту библиотеку 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?

Ответы [ 2 ]

2 голосов
/ 21 июня 2020

К сожалению, нет. Вам нужно будет переопределить перечисление, чтобы макрос 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!, чтобы использовать один и тот же источник в обоих ящиках. Предполагается, что ваши ящики находятся в одной рабочей области.
  • Используйте макрос, производный от EnumIter из strum_macros. Это позволит вам перебирать варианты перечисления, чтобы вы могли передать их в Clap, не имея зависимости Clap в этом ящике. Вместо этого у вас будет зависимость strum_macros, так что вам решать, действительно ли это лучше.
  • Добавьте вызов clap_args! во внутренний ящик, но заблокируйте его. Ваш ящик приложения может включить эту функцию, но большинство пользователей не будут этого делать.
0 голосов
/ 23 июня 2020

Это скорее расширение к приведенному выше ответу, на случай, если он может помочь кому-то другому. В конечном итоге я остановился на реализации реализации 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 намного более гибкий.

...