@@ -4,295 +4,83 @@ pub mod key;
44pub mod offline;
55pub mod online;
66pub 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