mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-08 22:28:12 -05:00
drk: introduced interactive shell infra
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -2468,6 +2468,7 @@ dependencies = [
|
||||
"darkfi_money_contract",
|
||||
"easy-parallel",
|
||||
"lazy_static",
|
||||
"linenoise-rs",
|
||||
"log",
|
||||
"num-bigint",
|
||||
"prettytable-rs",
|
||||
@@ -4098,6 +4099,16 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linenoise-rs"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51e207483a55cd856bc85af576f663b2ebd25616e7b624c4ba42cff9ec2f0631"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
|
||||
@@ -21,6 +21,7 @@ darkfi-serial = "0.5.0"
|
||||
blake3 = "1.8.2"
|
||||
bs58 = "0.5.1"
|
||||
lazy_static = "1.5.0"
|
||||
linenoise-rs = "0.1.1"
|
||||
log = "0.4.27"
|
||||
num-bigint = "0.4.6"
|
||||
prettytable-rs = "0.10.0"
|
||||
|
||||
@@ -26,6 +26,9 @@ wallet_pass = "changeme"
|
||||
# darkfid JSON-RPC endpoint
|
||||
endpoint = "tcp://127.0.0.1:8240"
|
||||
|
||||
# Path to interactive shell history file
|
||||
history_path = "~/.local/share/darkfi/drk/localnet/history.txt"
|
||||
|
||||
# Testnet blockchain network configuration
|
||||
[network_config."testnet"]
|
||||
# Path to blockchain cache database
|
||||
@@ -40,6 +43,9 @@ wallet_pass = "changeme"
|
||||
# darkfid JSON-RPC endpoint
|
||||
endpoint = "tcp://127.0.0.1:8340"
|
||||
|
||||
# Path to interactive shell history file
|
||||
history_path = "~/.local/share/darkfi/drk/testnet/history.txt"
|
||||
|
||||
# Mainnet blockchain network configuration
|
||||
[network_config."mainnet"]
|
||||
# Path to blockchain cache database
|
||||
@@ -53,3 +59,6 @@ wallet_pass = "changeme"
|
||||
|
||||
# darkfid JSON-RPC endpoint
|
||||
endpoint = "tcp://127.0.0.1:8440"
|
||||
|
||||
# Path to interactive shell history file
|
||||
history_path = "~/.local/share/darkfi/drk/mainnet/history.txt"
|
||||
|
||||
@@ -113,6 +113,9 @@ pub async fn kaching() {
|
||||
pub fn generate_completions(shell: &str) -> Result<()> {
|
||||
// Sub-commands
|
||||
|
||||
// Interactive
|
||||
let interactive = SubCommand::with_name("interactive").about("Enter Drk interactive shell");
|
||||
|
||||
// Kaching
|
||||
let kaching = SubCommand::with_name("kaching").about("Fun");
|
||||
|
||||
@@ -504,6 +507,7 @@ pub fn generate_completions(shell: &str) -> Result<()> {
|
||||
.help("Blockchain network to use");
|
||||
|
||||
let command = vec![
|
||||
interactive,
|
||||
kaching,
|
||||
ping,
|
||||
completions,
|
||||
|
||||
152
bin/drk/src/interactive.rs
Normal file
152
bin/drk/src/interactive.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
/* This file is part of DarkFi (https://dark.fi)
|
||||
*
|
||||
* Copyright (C) 2020-2025 Dyne.org foundation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use linenoise_rs::{
|
||||
linenoise, linenoise_history_add, linenoise_history_load, linenoise_history_save,
|
||||
linenoise_set_completion_callback, linenoise_set_hints_callback,
|
||||
};
|
||||
|
||||
use darkfi::{system::StoppableTask, util::path::expand_path};
|
||||
|
||||
use crate::{
|
||||
cli_util::{generate_completions, kaching},
|
||||
Drk,
|
||||
};
|
||||
|
||||
// TODO:
|
||||
// 1. add rest commands handling, along with their completions and hints.
|
||||
// 2. add input definitions, so you input from files not just stdin.
|
||||
// 3. add output definitions, so you can output to files not just stdout.
|
||||
// 4. create a transactions cache in the wallet db, so you can use it to handle them.
|
||||
|
||||
/// Auxiliary function to define the interactive shell completions.
|
||||
fn completion(buf: &str, lc: &mut Vec<String>) {
|
||||
if buf.starts_with("h") {
|
||||
lc.push("help".to_string());
|
||||
return
|
||||
}
|
||||
|
||||
if buf.starts_with("k") {
|
||||
lc.push("kaching".to_string());
|
||||
return
|
||||
}
|
||||
|
||||
if buf.starts_with("p") {
|
||||
lc.push("ping".to_string());
|
||||
return
|
||||
}
|
||||
|
||||
if buf.starts_with("c") {
|
||||
lc.push("completions".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliary function to define the interactive shell hints.
|
||||
fn hints(buf: &str) -> Option<(String, i32, bool)> {
|
||||
match buf {
|
||||
"completions " => Some(("{shell}".to_string(), 35, false)), // 35 = magenta
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliary function to start provided Drk as an interactive shell.
|
||||
pub async fn interactive(drk: &Drk, history_path: &str) {
|
||||
// Expand the history file path
|
||||
let history_path = match expand_path(history_path) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
println!("Error while expanding history file path: {e}");
|
||||
return
|
||||
}
|
||||
};
|
||||
let history_path = history_path.into_os_string();
|
||||
let history_file = history_path.to_str().unwrap();
|
||||
|
||||
// Set the completion callback. This will be called every time the
|
||||
// user uses the <tab> key.
|
||||
linenoise_set_completion_callback(completion);
|
||||
|
||||
// Set the shell hints
|
||||
linenoise_set_hints_callback(hints);
|
||||
|
||||
// Load history from file.The history file is just a plain text file
|
||||
// where entries are separated by newlines.
|
||||
let _ = linenoise_history_load(history_file);
|
||||
|
||||
// Create a detached task to use for block subscription
|
||||
let subscription_active = false;
|
||||
let subscription_task = StoppableTask::new();
|
||||
|
||||
// Start the interactive shell
|
||||
loop {
|
||||
// Grab input or end if Ctrl-D or Ctrl-C was pressed
|
||||
let Some(line) = linenoise("drk> ") else {
|
||||
// Stop the subscription task if its active
|
||||
if subscription_active {
|
||||
subscription_task.stop().await;
|
||||
}
|
||||
|
||||
// Write history file
|
||||
let _ = linenoise_history_save(history_file);
|
||||
|
||||
return
|
||||
};
|
||||
|
||||
// Check if line is empty
|
||||
if line.is_empty() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Add line to history
|
||||
linenoise_history_add(&line);
|
||||
|
||||
// Parse command parts
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.is_empty() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle command
|
||||
match parts[0] {
|
||||
"help" => println!("hhhheeeelp"),
|
||||
"kaching" => kaching().await,
|
||||
"ping" => handle_ping(drk).await,
|
||||
"completions" => handle_completions(&parts),
|
||||
_ => println!("Unreconized command: {}", parts[0]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliary function to define the ping command handling.
|
||||
async fn handle_ping(drk: &Drk) {
|
||||
if let Err(e) = drk.ping().await {
|
||||
println!("Error while executing ping command: {e}")
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliary function to define the completions command handling.
|
||||
fn handle_completions(parts: &[&str]) {
|
||||
if parts.len() != 2 {
|
||||
println!("Malformed `completions` command");
|
||||
println!("Usage: completions {{shell}}");
|
||||
}
|
||||
|
||||
if let Err(e) = generate_completions(parts[1]) {
|
||||
println!("Error while executing completions command: {e}")
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,9 @@ pub mod token;
|
||||
/// CLI utility functions
|
||||
pub mod cli_util;
|
||||
|
||||
/// Drk interactive shell
|
||||
pub mod interactive;
|
||||
|
||||
/// Wallet functionality related to Money
|
||||
pub mod money;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ use drk::{
|
||||
generate_completions, kaching, parse_token_pair, parse_tx_from_stdin, parse_value_pair,
|
||||
},
|
||||
dao::{DaoParams, ProposalRecord},
|
||||
interactive::interactive,
|
||||
money::BALANCE_BASE10_DECIMALS,
|
||||
swap::PartialSwapData,
|
||||
Drk,
|
||||
@@ -100,6 +101,9 @@ struct Args {
|
||||
// don't forget to update cli_util::generate_completions()
|
||||
#[derive(Clone, Debug, Deserialize, StructOpt)]
|
||||
enum Subcmd {
|
||||
/// Enter Drk interactive shell
|
||||
Interactive,
|
||||
|
||||
/// Fun
|
||||
Kaching,
|
||||
|
||||
@@ -565,6 +569,10 @@ struct BlockchainNetwork {
|
||||
#[structopt(short, long, default_value = "tcp://127.0.0.1:8240")]
|
||||
/// darkfid JSON-RPC endpoint
|
||||
endpoint: Url,
|
||||
|
||||
#[structopt(long, default_value = "~/.local/share/darkfi/drk/localnet/history.txt")]
|
||||
/// Path to interactive shell history file
|
||||
history_path: String,
|
||||
}
|
||||
|
||||
/// Auxiliary function to parse darkfid configuration file and extract requested
|
||||
@@ -648,6 +656,20 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
|
||||
};
|
||||
|
||||
match args.command {
|
||||
Subcmd::Interactive => {
|
||||
let drk = new_wallet(
|
||||
blockchain_config.cache_path,
|
||||
blockchain_config.wallet_path,
|
||||
blockchain_config.wallet_pass,
|
||||
Some(blockchain_config.endpoint),
|
||||
ex,
|
||||
args.fun,
|
||||
)
|
||||
.await;
|
||||
interactive(&drk, &blockchain_config.history_path).await;
|
||||
drk.stop_rpc_client().await
|
||||
}
|
||||
|
||||
Subcmd::Kaching => {
|
||||
if !args.fun {
|
||||
println!("Apparently you don't like fun...");
|
||||
|
||||
@@ -22,3 +22,6 @@ wallet_pass = "testing"
|
||||
|
||||
# darkfid JSON-RPC endpoint
|
||||
endpoint = "tcp://127.0.0.1:48240"
|
||||
|
||||
# Path to interactive shell history file
|
||||
history_path = "drk/history.txt"
|
||||
|
||||
Reference in New Issue
Block a user