btc_vanity/flags.rs
1//! # CLI and Input File Flags Module
2//!
3//! This module handles the extraction and management of flags and configuration options
4//! from the command-line interface (CLI) and input files. It provides mechanisms to:
5//! - Parse flags and input patterns from the CLI.
6//! - Combine and prioritize CLI-level and file-based flags.
7
8use crate::vanity_addr_generator::chain::Chain;
9use crate::VanityMode;
10use clap::ArgMatches;
11
12/// Represents the configuration flags for vanity address generation.
13#[derive(Debug, Clone, Default)]
14pub struct VanityFlags {
15 /// The number of threads to use for generation.
16 pub threads: usize,
17 /// The name of the output file, if specified.
18 pub output_file_name: Option<String>,
19
20 /// If `true`, CLI flags override file-based flags.
21 pub force_flags: bool,
22 /// If `true`, pattern matching is case-sensitive.
23 pub is_case_sensitive: bool,
24 /// If `true`, disables fast mode. Fast mode puts a length limit for the searching string.
25 pub disable_fast_mode: bool,
26 /// Specifies the mode of matching (e.g., `prefix`, `suffix`, `anywhere`, `regex`).
27 pub vanity_mode: Option<VanityMode>,
28 /// Specifies the blockchain type (e.g., `Bitcoin`, `Ethereum`, `Solana`).
29 pub chain: Option<Chain>,
30}
31
32/// Enum representing the source of the vanity patterns.
33#[derive(Debug)]
34pub enum PatternsSource {
35 /// A single pattern provided directly via the CLI.
36 SingleString(String),
37 /// Patterns read from an input file specified by its path.
38 InputFile(String),
39}
40
41/// Parses CLI arguments into [VanityFlags] and determines the source of the vanity patterns.
42///
43/// # Arguments
44/// - `matches`: The `ArgMatches` object provided by the `clap` library.
45///
46/// # Returns
47/// - A tuple containing:
48/// - `VanityFlags`: The parsed configuration flags.
49/// - `PatternsSource`: The source of the vanity patterns (either a single string or an input file).
50///
51/// # Behavior
52/// - Determines the blockchain (`chain`) based on flags (e.g., `ethereum`, `solana`, `bitcoin`).
53/// - Determines the vanity mode (`vanity_mode`) based on flags (e.g., `regex`, `anywhere`, `suffix`, `prefix`).
54/// - Parses the number of threads, defaulting to 16 if not specified.
55/// - Detects whether patterns are provided via a single string or an input file.
56pub fn parse_cli(matches: ArgMatches) -> (VanityFlags, PatternsSource) {
57 // 1) Extract chain
58 let chain = if matches.get_flag("ethereum") {
59 Some(Chain::Ethereum)
60 } else if matches.get_flag("solana") {
61 Some(Chain::Solana)
62 } else {
63 Some(Chain::Bitcoin)
64 };
65
66 // 2) Extract vanity mode
67 let vanity_mode = if matches.get_flag("regex") {
68 Some(VanityMode::Regex)
69 } else if matches.get_flag("anywhere") {
70 Some(VanityMode::Anywhere)
71 } else if matches.get_flag("suffix") {
72 Some(VanityMode::Suffix)
73 } else {
74 Some(VanityMode::Prefix)
75 };
76
77 // 3) Threads
78 let threads = matches
79 .get_one::<String>("threads")
80 .unwrap_or(&"16".to_owned())
81 .parse::<usize>()
82 .unwrap_or(16);
83
84 // 4) Build CLI-level `VanityFlags`
85 let cli_flags = VanityFlags {
86 force_flags: matches.get_flag("force-flags"),
87 is_case_sensitive: matches.get_flag("case-sensitive"),
88 disable_fast_mode: matches.get_flag("disable-fast-mode"),
89 output_file_name: matches.get_one::<String>("output-file").cloned(),
90 vanity_mode,
91 chain,
92 threads,
93 };
94
95 // 5) Figure out if user gave a single pattern or a file
96 if let Some(path) = matches.get_one::<String>("input-file") {
97 (cli_flags, PatternsSource::InputFile(path.to_string()))
98 } else {
99 let string = matches.get_one::<String>("string");
100 (
101 cli_flags,
102 PatternsSource::SingleString(string.unwrap_or(&String::new()).to_string()),
103 )
104 }
105}
106
107/// Combines CLI-level flags with file-based flags, giving priority to CLI flags if `force_flags` is set.
108///
109/// # Arguments
110/// - `file_flags`: The `VanityFlags` object derived from the input file.
111///
112/// # Returns
113/// - A unified `VanityFlags` object that combines CLI and file flags.
114///
115/// # Behavior
116/// - If `force_flags` is `true`, the CLI flags override all file-based flags.
117/// - Otherwise, the flags are merged, with file-based flags taking precedence where applicable.
118///
119/// # Example
120/// ```rust
121/// use btc_vanity::flags::VanityFlags;
122///
123/// let cli_flags = VanityFlags {
124/// threads: 8,
125/// force_flags: true,
126/// ..Default::default()
127/// };
128/// let file_flags = VanityFlags {
129/// threads: 4,
130/// ..Default::default()
131/// };
132///
133/// let unified_flags = cli_flags.unify(&file_flags);
134/// assert_eq!(unified_flags.threads, 8); // CLI flags take precedence.
135/// ```
136impl VanityFlags {
137 pub fn unify(&self, file_flags: &VanityFlags) -> VanityFlags {
138 if self.force_flags {
139 // If CLI has force_flags = true, ignore the file-based flags
140 self.clone()
141 } else {
142 VanityFlags {
143 threads: self.threads,
144 output_file_name: file_flags
145 .output_file_name
146 .clone()
147 .or_else(|| self.output_file_name.clone()),
148
149 force_flags: self.force_flags,
150 is_case_sensitive: file_flags.is_case_sensitive,
151 disable_fast_mode: file_flags.disable_fast_mode,
152
153 vanity_mode: file_flags.vanity_mode.or(self.vanity_mode), // Use `file_flags` if Some, otherwise fall back to `self`.
154
155 chain: file_flags.chain.or(self.chain), // Use `file_flags` if Some, otherwise fall back to `self`.
156 }
157 }
158 }
159}