]> Untitled Git - bdk-cli/commitdiff
Create online and offline wallets, use regex to split and filter repl commands
authorSteve Myers <steve@notmandatory.org>
Wed, 3 Feb 2021 07:04:37 +0000 (23:04 -0800)
committerSteve Myers <steve@notmandatory.org>
Wed, 3 Feb 2021 07:11:02 +0000 (23:11 -0800)
src/bdk_cli.rs

index 439c42066700021807a303830753cf0d1bd080b2..e8c20b725231349932e07332348c137318b4033e 100644 (file)
@@ -86,30 +86,26 @@ fn prepare_home_dir() -> PathBuf {
     dir
 }
 
-fn main() {
-    env_logger::init();
-
-    let cli_opt: WalletOpt = WalletOpt::from_args();
-
-    let network = cli_opt.network;
-    debug!("network: {:?}", network);
-    if network == Network::Bitcoin {
-        warn!("This is experimental software and not currently recommended for use on Bitcoin mainnet, proceed with caution.")
-    }
-
-    let descriptor = cli_opt.descriptor.as_str();
-    let change_descriptor = cli_opt.change_descriptor.as_deref();
-    debug!("descriptors: {:?} {:?}", descriptor, change_descriptor);
-
+fn open_database(wallet_opts: &WalletOpts) -> Tree {
     let database = sled::open(prepare_home_dir().to_str().unwrap()).unwrap();
-    let tree = database.open_tree(cli_opt.wallet).unwrap();
+    let tree = database.open_tree(&wallet_opts.wallet).unwrap();
     debug!("database opened successfully");
+    tree
+}
 
+fn new_online_wallet<D>(
+    network: Network,
+    wallet_opts: &WalletOpts,
+    database: D,
+) -> Result<Wallet<AnyBlockchain, D>, Error>
+where
+    D: BatchDatabase,
+{
     // Try to use Esplora config if "esplora" feature is enabled
     #[cfg(feature = "esplora")]
     let config_esplora: Option<AnyBlockchainConfig> = {
-        let esplora_concurrency = cli_opt.esplora_concurrency;
-        cli_opt.esplora.map(|base_url| {
+        let esplora_concurrency = wallet_opts.esplora_concurrency;
+        wallet_opts.esplora.clone().map(|base_url| {
             AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
                 base_url,
                 concurrency: Some(esplora_concurrency),
@@ -119,34 +115,97 @@ fn main() {
     #[cfg(not(feature = "esplora"))]
     let config_esplora = None;
 
+    let config_electrum = AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
+        url: wallet_opts.electrum.clone(),
+        socks5: wallet_opts.proxy.clone(),
+        retry: wallet_opts.retries,
+        timeout: wallet_opts.timeout,
+    });
+
     // Fall back to Electrum config if Esplora config isn't provided
-    let config =
-        config_esplora.unwrap_or(AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
-            url: cli_opt.electrum,
-            socks5: cli_opt.proxy,
-            retry: 10,
-            timeout: None,
-        }));
+    let config = config_esplora.unwrap_or(config_electrum);
 
+    let descriptor = wallet_opts.descriptor.as_str();
+    let change_descriptor = wallet_opts.change_descriptor.as_deref();
     let wallet = Wallet::new(
         descriptor,
         change_descriptor,
         network,
-        tree,
-        AnyBlockchain::from_config(&config).unwrap(),
-    )
-    .unwrap();
+        database,
+        AnyBlockchain::from_config(&config)?,
+    )?;
+    Ok(wallet)
+}
+
+fn new_offline_wallet<D>(
+    network: Network,
+    wallet_opts: &WalletOpts,
+    database: D,
+) -> Result<Wallet<(), D>, Error>
+where
+    D: BatchDatabase,
+{
+    let descriptor = wallet_opts.descriptor.as_str();
+    let change_descriptor = wallet_opts.change_descriptor.as_deref();
+    let wallet = Wallet::new_offline(descriptor, change_descriptor, network, database)?;
+    Ok(wallet)
+}
+
+fn main() {
+    env_logger::init();
+
+    let cli_opts: CliOpts = CliOpts::from_args();
 
-    let wallet = Arc::new(wallet);
+    let network = cli_opts.network;
+    debug!("network: {:?}", network);
+    if network == Network::Bitcoin {
+        warn!("This is experimental software and not currently recommended for use on Bitcoin mainnet, proceed with caution.")
+    }
+
+    //println!("cli_opts = {:?}", cli_opts);
+
+    let result = match cli_opts.subcommand {
+        CliSubCommand::Wallet {
+            wallet_opts,
+            subcommand: WalletSubCommand::OnlineWalletSubCommand(online_subcommand),
+        } => {
+            let database = open_database(&wallet_opts);
+            let wallet = new_online_wallet(network, &wallet_opts, database).unwrap();
+            let result = bdk_cli::handle_online_wallet_subcommand(&wallet, online_subcommand);
+            serde_json::to_string_pretty(&result.unwrap()).unwrap()
+        }
+        CliSubCommand::Wallet {
+            wallet_opts,
+            subcommand: WalletSubCommand::OfflineWalletSubCommand(offline_subcommand),
+        } => {
+            let database = open_database(&wallet_opts);
+            let wallet = new_offline_wallet(network, &wallet_opts, database).unwrap();
+            let result = bdk_cli::handle_offline_wallet_subcommand(&wallet, offline_subcommand);
+            serde_json::to_string_pretty(&result.unwrap()).unwrap()
+        }
+        CliSubCommand::Key {
+            subcommand: key_subcommand,
+        } => {
+            let result = bdk_cli::handle_key_subcommand(network, key_subcommand);
+            serde_json::to_string_pretty(&result.unwrap()).unwrap()
+        }
+        CliSubCommand::Repl { wallet_opts } => {
+            let database = open_database(&wallet_opts);
+            let online_wallet = new_online_wallet(network, &wallet_opts, database.clone()).unwrap();
+            let online_wallet = Arc::new(online_wallet);
+
+            let offline_wallet = new_offline_wallet(network, &wallet_opts, database).unwrap();
+            let offline_wallet = Arc::new(offline_wallet);
 
-    match cli_opt.subcommand {
-        WalletSubCommand::Repl => {
             let mut rl = Editor::<()>::new();
 
             // if rl.load_history("history.txt").is_err() {
             //     println!("No previous history.");
             // }
 
+            let split_regex = Regex::new(r#"[\w\-]+|"[\w\s]*""#).unwrap();
+            let filter_regex = Regex::new(r#"[\w\s\-]+"#).unwrap();
+
             loop {
                 let readline = rl.readline(">> ");
                 match readline {
@@ -155,22 +214,46 @@ fn main() {
                             continue;
                         }
                         rl.add_history_entry(line.as_str());
-                        let split_line: Vec<&str> = line.split(' ').collect();
-                        let repl_subcommand: Result<ReplOpt, clap::Error> =
-                            ReplOpt::from_iter_safe(split_line);
-                        debug!("repl_subcommand = {:?}", repl_subcommand);
+                        let split_line: Vec<&str> =
+                            split_regex.find_iter(&line).map(|m| m.as_str()).collect();
+                        let filtered_line: Vec<&str> = split_line
+                            .iter()
+                            .flat_map(|s| filter_regex.find_iter(s).map(|m| m.as_str()))
+                            .collect();
+                        let repl_opt: Result<ReplOpt, clap::Error> =
+                            ReplOpt::from_iter_safe(filtered_line);
+                        debug!("repl_opt = {:?}", repl_opt);
 
-                        if let Err(err) = repl_subcommand {
+                        if let Err(err) = repl_opt {
                             println!("{}", err.message);
                             continue;
                         }
 
-                        let result = bdk_cli::handle_wallet_subcommand(
-                            &Arc::clone(&wallet),
-                            repl_subcommand.unwrap().subcommand,
-                        )
-                        .unwrap();
-                        println!("{}", serde_json::to_string_pretty(&result).unwrap());
+                        let repl_subcommand = repl_opt.unwrap().subcommand;
+
+                        let result = match repl_subcommand {
+                            ReplSubCommand::OnlineWalletSubCommand(online_subcommand) => {
+                                bdk_cli::handle_online_wallet_subcommand(
+                                    &Arc::clone(&online_wallet),
+                                    online_subcommand,
+                                )
+                            }
+                            ReplSubCommand::OfflineWalletSubCommand(offline_subcommand) => {
+                                bdk_cli::handle_offline_wallet_subcommand(
+                                    &Arc::clone(&offline_wallet),
+                                    offline_subcommand,
+                                )
+                            }
+                            ReplSubCommand::KeySubCommand(key_subcommand) => {
+                                bdk_cli::handle_key_subcommand(network, key_subcommand)
+                            }
+                            ReplSubCommand::Exit => break,
+                        };
+
+                        println!(
+                            "{}",
+                            serde_json::to_string_pretty(&result.unwrap()).unwrap()
+                        );
                     }
                     Err(ReadlineError::Interrupted) => continue,
                     Err(ReadlineError::Eof) => break,
@@ -182,10 +265,34 @@ fn main() {
             }
 
             // rl.save_history("history.txt").unwrap();
+            "Exiting REPL".to_string()
         }
-        _ => {
-            let result = bdk_cli::handle_wallet_subcommand(&wallet, cli_opt.subcommand).unwrap();
-            println!("{}", serde_json::to_string_pretty(&result).unwrap());
-        }
+    };
+
+    println!("{}", result);
+}
+
+#[cfg(test)]
+mod test {
+    use regex::Regex;
+
+    #[test]
+    fn test_regex() {
+        let split_regex = Regex::new(r#"[\w\-]+|"[\w\s]*""#).unwrap();
+        //println!("split_regex = {:?}", &split_regex);
+        let filter_regex = Regex::new(r#"[\w\s\-]+"#).unwrap();
+        //println!("filter_regex = {:?}", &filter_regex);
+        let line = r#"restore -m "word1 word2 word3" -p test"#;
+        let split_line: Vec<&str> = split_regex.find_iter(&line).map(|m| m.as_str()).collect();
+        //println!("split_line({}) = {:?}", &line, &split_line);
+        let filtered_line: Vec<&str> = split_line
+            .iter()
+            .flat_map(|s| filter_regex.find_iter(s).map(|m| m.as_str()))
+            .collect();
+        //println!("filtered_line({:?}) = {:?}", &split_line, &filtered_line);
+        assert_eq!(
+            vec!("restore", "-m", "word1 word2 word3", "-p", "test"),
+            filtered_line
+        );
     }
 }