Skip to content

Commit 04dd213

Browse files
committed
ref(handlers): fix offline, online and desc mod
- fix execution logic for subcommands under offline and online wallet subcommands
1 parent 306069a commit 04dd213

7 files changed

Lines changed: 1130 additions & 913 deletions

File tree

src/handlers/mod.rs

Lines changed: 68 additions & 280 deletions
Original file line numberDiff line numberDiff line change
@@ -4,295 +4,83 @@ pub mod key;
44
pub mod offline;
55
pub mod online;
66
pub mod repl;
7-
pub mod types;
8-
pub mod wallets;
97

10-
#[cfg(feature = "repl")]
11-
use crate::handlers::repl::respond;
12-
use crate::{
13-
commands::{CliOpts, CliSubCommand, WalletSubCommand},
14-
error::BDKCliError as Error,
15-
handlers::{
16-
config::handle_config_subcommand, descriptor::handle_descriptor_command,
17-
key::handle_key_subcommand, wallets::handle_wallets_subcommand,
18-
},
19-
utils::{load_wallet_config, prepare_home_dir},
20-
};
21-
22-
#[cfg(any(feature = "sqlite", feature = "redb"))]
23-
use crate::utils::prepare_wallet_db_dir;
24-
#[cfg(not(any(feature = "sqlite", feature = "redb")))]
25-
use crate::wallet::new_wallet;
26-
27-
#[cfg(feature = "compiler")]
28-
use {
29-
crate::handlers::descriptor::handle_compile_subcommand, bdk_redb::Store as RedbStore,
30-
std::sync::Arc,
31-
};
32-
33-
#[cfg(feature = "repl")]
34-
use crate::handlers::repl::readline;
35-
36-
#[cfg(any(feature = "sqlite", feature = "redb"))]
37-
use crate::commands::DatabaseType;
38-
use crate::handlers::offline::handle_offline_wallet_subcommand;
39-
use clap::CommandFactory;
408
#[cfg(any(
419
feature = "electrum",
4210
feature = "esplora",
4311
feature = "rpc",
44-
feature = "cbf",
12+
feature = "cbf"
4513
))]
46-
use {
47-
crate::backend::new_blockchain_client, crate::handlers::online::handle_online_wallet_subcommand,
48-
};
49-
#[cfg(any(feature = "sqlite", feature = "redb"))]
50-
use {
51-
crate::wallet::{new_persisted_wallet, persister::Persister},
52-
bdk_wallet::rusqlite::Connection,
53-
std::io::Write,
54-
};
55-
56-
/// The global top level handler.
57-
pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
58-
let pretty = cli_opts.pretty;
59-
let subcommand = cli_opts.subcommand.clone();
60-
61-
let result: Result<String, Error> = match subcommand {
62-
#[cfg(any(
63-
feature = "electrum",
64-
feature = "esplora",
65-
feature = "cbf",
66-
feature = "rpc"
67-
))]
68-
CliSubCommand::Wallet {
69-
wallet,
70-
subcommand: WalletSubCommand::OnlineWalletSubCommand(online_subcommand),
71-
} => {
72-
let home_dir = prepare_home_dir(cli_opts.datadir)?;
73-
74-
let (wallet_opts, network) = load_wallet_config(&home_dir, &wallet)?;
75-
76-
let database_path = prepare_wallet_db_dir(&home_dir, &wallet)?;
77-
78-
#[cfg(any(feature = "sqlite", feature = "redb"))]
79-
let result = {
80-
#[cfg(feature = "sqlite")]
81-
let mut persister: Persister = match &wallet_opts.database_type {
82-
#[cfg(feature = "sqlite")]
83-
DatabaseType::Sqlite => {
84-
let db_file = database_path.join("wallet.sqlite");
85-
let connection = Connection::open(db_file)?;
86-
log::debug!("Sqlite database opened successfully");
87-
Persister::Connection(connection)
88-
}
89-
#[cfg(feature = "redb")]
90-
DatabaseType::Redb => {
91-
let wallet_name = &wallet_opts.wallet;
92-
let db = Arc::new(bdk_redb::redb::Database::create(
93-
home_dir.join("wallet.redb"),
94-
)?);
95-
let store = RedbStore::new(
96-
db,
97-
wallet_name.as_deref().unwrap_or("wallet").to_string(),
98-
)?;
99-
log::debug!("Redb database opened successfully");
100-
Persister::RedbStore(store)
101-
}
102-
};
103-
104-
let mut wallet = new_persisted_wallet(network, &mut persister, &wallet_opts)?;
105-
let blockchain_client =
106-
new_blockchain_client(&wallet_opts, &wallet, database_path)?;
107-
108-
let result = handle_online_wallet_subcommand(
109-
&mut wallet,
110-
&blockchain_client,
111-
online_subcommand,
112-
)
113-
.await?;
114-
wallet.persist(&mut persister)?;
115-
result
116-
};
117-
#[cfg(not(any(feature = "sqlite", feature = "redb")))]
118-
let result = {
119-
let mut wallet = new_wallet(network, &wallet_opts)?;
120-
let blockchain_client =
121-
new_blockchain_client(&wallet_opts, &wallet, database_path)?;
122-
handle_online_wallet_subcommand(&mut wallet, &blockchain_client, online_subcommand)
123-
.await?
124-
};
125-
Ok(result)
126-
}
127-
CliSubCommand::Wallet {
128-
wallet: wallet_name,
129-
subcommand: WalletSubCommand::OfflineWalletSubCommand(offline_subcommand),
130-
} => {
131-
let datadir = cli_opts.datadir.clone();
132-
let home_dir = prepare_home_dir(datadir)?;
133-
let (wallet_opts, network) = load_wallet_config(&home_dir, &wallet_name)?;
134-
135-
#[cfg(any(feature = "sqlite", feature = "redb"))]
136-
let result = {
137-
let mut persister: Persister = match &wallet_opts.database_type {
138-
#[cfg(feature = "sqlite")]
139-
DatabaseType::Sqlite => {
140-
let database_path = prepare_wallet_db_dir(&home_dir, &wallet_name)?;
141-
let db_file = database_path.join("wallet.sqlite");
142-
let connection = Connection::open(db_file)?;
143-
log::debug!("Sqlite database opened successfully");
144-
Persister::Connection(connection)
145-
}
146-
#[cfg(feature = "redb")]
147-
DatabaseType::Redb => {
148-
let db = Arc::new(bdk_redb::redb::Database::create(
149-
home_dir.join("wallet.redb"),
150-
)?);
151-
let store = RedbStore::new(db, wallet_name)?;
152-
log::debug!("Redb database opened successfully");
153-
Persister::RedbStore(store)
154-
}
155-
};
156-
157-
let mut wallet = new_persisted_wallet(network, &mut persister, &wallet_opts)?;
14+
use crate::client::BlockchainClient;
15+
use std::path::PathBuf;
16+
17+
use crate::{error::BDKCliError as Error, utils::output::FormatOutput};
18+
use bdk_wallet::{Wallet, bitcoin::Network};
19+
20+
/// The shared environment for all commands
21+
pub struct AppContext<'a> {
22+
pub network: Network,
23+
pub datadir: PathBuf,
24+
pub wallet: Option<&'a mut Wallet>,
25+
#[cfg(any(
26+
feature = "electrum",
27+
feature = "esplora",
28+
feature = "rpc",
29+
feature = "cbf"
30+
))]
31+
pub client: Option<&'a BlockchainClient>,
32+
}
15833

