Mise à jour de GitLab prévue ce samedi 23 octobre 2021 à partir de 9h00 CET

Commit dd3b2f96 authored by Éloïs's avatar Éloïs
Browse files

[feat] create rust bin duniter-dbex (dex)

parent 2e2190d7
......@@ -49,6 +49,12 @@ version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
[[package]]
name = "arc-swap"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
[[package]]
name = "arrayref"
version = "0.3.6"
......@@ -389,6 +395,15 @@ dependencies = [
"vec_map",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "cloudabi"
version = "0.1.0"
......@@ -407,6 +422,17 @@ dependencies = [
"cc",
]
[[package]]
name = "comfy-table"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f97a418f1dee79b100875499e272ea81882c1b931a9c8432e165b595b461752"
dependencies = [
"crossterm",
"strum",
"strum_macros",
]
[[package]]
name = "concurrent-queue"
version = "1.2.2"
......@@ -514,6 +540,31 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "crossterm"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7"
dependencies = [
"bitflags",
"crossterm_winapi",
"lazy_static",
"libc",
"mio",
"parking_lot 0.10.2",
"signal-hook",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c"
dependencies = [
"winapi",
]
[[package]]
name = "cryptoxide"
version = "0.2.1"
......@@ -679,6 +730,22 @@ dependencies = [
"serde",
]
[[package]]
name = "duniter-dbex"
version = "0.1.0"
dependencies = [
"arrayvec",
"comfy-table",
"dirs",
"dubp-common",
"duniter-dbs",
"rayon",
"serde",
"serde_json",
"structopt",
"unwrap",
]
[[package]]
name = "duniter-dbs"
version = "0.1.0"
......@@ -1065,7 +1132,7 @@ dependencies = [
"leveldb_minimal",
"maybe-async",
"mockall",
"parking_lot",
"parking_lot 0.11.0",
"rayon",
"regex",
"serde_json",
......@@ -1118,6 +1185,15 @@ version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98"
[[package]]
name = "lock_api"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
dependencies = [
"scopeguard",
]
[[package]]
name = "lock_api"
version = "0.4.1"
......@@ -1190,6 +1266,29 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mio"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e53a6ea5f38c0a48ca42159868c6d8e1bd56c0451238856cc08d58563643bdc3"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e"
dependencies = [
"socket2",
"winapi",
]
[[package]]
name = "mockall"
version = "0.8.1"
......@@ -1307,6 +1406,15 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "ntapi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2"
dependencies = [
"winapi",
]
[[package]]
name = "num"
version = "0.2.1"
......@@ -1411,6 +1519,16 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "parking_lot"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
dependencies = [
"lock_api 0.3.4",
"parking_lot_core 0.7.2",
]
[[package]]
name = "parking_lot"
version = "0.11.0"
......@@ -1418,8 +1536,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
"lock_api 0.4.1",
"parking_lot_core 0.8.0",
]
[[package]]
name = "parking_lot_core"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
dependencies = [
"cfg-if",
"cloudabi 0.0.3",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
......@@ -1429,7 +1561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
dependencies = [
"cfg-if",
"cloudabi",
"cloudabi 0.1.0",
"instant",
"libc",
"redox_syscall",
......@@ -1846,6 +1978,27 @@ dependencies = [
"opaque-debug",
]
[[package]]
name = "signal-hook"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
dependencies = [
"libc",
"mio",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
dependencies = [
"arc-swap",
"libc",
]
[[package]]
name = "slab"
version = "0.4.2"
......@@ -1865,7 +2018,7 @@ dependencies = [
"fxhash",
"libc",
"log",
"parking_lot",
"parking_lot 0.11.0",
]
[[package]]
......@@ -1898,6 +2051,18 @@ dependencies = [
"syn",
]
[[package]]
name = "socket2"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"winapi",
]
[[package]]
name = "spin"
version = "0.5.2"
......@@ -1934,6 +2099,24 @@ dependencies = [
"syn",
]
[[package]]
name = "strum"
version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b89a286a7e3b5720b9a477b23253bc50debac207c8d21505f8e70b36792f11b5"
[[package]]
name = "strum_macros"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.42"
......
......@@ -29,6 +29,7 @@ rusty-hook = "0.11.2"
[workspace]
members = [
"neon/native",
"rust-bins/duniter-dbex",
"rust-bins/xtask",
"rust-libs/dubp-wot",
"rust-libs/duniter-dbs",
......
[package]
name = "duniter-dbex"
version = "0.1.0"
authors = ["elois <elois@duniter.org>"]
description = "Duniter blockchain DB"
repository = "https://git.duniter.org/nodes/typescript/duniter/rust-bins/duniter-dbs-explorer"
readme = "README.md"
keywords = ["duniter", "database"]
license = "AGPL-3.0"
edition = "2018"
[[bin]]
bench = false
path = "src/main.rs"
name = "dex"
[build-dependencies]
structopt = "0.3.16"
[dependencies]
arrayvec = "0.5.1"
comfy-table = "1.0.0"
dirs = "3.0.1"
dubp-common = { version = "0.25.2", features = ["crypto_scrypt"] }
duniter-dbs = { path = "../../rust-libs/duniter-dbs", default-features = false, features = ["explorer", "leveldb_backend", "sync"] }
rayon = "1.3.1"
serde_json = "1.0.53"
structopt = "0.3.16"
[dev-dependencies]
serde = { version = "1.0.105", features = ["derive"] }
unwrap = "1.2.1"
# Duniter databases explorer (dex)
## Compile
git clone https://git.duniter.org/nodes/typescript/duniter.git
cd duniter
cargo build --release -p duniter-dbex
The binary executable is then here: `target/release/dex`
## Use
See `dex --help`
## Autocompletion
Bash autocompletion script is available here : `target/release/dex.bash`
**Several others Shell are supported : Zsh, Fish, Powershell and Elvish!**
To generate the autocompletion script for your shell, recompile with env var `COMPLETION_SHELL`.
For exemple for fish : `COMPLETION_SHELL=fish cargo build --release -p duniter-dbex`
The autocompletion script can be found in : `target/release/`
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// 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/>.
extern crate structopt;
include!("src/cli.rs");
use std::env;
use structopt::clap::Shell;
fn main() {
// Define out dir
let current_dir = match env::current_dir() {
Err(_e) => return,
Ok(current_dir) => current_dir,
};
let out_dir = current_dir.as_path().join(format!(
"../../target/{}",
env::var("PROFILE").unwrap_or_else(|_| "debug".to_owned())
));
// Define shell
let shell = if let Some(shell_str) = option_env!("COMPLETION_SHELL") {
Shell::from_str(shell_str).expect("Unknown shell")
} else {
Shell::Bash
};
let mut app = Opt::clap();
app.gen_completions(
"dex", // We need to specify the bin name manually
shell, // Then say which shell to build completions for
out_dir,
); // Then say where write the completions to
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// 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 std::{num::NonZeroUsize, path::PathBuf, str::FromStr};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "duniter-dbex", about = "Duniter databases explorer.")]
pub struct Opt {
/// Duniter profile name
#[structopt(short, long)]
pub profile: Option<String>,
/// Duniter home directory
#[structopt(short, long, parse(from_os_str))]
pub home: Option<PathBuf>,
/// database
#[structopt(default_value = "bc_v1", possible_values = &["bc_v1", "bc_v2", "mp_v1"])]
pub database: Database,
#[structopt(subcommand)]
pub cmd: SubCommand,
}
#[derive(Debug)]
pub enum Database {
BcV1,
}
impl FromStr for Database {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bc_v1" => Ok(Self::BcV1),
"bc_v2" | "mp_v1" => unimplemented!(),
_ => unreachable!(),
}
}
}
#[derive(Debug, StructOpt)]
pub enum SubCommand {
/// Count collection entries
Count { collection: String },
/// Get one value
Get { collection: String, key: String },
/// Search values by criteria
Find {
collection: String,
#[structopt(short, long)]
/// Key min
start: Option<String>,
#[structopt(short, long)]
/// Key max
end: Option<String>,
/// Filter keys by a regular expression
#[structopt(short = "k", long)]
key_regex: Option<String>,
/// Show keys only
#[structopt(long)]
keys_only: bool,
/// Filter values by a regular expression
#[structopt(short = "v", long)]
value_regex: Option<String>,
/// Maximum number of entries to be found (Slower because force sequential search)
#[structopt(short, long)]
limit: Option<usize>,
/// Browse the collection upside down
#[structopt(short, long)]
reverse: bool,
/// Step by
#[structopt(long, default_value = "1")]
step: NonZeroUsize,
/// Output format
#[structopt(short, long, default_value = "table-json", possible_values = &["csv", "json", "table-json", "table-properties"])]
output: OutputFormat,
/// Pretty json (Only for output format json or table-json)
#[structopt(long)]
pretty: bool,
/// Show only the specified properties
#[structopt(short, long)]
properties: Vec<String>,
/// Export found data to a file
#[structopt(short, long, parse(from_os_str))]
file: Option<PathBuf>,
},
/// Show database schema
Schema,
}
#[derive(Clone, Copy, Debug)]
pub enum OutputFormat {
Table,
TableJson,
Json,
Csv,
}
impl FromStr for OutputFormat {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"csv" => Ok(Self::Csv),
"json" => Ok(Self::Json),
"table-properties" => Ok(Self::Table),
"table-json" => Ok(Self::TableJson),
_ => unreachable!(),
}
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// 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/>.
#![deny(
clippy::unwrap_used,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unstable_features,
unused_import_braces
)]
mod cli;
mod print_found_data;
mod stringify_json_value;
use self::cli::{Database, Opt, OutputFormat, SubCommand};
use self::stringify_json_value::stringify_json_value;
use comfy_table::Table;
use duniter_dbs::kv_typed::prelude::*;
use duniter_dbs::prelude::*;
use duniter_dbs::regex::Regex;
use duniter_dbs::serde_json::{Map, Value};
use duniter_dbs::smallvec::{smallvec, SmallVec};
use duniter_dbs::BcV1Db;
use duniter_dbs::BcV1DbWritable;
use rayon::prelude::*;
use std::{
collections::{HashMap, HashSet},
fs::File,
io::{stdin, Write},
iter::FromIterator,
time::Instant,
};
use structopt::StructOpt;
const DATA_DIR: &str = "data";
const TOO_MANY_ENTRIES_ALERT: usize = 5_000;
fn main() -> Result<(), String> {
let opt = Opt::from_args();
let home = if let Some(home) = opt.home {
home
} else {
dirs::config_dir()
.ok_or_else(|| {
"Fail to auto find duniter's home directory, please specify it explicitly."
.to_owned()
})?
.as_path()
.join("duniter")
};
let profile_name = if let Some(profile_name) = opt.profile {
profile_name
} else {
"duniter_default".to_owned()
};
let profile_path = home.as_path().join(&profile_name);
let data_path = profile_path.as_path().join(DATA_DIR);
if !data_path.exists() {
return Err(format!(
"Path '{}' don't exist !",
data_path.to_str().expect("non-UTF-8 strings not supported")
));
}
let open_db_start_time = Instant::now();
match opt.database {
Database::BcV1 => apply_subcommand(
BcV1Db::<LevelDb>::open(LevelDbConf {
db_path: data_path.as_path().join("leveldb"),
..Default::default()
})
.map_err(|e| format!("{}", e))?,
opt.cmd,
open_db_start_time,
),
}
}
fn apply_subcommand<DB: DbExplorable>(
db: DB,
cmd: SubCommand,
open_db_start_time: Instant,
) -> Result<(), String> {