solana_clap_v3_utils/keygen/
mnemonic.rs1use {
2 crate::{keypair::prompt_passphrase, ArgConstant},
3 bip39::Language,
4 clap::{builder::PossibleValuesParser, Arg, ArgMatches},
5 std::error,
6};
7
8pub const NO_PASSPHRASE: &str = "";
9
10pub const WORD_COUNT_ARG: ArgConstant<'static> = ArgConstant {
11 long: "word-count",
12 name: "word_count",
13 help: "Specify the number of words that will be present in the generated seed phrase",
14};
15
16pub const LANGUAGE_ARG: ArgConstant<'static> = ArgConstant {
17 long: "language",
18 name: "language",
19 help: "Specify the mnemonic language that will be present in the generated seed phrase",
20};
21
22pub const NO_PASSPHRASE_ARG: ArgConstant<'static> = ArgConstant {
23 long: "no-bip39-passphrase",
24 name: "no_passphrase",
25 help: "Do not prompt for a BIP39 passphrase",
26};
27
28const POSSIBLE_WORD_COUNTS: &[&str] = &["12", "15", "18", "21", "24"];
31pub fn word_count_arg<'a>() -> Arg<'a> {
32 Arg::new(WORD_COUNT_ARG.name)
33 .long(WORD_COUNT_ARG.long)
34 .value_parser(PossibleValuesParser::new(POSSIBLE_WORD_COUNTS))
35 .default_value("12")
36 .value_name("NUMBER")
37 .takes_value(true)
38 .help(WORD_COUNT_ARG.help)
39}
40
41pub fn try_get_word_count(matches: &ArgMatches) -> Result<Option<usize>, Box<dyn error::Error>> {
42 Ok(matches
43 .try_get_one::<String>(WORD_COUNT_ARG.name)?
44 .map(|count| match count.as_str() {
45 "12" => 12,
46 "15" => 15,
47 "18" => 18,
48 "21" => 21,
49 "24" => 24,
50 _ => unreachable!(),
51 }))
52}
53
54const POSSIBLE_LANGUAGES: &[&str] = &[
56 "english",
57 "chinese-simplified",
58 "chinese-traditional",
59 "japanese",
60 "spanish",
61 "korean",
62 "french",
63 "italian",
64];
65pub fn language_arg<'a>() -> Arg<'a> {
66 Arg::new(LANGUAGE_ARG.name)
67 .long(LANGUAGE_ARG.long)
68 .value_parser(PossibleValuesParser::new(POSSIBLE_LANGUAGES))
69 .default_value("english")
70 .value_name("LANGUAGE")
71 .takes_value(true)
72 .help(LANGUAGE_ARG.help)
73}
74
75pub fn no_passphrase_arg<'a>() -> Arg<'a> {
76 Arg::new(NO_PASSPHRASE_ARG.name)
77 .long(NO_PASSPHRASE_ARG.long)
78 .alias("no-passphrase")
79 .help(NO_PASSPHRASE_ARG.help)
80}
81
82#[deprecated(since = "2.0.0", note = "Please use `try_get_language` instead")]
83pub fn acquire_language(matches: &ArgMatches) -> Language {
84 #[allow(deprecated)]
85 let language_name = LANGUAGE_ARG.name;
86 match matches.get_one::<String>(language_name).unwrap().as_str() {
87 "english" => Language::English,
88 "chinese-simplified" => Language::ChineseSimplified,
89 "chinese-traditional" => Language::ChineseTraditional,
90 "japanese" => Language::Japanese,
91 "spanish" => Language::Spanish,
92 "korean" => Language::Korean,
93 "french" => Language::French,
94 "italian" => Language::Italian,
95 _ => unreachable!(),
96 }
97}
98
99pub fn try_get_language(matches: &ArgMatches) -> Result<Option<Language>, Box<dyn error::Error>> {
100 Ok(matches
101 .try_get_one::<String>(LANGUAGE_ARG.name)?
102 .map(|language| match language.as_str() {
103 "english" => Language::English,
104 "chinese-simplified" => Language::ChineseSimplified,
105 "chinese-traditional" => Language::ChineseTraditional,
106 "japanese" => Language::Japanese,
107 "spanish" => Language::Spanish,
108 "korean" => Language::Korean,
109 "french" => Language::French,
110 "italian" => Language::Italian,
111 _ => unreachable!(),
112 }))
113}
114
115pub fn no_passphrase_and_message() -> (String, String) {
116 (NO_PASSPHRASE.to_string(), "".to_string())
117}
118
119pub fn acquire_passphrase_and_message(
120 matches: &ArgMatches,
121) -> Result<(String, String), Box<dyn error::Error>> {
122 #[rustfmt::skip]
123 const PROMPT: &str =
124 "\nFor added security, enter a BIP39 passphrase\n\
125 \nNOTE! This passphrase improves security of the recovery seed phrase NOT the\n\
126 keypair file itself, which is stored as insecure plain text\n\
127 \nBIP39 Passphrase (empty for none): ";
128
129 if matches.try_contains_id(NO_PASSPHRASE_ARG.name)? {
130 Ok(no_passphrase_and_message())
131 } else {
132 match prompt_passphrase(PROMPT) {
133 Ok(passphrase) => {
134 println!();
135 Ok((passphrase, " and your BIP39 passphrase".to_string()))
136 }
137 Err(e) => Err(e),
138 }
139 }
140}