159-
let result = handle_offline_wallet_subcommand(
160-
&mut wallet,
161-
&wallet_opts,
162-
&cli_opts,
163-
offline_subcommand.clone(),
164-
)?;
165-
wallet.persist(&mut persister)?;
166-
result
167-
};
168-
#[cfg(not(any(feature = "sqlite", feature = "redb")))]
169-
let result = {
170-
let mut wallet = new_wallet(network, &wallet_opts)?;
171-
handle_offline_wallet_subcommand(
172-
&mut wallet,
173-
&wallet_opts,
174-
&cli_opts,
175-
offline_subcommand.clone(),
176-
)?
177-
};
178-
Ok(result)
179-
}
180-
CliSubCommand::Wallet {
181-
wallet,
182-
subcommand: WalletSubCommand::Config { force, wallet_opts },
183-
} => {
184-
let network = cli_opts.network;
185-
let home_dir = prepare_home_dir(cli_opts.datadir)?;
186-
let result = handle_config_subcommand(&home_dir, network, wallet, &wallet_opts, force)?;
187-
Ok(result)
188-
}
189-
CliSubCommand::Wallets => {
190-
let home_dir = prepare_home_dir(cli_opts.datadir)?;
191-
let result = handle_wallets_subcommand(&home_dir, pretty)?;
192-
Ok(result)
34+
impl<'a> AppContext<'a> {
35+
pub fn new(network: Network, datadir: PathBuf) -> Self {
36+
Self {
37+
network,
38+
datadir,
39+
wallet: None,
40+
#[cfg(any(
41+
feature = "electrum",
42+
feature = "esplora",
43+
feature = "rpc",
44+
feature = "cbf"
45+
))]
46+
client: None,
19347
}
194-
CliSubCommand::Key {
195-
subcommand: key_subcommand,
196-
} => {
197-
let network = cli_opts.network;
198-
let result = handle_key_subcommand(network, key_subcommand, pretty)?;
199-
Ok(result)
200-
}
201-
#[cfg(feature = "compiler")]
202-
CliSubCommand::Compile {
203-
policy,
204-
script_type,
205-
} => {
206-
let network = cli_opts.network;
207-
let result = handle_compile_subcommand(network, policy, script_type, pretty)?;
208-
Ok(result)
209-
}
210-
#[cfg(feature = "repl")]
211-
CliSubCommand::Repl {
212-
wallet: wallet_name,
213-
} => {
214-
let home_dir = prepare_home_dir(cli_opts.datadir.clone())?;
215-
let (wallet_opts, network) = load_wallet_config(&home_dir, &wallet_name)?;
216-
217-
#[cfg(any(feature = "sqlite", feature = "redb"))]
218-
let (mut wallet, mut persister) = {
219-
let mut persister: Persister = match &wallet_opts.database_type {
220-
#[cfg(feature = "sqlite")]
221-
DatabaseType::Sqlite => {
222-
let database_path = prepare_wallet_db_dir(&home_dir, &wallet_name)?;
223-
let db_file = database_path.join("wallet.sqlite");
224-
let connection = Connection::open(db_file)?;
225-
log::debug!("Sqlite database opened successfully");
226-
Persister::Connection(connection)
227-
}
228-
#[cfg(feature = "redb")]
229-
DatabaseType::Redb => {
230-
let db = Arc::new(bdk_redb::redb::Database::create(
231-
home_dir.join("wallet.redb"),
232-
)?);
233-
let store = RedbStore::new(db, wallet_name.clone())?;
234-
log::debug!("Redb database opened successfully");
235-
Persister::RedbStore(store)
236-
}
237-
};
238-
let wallet = new_persisted_wallet(network, &mut persister, &wallet_opts)?;
239-
(wallet, persister)
240-
};
241-
#[cfg(not(any(feature = "sqlite", feature = "redb")))]
242-
let mut wallet = new_wallet(network, &loaded_wallet_opts)?;
243-
let home_dir = prepare_home_dir(cli_opts.datadir.clone())?;
244-
let database_path = prepare_wallet_db_dir(&home_dir, &wallet_name)?;
245-
loop {
246-
let line = readline()?;
247-
let line = line.trim();
248-
if line.is_empty() {
249-
continue;
250-
}
48+
}
49+
50+
/// Attach a mutable wallet reference to the context.
51+
pub fn with_wallet(mut self, wallet: &'a mut Wallet) -> Self {
52+
self.wallet = Some(wallet);
53+
self
54+
}
55+
56+
/// Attach a client reference to the context.
57+
#[cfg(any(
58+
feature = "electrum",
59+
feature = "esplora",
60+
feature = "rpc",
61+
feature = "cbf"
62+
))]
63+
pub fn with_client(mut self, client: &'a BlockchainClient) -> Self {
64+
self.client = Some(client);
65+
self
66+
}
67+
}
25168

252-
let result = respond(
253-
network,
254-
&mut wallet,
255-
&wallet_name,
256-
&mut wallet_opts.clone(),
257-
line,
258-
database_path.clone(),
259-
&cli_opts,
260-
)
261-
.await;
262-
#[cfg(any(feature = "sqlite", feature = "redb"))]
263-
wallet.persist(&mut persister)?;
69+
pub trait AsyncCommand {
70+
type Output: FormatOutput;
71+
async fn execute(&self, ctx: &mut AppContext<'_>) -> Result<Self::Output, Error>;
72+
}
26473

265-
match result {
266-
Ok(quit) => {
267-
if quit {
268-
break;
269-
}
270-
}
271-
Err(err) => {
272-
writeln!(std::io::stdout(), "{err}")
273-
.map_err(|e| Error::Generic(e.to_string()))?;
274-
std::io::stdout()
275-
.flush()
276-
.map_err(|e| Error::Generic(e.to_string()))?;
277-
}
278-
}
279-
}
280-
Ok("".to_string())
281-
}
282-
CliSubCommand::Descriptor { desc_type, key } => {
283-
let descriptor = handle_descriptor_command(cli_opts.network, desc_type, key, pretty)?;
284-
Ok(descriptor)
285-
}
286-
CliSubCommand::Completions { shell } => {
287-
clap_complete::generate(
288-
shell,
289-
&mut CliOpts::command(),
290-
"bdk-cli",
291-
&mut std::io::stdout(),
292-
);
74+
/// The command trait
75+
pub trait AppCommand {
76+
type Output: FormatOutput;
29377

294-
Ok("".to_string())
295-
}
296-
};
297-
result
78+
/// The execution logic
79+
fn execute(&self, ctx: &mut AppContext) -> Result<Self::Output, Error>;
29880
}
81+
82+
// context for online and online
83+
// => cli.rs
84+
// handlers/{mod for commands}
85+
// wallet subdir /
86+
// wallet-offline and wallet-online (client mod)

0 commit comments

Comments
 (0)