This commit is contained in:
victor 2025-09-04 01:37:11 +02:00
parent 61d71006f7
commit f9ae2949e4
47 changed files with 8058 additions and 1294 deletions

1542
cli-renderer/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

12
cli-renderer/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "cli-renderer"
version = "0.1.0"
edition = "2024"
[dependencies]
crossterm = "0.29.0"
ratatui = "0.29.0"
ws = "0.9.2"
vlogger = { path = "../lib/logger-rs" }
clap = { version = "4.5.47", features = ["derive"] }
textwrap = "0.16.2"

View File

@ -9,6 +9,8 @@ pub enum RenderLayoutKind {
CliHorizontal,
#[value(name = "vertical", aliases = ["v"])]
CliVertical,
CliOutputSplit,
}
const CLI_INPUT_PREFIX: &str = "> ";
@ -88,6 +90,14 @@ impl RenderLayout {
),
],
},
RenderLayoutKind::CliOutputSplit => {
let layout = Layout::default()
.constraints([Constraint::Percentage(70), Constraint::Percentage(30)]);
RenderLayout {
layout,
panes: vec![]
}
}
}
}
}

11
cli-renderer/src/lib.rs Normal file
View File

@ -0,0 +1,11 @@
pub mod renderer;
pub use renderer::*;
pub mod command;
pub use command::*;
pub mod layout;
pub use layout::*;
pub mod pane;
pub use pane::*;

View File

@ -25,11 +25,8 @@ pub enum RenderBuffer {
#[derive(Debug, PartialEq, Clone)]
pub enum RenderTarget {
All,
CliInput,
CliOutput,
PopUp
}

View File

@ -4,7 +4,6 @@ use crossterm::event::KeyCode;
use ratatui::{Frame, buffer::Buffer, layout::Rect, widgets::Widget};
use vlogger::*;
use crate::log;
use super::*;
#[derive(Debug, Clone)]
@ -19,6 +18,7 @@ pub struct Renderer {
exit: bool,
layout: RenderLayout,
mode: InputMode,
area: Rect,
}
#[allow(dead_code)]
@ -29,11 +29,12 @@ impl Renderer {
exit: false,
layout: RenderLayout::generate(layout),
mode: InputMode::Input,
area: Default::default(),
}
}
fn log(&mut self, msg: String) {
self.buffer.push_str(&msg)
self.apply(RenderCommand::StringToPaneFocused{str: msg})
}
pub fn draw(&mut self, frame: &mut Frame) {
@ -65,8 +66,12 @@ impl Renderer {
self.layout.panes.iter_mut().find(|p| p.focused == true)
}
pub fn rects(&self, area: Rect) -> std::rc::Rc<[Rect]> {
self.layout.rects(area)
pub fn rects(&self) -> std::rc::Rc<[Rect]> {
self.layout.rects(self.area)
}
pub fn set_area(&mut self, area: Rect) {
self.area = area;
}
pub fn handle_mouse_click_left(&mut self, x: u16, y: u16, rects: std::rc::Rc<[Rect]>) {
@ -128,7 +133,6 @@ impl Renderer {
RenderTarget::PopUp => {
match &mut pane.buffer {
RenderBuffer::Select(content, idx) => {
log(msg!(DEBUG, "idx before: {idx}"));
match key {
KeyCode::Up => { *idx = idx.saturating_sub(1) }
KeyCode::Down => {
@ -138,7 +142,6 @@ impl Renderer {
}
_ => {}
}
log(msg!(DEBUG, "idx after: {idx}"))
}
_ => {}
}
@ -219,8 +222,8 @@ impl Renderer {
}
}
pub fn apply(&mut self, mes: RenderCommand, area: Rect) {
let rects = self.layout.rects(area);
pub fn apply(&mut self, mes: RenderCommand) {
let rects = self.rects();
match mes {
RenderCommand::MouseClickLeft(x, y) => self.handle_mouse_click_left(x, y, rects),
RenderCommand::MouseScrollUp => self.handle_scroll_up(),

666
node/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -29,3 +29,5 @@ bincode = { version = "2.0.1", features = ["derive", "serde"] }
futures = "0.3.31"
secp256k1 = { version = "0.31.1", features = ["hashes", "rand", "recovery", "serde"] }
ring = "0.17.14"
shared = { path = "../shared", features = ["node"] }
cli-renderer = { path = "../cli-renderer" }

View File

@ -1,7 +1,7 @@
use std::net::SocketAddr;
use crate::core;
use crate::renderer::RenderLayoutKind;
use shared::core;
use cli_renderer::RenderLayoutKind;
use clap::{Parser, Subcommand};
use clap::*;

View File

@ -1,8 +1,8 @@
use crate::args::*;
use crate::core::ChainData;
use shared::core::ChainData;
use crate::executor::ExecutorCommand;
use crate::node::*;
use crate::renderer::RenderCommand;
use cli_renderer::RenderCommand;
use clap::Parser;
pub fn handle_peer_command(cmd: CliPeerCommand) -> NodeCommand {

View File

@ -1,51 +0,0 @@
use sha2::{Sha256, Digest};
use secp256k1::rand;
use secp256k1::{Secp256k1, PublicKey, SecretKey};
pub type Address = String;
#[derive(Debug, bincode::Decode, bincode::Encode)]
pub struct Account {
address: Address,
balance: u64,
nonce: u64,
}
impl Account {
pub fn public_key_to_address(public_key: &PublicKey) -> Address {
let pk_ser = public_key.serialize();
let hash = Sha256::digest(&pk_ser);
hex::encode(&hash[..20])
}
pub fn nonce(&self) -> u64 {
self.nonce
}
pub fn balance(&self) -> u64 {
self.balance
}
pub fn address(&self) -> &Address {
&self.address
}
pub fn new() -> Self {
let secp = Secp256k1::new();
let (secret_key, public_key) = secp.generate_keypair(&mut rand::rng());
let addr = Self::public_key_to_address(&public_key);
println!("{:?}", secret_key);
Self {
address: addr,
balance: 0,
nonce: 0,
}
}
}
#[test]
fn test_acc_new() {
Account::new();
}

View File

@ -2,14 +2,15 @@ use bincode::{self, config::Configuration};
use sled::{self, Batch};
use std::sync::Arc;
use vlogger::*;
use shared::core::{self, Block, ChainData, Hasher, Address};
use crate::{
core::{self, Block, ChainData, Hasher, Address, Account},
db::error::DatabaseError,
error::print_error_chain,
log,
};
use shared::core::print_error_chain;
static BINCODE_CONFIG: Configuration = bincode::config::standard();
const DB_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/database");
@ -21,7 +22,6 @@ const CHAIN_DATA_PREFIX: &str = "chain_data";
const DATA_TO_BLOCK_PREFIX: &str = "data_to_block";
const METADATA_PREFIX: &str = "metadata";
const BALANCE_PREFIX: &str = "balance";
const ACCOUNT_PREFIX: &str = "account";
const MEMPOOL_PREFIX: &str = "mempool";
@ -112,7 +112,7 @@ impl ChainDb {
}
}
pub fn insert_account(&self, acc: &Account) -> Result<(), DatabaseError> {
fn insert_account(&self, acc: &Account) -> Result<(), DatabaseError> {
let mut batch = Batch::default();
if let Some(_) = self.get_account(acc.address())? {
return Err(DatabaseError::AccountExists(acc.address().to_string()));

View File

@ -1,35 +1,6 @@
use crate::log;
use thiserror::Error;
#[allow(dead_code)]
#[derive(Error, Debug)]
pub enum TxError {
#[error("from field is empty")]
FromEmpty,
#[error("to field is empty")]
ToEmpty,
#[error("insuffitient fonds")]
FromInsuffitientFonds,
#[error("0 value transaction")]
ValueEmpty,
#[error("account {0} not found in database")]
UnknownAccount(String),
}
pub fn print_error_chain(err: &anyhow::Error) {
let mut err_string = String::from(format!("Error: {}\n", err));
let mut source = err.source();
let mut level = 1;
while let Some(err) = source {
err_string.push_str(format!(" {}: {}\n", level, err).as_str());
source = err.source();
level += 1;
}
log(err_string)
}
#[derive(Debug, Error)]
pub enum SystemError<T>
where

View File

@ -1,5 +1,5 @@
use crate::node::NodeCommand;
use crate::renderer::RenderCommand;
use cli_renderer::RenderCommand;
use crate::watcher::WatcherCommand;
#[derive(Clone, Debug)]

View File

@ -2,9 +2,10 @@ use crate::{
bus::{publish_system_event, publish_watcher_event, subscribe_system_event, SystemEvent},
log,
node::NodeCommand,
renderer::RenderTarget,
watcher::WatcherCommand,
};
use cli_renderer::pane::RenderTarget;
use thiserror::Error;
use tokio::{select, sync::mpsc};
use vlogger::*;

View File

@ -4,6 +4,9 @@ pub mod node {
pub mod error;
pub use error::*;
pub mod blockchain;
pub use blockchain::*;
}
pub mod cli;
@ -12,26 +15,6 @@ pub mod args;
pub mod error;
pub mod core {
pub mod block;
pub use block::*;
pub mod blockchain;
pub use blockchain::*;
pub mod tx;
pub use tx::*;
pub mod data;
pub use data::*;
pub mod hasher;
pub use hasher::*;
pub mod account;
pub use account::*;
}
pub mod db {
pub mod database;
pub mod error;
@ -61,20 +44,6 @@ pub mod executor {
pub use command::*;
}
pub mod renderer {
pub mod renderer;
pub use renderer::*;
pub mod pane;
pub use pane::*;
pub mod layout;
pub use layout::*;
pub mod command;
pub use command::*;
}
pub mod watcher {
pub mod builder;
pub mod watcher;
@ -99,7 +68,7 @@ pub mod protocol {
pub mod seeds_constants;
use crate::renderer::{RenderCommand, RenderTarget};
use cli_renderer::{RenderCommand, RenderTarget};
pub fn log(msg: String) {
crate::bus::publish_watcher_event(watcher::WatcherCommand::Render(RenderCommand::StringToPaneId {

View File

@ -3,15 +3,21 @@ use std::time::UNIX_EPOCH;
use thiserror::*;
use crate::core;
use crate::core::ChainData;
use shared::core;
use shared::core::ChainData;
use crate::db;
use crate::db::DatabaseError;
use crate::db::database;
use crate::error::TxError;
use crate::log;
use super::hasher::Hasher;
use shared::core::Hasher;
#[allow(dead_code)]
#[derive(Debug)]
pub struct Blockchain {
id: String,
mempool: Vec<ChainData>,
db: database::ChainDb,
}
#[allow(dead_code)]
#[derive(Error, Debug)]
@ -23,7 +29,7 @@ pub enum BlockchainError {
InvalidAccountCreation,
#[error("Transactional error")]
Tx(#[from] TxError),
Tx(#[from] shared::core::TxError),
#[error("Validation Error")]
Validation(#[from] ValidationError),
@ -47,14 +53,6 @@ pub enum ValidationError {
InvalidBlockJson(#[from] serde_json::Error),
}
#[allow(dead_code)]
#[derive(Debug)]
pub struct Blockchain {
id: String,
mempool: Vec<ChainData>,
db: database::ChainDb,
}
#[allow(dead_code)]
impl Blockchain {
fn hash_transaction_pool(&self) -> Vec<String> {

View File

@ -1,12 +1,13 @@
use crate::bus::{publish_system_event, publish_watcher_event, subscribe_system_event, SystemEvent};
use crate::core::{self, Blockchain, BlockchainError, ChainData, ValidationError};
use crate::error::print_error_chain;
use shared::core::{self, ChainData};
use shared::core::print_error_chain;
use crate::executor::ExecutorCommand;
use crate::log;
use crate::protocol::ProtocolMessage;
use crate::protocol::{Connector, ConnectorCommand};
use crate::seeds_constants::SEED_NODES;
use crate::watcher::{WatcherCommand, WatcherMode};
use super::{ Blockchain, BlockchainError, ValidationError };
use std::collections::HashMap;
use std::net::SocketAddr;
@ -202,7 +203,7 @@ impl Node {
}
ProtocolMessage::BootstrapResponse { blocks } => {
log(msg!(DEBUG, "Received BootstrapResponse from seed"));
self.chain = core::Blockchain::build(blocks).unwrap();
self.chain = Blockchain::build(blocks).unwrap();
}
ProtocolMessage::Pong { peer_id } => {
log(msg!(DEBUG, "Received Pong from {peer_id}"));

View File

@ -4,17 +4,16 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net;
use tokio::sync::mpsc;
use vlogger::*;
use shared::core::print_error_chain;
use thiserror::*;
use crate::error::print_error_chain;
use crate::log;
use super::Connection;
use crate::bus::*;
use crate::executor::ExecutorCommand;
use crate::node::node;
use crate::node::{NetworkError, error};
use crate::protocol::ProtocolMessage;
use thiserror::*;
pub enum ConnectorCommand {
ConnectToTcpPeer(SocketAddr),

View File

@ -1,4 +1,4 @@
use crate::core::{self, ChainData};
use shared::core::{self, ChainData};
use std::fmt;
use std::net::SocketAddr;

View File

@ -1,26 +0,0 @@
#[derive(Clone, Debug)]
pub enum RenderCommand {
StringToPaneId {
str: String,
pane: RenderTarget,
},
StringToPaneFocused {
str: String,
},
KeyInput(KeyCode),
ListMove {
pane: RenderTarget,
index: usize,
},
ChangeLayout(RenderLayoutKind),
ClearPane,
/// Mouse Events
MouseClickLeft(u16, u16),
MouseScrollUp,
MouseScrollDown,
SetMode(InputMode),
Exit,
}

View File

@ -1,93 +0,0 @@
use ratatui::layout::{ Rect, Flex };
use ratatui::prelude::*;
use super::{Pane, RenderBuffer, RenderTarget};
#[derive(Debug, Clone, clap::ValueEnum)]
pub enum RenderLayoutKind {
#[value(name = "horizontal", aliases = ["h"])]
CliHorizontal,
#[value(name = "vertical", aliases = ["v"])]
CliVertical,
}
const CLI_INPUT_PREFIX: &str = "> ";
#[derive(Debug)]
pub struct RenderLayout {
layout: Layout,
pub panes: Vec<Pane>,
}
impl Widget for &mut RenderLayout {
fn render(self, area: Rect, buffer: &mut Buffer) {
let rects = self.rects(area);
for (p, r) in self.panes.iter_mut().zip(rects.iter()) {
p.render(*r, buffer);
}
}
}
pub fn center(area: Rect, horizontal: Constraint, vertical: Constraint) -> Rect {
let [area] = Layout::horizontal([horizontal])
.flex(Flex::Center)
.areas(area);
let [area] = Layout::vertical([vertical]).flex(Flex::Center).areas(area);
area
}
impl RenderLayout {
pub fn rects(&self, area: Rect) -> std::rc::Rc<[Rect]> {
self.layout.split(area)
}
pub fn generate(kind: RenderLayoutKind) -> RenderLayout {
match kind {
RenderLayoutKind::CliVertical => RenderLayout {
layout: Layout::default()
.constraints([Constraint::Percentage(70), Constraint::Percentage(30)]),
panes: vec![
Pane::new(
Some(" Input Pane ".to_string()),
RenderTarget::CliInput,
RenderBuffer::List {
list: vec![String::new()],
index: 0,
prefix: CLI_INPUT_PREFIX,
},
true,
),
Pane::new(
Some(" Output Pane ".to_string()),
RenderTarget::CliOutput,
RenderBuffer::String(String::new()),
false,
),
],
},
RenderLayoutKind::CliHorizontal => RenderLayout {
layout: Layout::default()
.constraints([Constraint::Percentage(70), Constraint::Percentage(30)]),
panes: vec![
Pane::new(
Some(" Output Pane ".to_string()),
RenderTarget::CliOutput,
RenderBuffer::String(String::new()),
false,
),
Pane::new(
Some(" Input Pane ".to_string()),
RenderTarget::CliInput,
RenderBuffer::List {
list: vec![String::new()],
index: 0,
prefix: CLI_INPUT_PREFIX,
},
true,
),
],
},
}
}
}

View File

@ -4,11 +4,10 @@ use tokio::sync::mpsc;
use vlogger::*;
use crate::bus::{NetworkEvent, SystemEvent, subscribe_system_event};
use crate::core;
use crate::executor::{Executor, ExecutorCommand};
use crate::log;
use crate::{log, node};
use crate::node::{Node, NodeCommand};
use crate::renderer::{RenderLayoutKind, Renderer};
use cli_renderer::{RenderLayoutKind, Renderer};
use crate::watcher::Watcher;
#[derive(Default)]
@ -71,7 +70,7 @@ impl WatcherBuilder {
self.addr = Some(crate::seeds_constants::SEED_NODES[0]);
}
let chain = core::Blockchain::build(None).unwrap();
let chain = node::Blockchain::build(None).unwrap();
let mut node = Node::new(self.addr.clone(), exec_tx.clone(), chain);
log(msg!(INFO, "Built Node"));

View File

@ -1,6 +1,7 @@
use std::sync::Arc;
use crate::{executor::ExecutorCommand, renderer::RenderCommand};
use cli_renderer::RenderCommand;
use crate::executor::ExecutorCommand;
#[derive(Debug, Clone)]
pub enum WatcherCommand {

View File

@ -1,9 +1,15 @@
use crate::{bus::{publish_system_event, subscribe_system_event, SystemEvent}, cli::cli, error::print_error_chain, node::node::NodeCommand, watcher::WatcherMode};
use crate::{
bus::{publish_system_event, subscribe_system_event, SystemEvent},
cli::cli,
node::node::NodeCommand,
watcher::WatcherMode
};
use shared::core::print_error_chain;
use crossterm::event::{Event, EventStream, KeyCode, KeyEventKind, MouseButton, MouseEventKind};
use futures::StreamExt;
use memory_stats::memory_stats;
use ratatui::{layout::Rect, Terminal};
use std::io::{self, Stdout, Write};
use std::io::{self, Write};
use tokio::{
select,
sync::mpsc,
@ -17,7 +23,11 @@ use crate::bus::subscribe_watcher_event;
use crate::executor::*;
use crate::log;
use crate::renderer::*;
use cli_renderer::{
Renderer,
InputMode,
RenderCommand
};
#[allow(dead_code)]
pub struct Watcher {
@ -78,19 +88,17 @@ impl Watcher {
)
}
pub fn handle_cmd(&mut self, cmd: WatcherCommand, terminal: &mut Terminal<ratatui::backend::CrosstermBackend<Stdout>>) {
pub fn handle_cmd(&mut self, cmd: WatcherCommand) {
match cmd {
WatcherCommand::Render(rend_cmd) => {
let frame = terminal.get_frame();
self.renderer.apply(rend_cmd, frame.area());
self.renderer.apply(rend_cmd);
}
WatcherCommand::SetMode(mode) => {
match &mode {
WatcherMode::Input => {}
WatcherMode::Select{content, title, ..} => {
let rd_cmd = RenderCommand::SetMode(InputMode::PopUp(content.clone(), title.clone(), 0));
let frame = terminal.get_frame();
self.renderer.apply(rd_cmd, frame.area());
self.renderer.apply(rd_cmd);
}
}
self.mode = mode;
@ -111,7 +119,8 @@ impl Watcher {
poll_res = self.poll() => {
match poll_res {
Ok(event) => {
match self.handle_event(event, terminal.get_frame().area()).await {
self.renderer.set_area(terminal.get_frame().area());
match self.handle_event(event).await {
Ok(ret) => if !ret { self.exit(); break }
Err(e) => log(msg!(ERROR, "{}", e)),
}
@ -122,7 +131,8 @@ impl Watcher {
ui_event = ui_rx.recv() => {
match ui_event {
Ok(cmd) => {
self.handle_cmd(cmd, &mut terminal);
self.renderer.set_area(terminal.get_frame().area());
self.handle_cmd(cmd);
},
Err(e) => {
log(msg!(ERROR, "{}", e))
@ -140,6 +150,7 @@ impl Watcher {
}
}
_ = render_interval.tick() => {
self.renderer.set_area(terminal.get_frame().area());
terminal.draw(|frame| self.renderer.draw(frame))?;
}
}
@ -183,7 +194,7 @@ impl Watcher {
}
self.mode = WatcherMode::Input;
let rd_cmd = RenderCommand::SetMode(InputMode::Input);
self.renderer.apply(rd_cmd, Rect::default());
self.renderer.apply(rd_cmd);
}
}
}
@ -251,7 +262,7 @@ impl Watcher {
});
}
pub async fn handle_event(&mut self, event: Event, area: Rect) -> io::Result<bool> {
pub async fn handle_event(&mut self, event: Event) -> io::Result<bool> {
match event {
Event::Mouse(event) => match event.kind {
MouseEventKind::ScrollUp => {
@ -262,7 +273,7 @@ impl Watcher {
}
MouseEventKind::Down(b) => match b {
MouseButton::Left => {
let rects = self.renderer.rects(area);
let rects = self.renderer.rects();
self
.renderer
.handle_mouse_click_left(event.column, event.row, rects);

620
shared/Cargo.lock generated Normal file
View File

@ -0,0 +1,620 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anstream"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "bincode"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
dependencies = [
"bincode_derive",
"serde",
"unty",
]
[[package]]
name = "bincode_derive"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09"
dependencies = [
"virtue",
]
[[package]]
name = "bitcoin-io"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
[[package]]
name = "bitcoin_hashes"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
dependencies = [
"bitcoin-io",
"hex-conservative",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cc"
version = "1.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "clap"
version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hex-conservative"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
dependencies = [
"arrayvec",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "libc"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "once_cell_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "secp256k1"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2"
dependencies = [
"bitcoin_hashes",
"rand",
"secp256k1-sys",
]
[[package]]
name = "secp256k1-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38"
dependencies = [
"cc",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shared"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"clap",
"hex",
"secp256k1",
"serde",
"serde_json",
"sha2",
"thiserror",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unty"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "virtue"
version = "0.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
[[package]]
name = "wasi"
version = "0.14.3+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wit-bindgen"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
[[package]]
name = "zerocopy"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

21
shared/Cargo.toml Normal file
View File

@ -0,0 +1,21 @@
[package]
name = "shared"
version = "0.1.0"
edition = "2024"
[features]
default = []
node = []
client = []
full = ["node", "client"]
[dependencies]
anyhow = "1.0.99"
bincode = "2.0.1"
clap = { version = "4.5.47", features = ["derive"] }
hex = "0.4.3"
secp256k1 = { version = "0.31.1", features = ["rand"] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.143"
sha2 = "0.10.9"
thiserror = "2.0.16"

View File

@ -0,0 +1 @@
pub type Address = String;

View File

@ -1,5 +1,7 @@
use crate::error::TxError;
use super::{ Address, Account};
use super::Address;
use crate::log;
use thiserror::Error;
//use secp256k1::{
// ecdsa::{RecoveryId, Signature},
@ -8,6 +10,36 @@ use super::{ Address, Account};
//
//use ring::digest;
//
#[allow(dead_code)]
#[derive(Error, Debug)]
pub enum TxError {
#[error("from field is empty")]
FromEmpty,
#[error("to field is empty")]
ToEmpty,
#[error("insuffitient fonds")]
FromInsuffitientFonds,
#[error("0 value transaction")]
ValueEmpty,
#[error("account {0} not found in database")]
UnknownAccount(String),
}
pub fn print_error_chain(err: &anyhow::Error) {
let mut err_string = String::from(format!("Error: {}\n", err));
let mut source = err.source();
let mut level = 1;
while let Some(err) = source {
err_string.push_str(format!(" {}: {}\n", level, err).as_str());
source = err.source();
level += 1;
}
log(err_string)
}
#[derive(
serde::Deserialize, serde::Serialize, Debug, clap::Args, Clone, bincode::Encode, bincode::Decode,
)]
@ -21,13 +53,13 @@ pub struct Tx {
}
impl Tx {
pub fn new(from: &Account, to: Address, value: u64, data: String) -> Self {
pub fn new(from: Address, to: Address, value: u64, nonce: u64, data: String) -> Self {
Self {
from: from.address().to_string(),
from,
to,
value,
data,
nonce: from.nonce() + 1,
nonce,
signature: vec![],
}
}

20
shared/src/lib.rs Normal file
View File

@ -0,0 +1,20 @@
pub mod core {
pub mod block;
pub use block::*;
pub mod tx;
pub use tx::*;
pub mod data;
pub use data::*;
pub mod hasher;
pub use hasher::*;
pub mod address;
pub use address::*;
}
pub fn log(msg: String) {
println!("{msg}")
}

0
shared/src/utils.rs Normal file
View File

3344
wallet/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -3,9 +3,235 @@ name = "wallet"
version = "0.1.0"
edition = "2024"
[package.metadata.docs.rs]
all-features = true
targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
[dependencies]
crossterm = "0.29.0"
ratatui = "0.29.0"
ws = "0.9.2"
vlogger = { path = "../lib/logger-rs" }
clap = { version = "4.5.47", features = ["derive"] }
egui = "0.32"
eframe = { version = "0.32", default-features = false, features = [
"accesskit", # Make egui compatible with screen readers. NOTE: adds a lot of dependencies.
"default_fonts", # Embed the default egui fonts.
"glow", # Use the glow rendering backend. Alternative: "wgpu".
"persistence", # Enable restoring app state when restarting the app.
"wayland", # To support Linux (and CI)
"x11", # To support older Linux distributions (restores one of the default features)
] }
log = "0.4.27"
# You only need serde if you want app persistence:
serde = { version = "1.0.219", features = ["derive"] }
# native:
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
env_logger = "0.11.8"
# web:
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-futures = "0.4.50"
web-sys = "0.3.70" # to access the DOM (to hide the loading text)
[profile.release]
opt-level = 2 # fast and small wasm
# Optimize all dependencies even in debug builds:
[profile.dev.package."*"]
opt-level = 2
[patch.crates-io]
# If you want to use the bleeding edge version of egui and eframe:
# egui = { git = "https://github.com/emilk/egui", branch = "main" }
# eframe = { git = "https://github.com/emilk/egui", branch = "main" }
# If you fork https://github.com/emilk/egui you can test with:
# egui = { path = "../egui/crates/egui" }
# eframe = { path = "../egui/crates/eframe" }
# ----------------------------------------------------------------------------------------
# Lints:
[lints]
workspace = true
[workspace.lints.rust]
unsafe_code = "deny"
elided_lifetimes_in_paths = "warn"
future_incompatible = { level = "warn", priority = -1 }
nonstandard_style = { level = "warn", priority = -1 }
rust_2018_idioms = { level = "warn", priority = -1 }
rust_2021_prelude_collisions = "warn"
semicolon_in_expressions_from_macros = "warn"
trivial_numeric_casts = "warn"
unsafe_op_in_unsafe_fn = "warn" # `unsafe_op_in_unsafe_fn` may become the default in future Rust versions: https://github.com/rust-lang/rust/issues/71668
unused_extern_crates = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
trivial_casts = "allow"
unused_qualifications = "allow"
[workspace.lints.rustdoc]
all = "warn"
missing_crate_level_docs = "warn"
[workspace.lints.clippy]
allow_attributes = "warn"
as_ptr_cast_mut = "warn"
await_holding_lock = "warn"
bool_to_int_with_if = "warn"
char_lit_as_u8 = "warn"
checked_conversions = "warn"
clear_with_drain = "warn"
cloned_instead_of_copied = "warn"
dbg_macro = "warn"
debug_assert_with_mut_call = "warn"
derive_partial_eq_without_eq = "warn"
disallowed_macros = "warn" # See clippy.toml
disallowed_methods = "warn" # See clippy.toml
disallowed_names = "warn" # See clippy.toml
disallowed_script_idents = "warn" # See clippy.toml
disallowed_types = "warn" # See clippy.toml
doc_include_without_cfg = "warn"
doc_link_with_quotes = "warn"
doc_markdown = "warn"
empty_enum = "warn"
empty_enum_variants_with_brackets = "warn"
enum_glob_use = "warn"
equatable_if_let = "warn"
exit = "warn"
expl_impl_clone_on_copy = "warn"
explicit_deref_methods = "warn"
explicit_into_iter_loop = "warn"
explicit_iter_loop = "warn"
fallible_impl_from = "warn"
filter_map_next = "warn"
flat_map_option = "warn"
float_cmp_const = "warn"
fn_params_excessive_bools = "warn"
fn_to_numeric_cast_any = "warn"
from_iter_instead_of_collect = "warn"
get_unwrap = "warn"
implicit_clone = "warn"
imprecise_flops = "warn"
index_refutable_slice = "warn"
inefficient_to_string = "warn"
infinite_loop = "warn"
into_iter_without_iter = "warn"
invalid_upcast_comparisons = "warn"
iter_filter_is_ok = "warn"
iter_filter_is_some = "warn"
iter_not_returning_iterator = "warn"
iter_on_empty_collections = "warn"
iter_on_single_items = "warn"
iter_over_hash_type = "warn"
iter_without_into_iter = "warn"
large_digit_groups = "warn"
large_include_file = "warn"
large_stack_arrays = "warn"
large_stack_frames = "warn"
large_types_passed_by_value = "warn"
let_underscore_must_use = "warn"
let_underscore_untyped = "warn"
let_unit_value = "warn"
linkedlist = "warn"
literal_string_with_formatting_args = "warn"
lossy_float_literal = "warn"
macro_use_imports = "warn"
manual_assert = "warn"
manual_clamp = "warn"
manual_instant_elapsed = "warn"
manual_is_power_of_two = "warn"
manual_is_variant_and = "warn"
manual_let_else = "warn"
manual_ok_or = "warn"
manual_string_new = "warn"
map_err_ignore = "warn"
map_flatten = "warn"
match_bool = "warn"
match_on_vec_items = "warn"
match_same_arms = "warn"
match_wild_err_arm = "warn"
match_wildcard_for_single_variants = "warn"
mem_forget = "warn"
mismatching_type_param_order = "warn"
missing_assert_message = "warn"
missing_enforced_import_renames = "warn"
missing_safety_doc = "warn"
mixed_attributes_style = "warn"
mut_mut = "warn"
mutex_integer = "warn"
needless_borrow = "warn"
needless_continue = "warn"
needless_for_each = "warn"
needless_pass_by_ref_mut = "warn"
needless_pass_by_value = "warn"
negative_feature_names = "warn"
non_zero_suggestions = "warn"
nonstandard_macro_braces = "warn"
option_as_ref_cloned = "warn"
option_option = "warn"
path_buf_push_overwrite = "warn"
pathbuf_init_then_push = "warn"
ptr_as_ptr = "warn"
ptr_cast_constness = "warn"
pub_underscore_fields = "warn"
pub_without_shorthand = "warn"
rc_mutex = "warn"
readonly_write_lock = "warn"
redundant_type_annotations = "warn"
ref_as_ptr = "warn"
ref_option_ref = "warn"
rest_pat_in_fully_bound_structs = "warn"
same_functions_in_if_condition = "warn"
semicolon_if_nothing_returned = "warn"
set_contains_or_insert = "warn"
should_panic_without_expect = "warn"
single_char_pattern = "warn"
single_match_else = "warn"
str_split_at_newline = "warn"
str_to_string = "warn"
string_add = "warn"
string_add_assign = "warn"
string_lit_as_bytes = "warn"
string_lit_chars_any = "warn"
string_to_string = "warn"
suspicious_command_arg_space = "warn"
suspicious_xor_used_as_pow = "warn"
todo = "warn"
too_long_first_doc_paragraph = "warn"
too_many_lines = "warn"
trailing_empty_array = "warn"
trait_duplication_in_bounds = "warn"
tuple_array_conversions = "warn"
unchecked_duration_subtraction = "warn"
undocumented_unsafe_blocks = "warn"
unimplemented = "warn"
uninhabited_references = "warn"
uninlined_format_args = "warn"
unnecessary_box_returns = "warn"
unnecessary_literal_bound = "warn"
unnecessary_safety_doc = "warn"
unnecessary_struct_initialization = "warn"
unnecessary_wraps = "warn"
unnested_or_patterns = "warn"
unused_peekable = "warn"
unused_rounding = "warn"
unused_self = "warn"
unused_trait_names = "warn"
unwrap_used = "warn"
use_self = "warn"
useless_transmute = "warn"
verbose_file_reads = "warn"
wildcard_dependencies = "warn"
wildcard_imports = "warn"
zero_sized_map_values = "warn"
manual_range_contains = "allow" # this is better on 'allow'
map_unwrap_or = "allow" # this is better on 'allow'

151
wallet/dist/index.html vendored Normal file
View File

@ -0,0 +1,151 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>My App</title>
<script type="module">
import init, * as bindings from '/wallet-5d1b8085460fdcf6.js';
const wasm = await init({ module_or_path: '/wallet-5d1b8085460fdcf6_bg.wasm' });
window.wasmBindings = bindings;
dispatchEvent(new CustomEvent("TrunkApplicationStarted", {detail: {wasm}}));
</script>
<link rel="modulepreload" href="/wallet-5d1b8085460fdcf6.js" crossorigin="anonymous" integrity="sha384-phKOKZTFYPix/VFiKjin1txUn2nxtPk30fPdrro5PTdkGPq/+5dP1m7tONQ21dTf"><link rel="preload" href="/wallet-5d1b8085460fdcf6_bg.wasm" crossorigin="anonymous" integrity="sha384-gEKa9S/QlWrheWFbm/lNELMj8L9QLKbE4RYtCB9dU2Jl3Y4OvRmehoHMdNb7JceO" as="fetch" type="application/wasm"></head>
<body>
<div id="app"></div>
<script>"use strict";
(function () {
const address = '{{__TRUNK_ADDRESS__}}';
const base = '{{__TRUNK_WS_BASE__}}';
let protocol = '';
protocol =
protocol
? protocol
: window.location.protocol === 'https:'
? 'wss'
: 'ws';
const url = protocol + '://' + address + base + '.well-known/trunk/ws';
class Overlay {
constructor() {
// create an overlay
this._overlay = document.createElement("div");
const style = this._overlay.style;
style.height = "100vh";
style.width = "100vw";
style.position = "fixed";
style.top = "0";
style.left = "0";
style.backgroundColor = "rgba(222, 222, 222, 0.5)";
style.fontFamily = "sans-serif";
// not sure that's the right approach
style.zIndex = "1000000";
style.backdropFilter = "blur(1rem)";
const container = document.createElement("div");
// center it
container.style.position = "absolute";
container.style.top = "30%";
container.style.left = "15%";
container.style.maxWidth = "85%";
this._title = document.createElement("div");
this._title.innerText = "Build failure";
this._title.style.paddingBottom = "2rem";
this._title.style.fontSize = "2.5rem";
this._message = document.createElement("div");
this._message.style.whiteSpace = "pre-wrap";
const icon= document.createElement("div");
icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="#dc3545" viewBox="0 0 16 16"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg>';
this._title.prepend(icon);
container.append(this._title, this._message);
this._overlay.append(container);
this._inject();
window.setInterval(() => {
this._inject();
}, 250);
}
set reason(reason) {
this._message.textContent = reason;
}
_inject() {
if (!this._overlay.isConnected) {
// prepend it
document.body?.prepend(this._overlay);
}
}
}
class Client {
constructor(url) {
this.url = url;
this.poll_interval = 5000;
this._overlay = null;
}
start() {
const ws = new WebSocket(this.url);
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
switch (msg.type) {
case "reload":
this.reload();
break;
case "buildFailure":
this.buildFailure(msg.data)
break;
}
};
ws.onclose = () => this.onclose();
}
onclose() {
window.setTimeout(
() => {
// when we successfully reconnect, we'll force a
// reload (since we presumably lost connection to
// trunk due to it being killed, so it will have
// rebuilt on restart)
const ws = new WebSocket(this.url);
ws.onopen = () => window.location.reload();
ws.onclose = () => this.onclose();
},
this.poll_interval);
}
reload() {
window.location.reload();
}
buildFailure({reason}) {
// also log the console
console.error("Build failed:", reason);
console.debug("Overlay", this._overlay);
if (!this._overlay) {
this._overlay = new Overlay();
}
this._overlay.reason = reason;
}
}
new Client(url).start();
})()
</script></body>
</html>

1578
wallet/dist/wallet-5d1b8085460fdcf6.js vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

11
wallet/index.html Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>My App</title>
<link data-trunk rel="rust" />
</head>
<body>
<div id="app"></div>
</body>
</html>

50
wallet/src/account.rs Normal file
View File

@ -0,0 +1,50 @@
// use sha2::{Sha256, Digest};
// use secp256k1::rand;
// use secp256k1::{Secp256k1, PublicKey, SecretKey};
// use shared::core::Address;
//
// #[derive(Debug)]
// pub struct Account {
// address: Address,
// balance: u64,
// nonce: u64,
// }
//
// impl Account {
// pub fn public_key_to_address(public_key: &PublicKey) -> Address {
// let pk_ser = public_key.serialize();
// let hash = Sha256::digest(&pk_ser);
// hex::encode(&hash[..20])
// }
//
// pub fn nonce(&self) -> u64 {
// self.nonce
// }
//
// pub fn balance(&self) -> u64 {
// self.balance
// }
//
// pub fn address(&self) -> &Address {
// &self.address
// }
//
// pub fn new() -> Self {
// let secp = Secp256k1::new();
// let (secret_key, public_key) = secp.generate_keypair(&mut rand::rng());
// let addr = Self::public_key_to_address(&public_key);
//
// println!("{:?}", secret_key);
//
// Self {
// address: addr,
// balance: 0,
// nonce: 0,
// }
// }
// }
//
// #[test]
// fn test_acc_new() {
// Account::new();
// }

109
wallet/src/app.rs Normal file
View File

@ -0,0 +1,109 @@
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(default)] // if we add new fields, give them default values when deserializing old state
pub struct TemplateApp {
// Example stuff:
label: String,
#[serde(skip)] // This how you opt-out of serialization of a field
value: f32,
}
impl Default for TemplateApp {
fn default() -> Self {
Self {
// Example stuff:
label: "Hello World!".to_owned(),
value: 2.7,
}
}
}
impl TemplateApp {
/// Called once before the first frame.
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// This is also where you can customize the look and feel of egui using
// `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
// Load previous app state (if any).
// Note that you must enable the `persistence` feature for this to work.
if let Some(storage) = cc.storage {
eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default()
} else {
Default::default()
}
}
}
impl eframe::App for TemplateApp {
/// Called by the framework to save state before shutdown.
fn save(&mut self, storage: &mut dyn eframe::Storage) {
eframe::set_value(storage, eframe::APP_KEY, self);
}
/// Called each time the UI needs repainting, which may be many times per second.
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
// For inspiration and more examples, go to https://emilk.github.io/egui
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
// The top panel is often a good place for a menu bar:
egui::MenuBar::new().ui(ui, |ui| {
// NOTE: no File->Quit on web pages!
let is_web = cfg!(target_arch = "wasm32");
if !is_web {
ui.menu_button("File", |ui| {
if ui.button("Quit").clicked() {
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
}
});
ui.add_space(16.0);
}
egui::widgets::global_theme_preference_buttons(ui);
});
});
egui::CentralPanel::default().show(ctx, |ui| {
// The central panel the region left after adding TopPanel's and SidePanel's
ui.heading("eframe template");
ui.horizontal(|ui| {
ui.label("Write something: ");
ui.text_edit_singleline(&mut self.label);
});
ui.add(egui::Slider::new(&mut self.value, 0.0..=10.0).text("value"));
if ui.button("Increment").clicked() {
self.value += 1.0;
}
ui.separator();
ui.add(egui::github_link_file!(
"https://github.com/emilk/eframe_template/blob/main/",
"Source code."
));
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
powered_by_egui_and_eframe(ui);
egui::warn_if_debug_build(ui);
});
});
}
}
fn powered_by_egui_and_eframe(ui: &mut egui::Ui) {
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 0.0;
ui.label("Powered by ");
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
ui.label(" and ");
ui.hyperlink_to(
"eframe",
"https://github.com/emilk/egui/tree/master/crates/eframe",
);
ui.label(".");
});
}

View File

@ -1,13 +1,2 @@
pub mod renderer {
pub mod renderer;
pub use renderer::*;
pub mod pane;
pub use pane::*;
pub mod layout;
pub use layout::*;
pub mod command;
pub use command::*;
}
mod app;
pub use app::TemplateApp;

View File

@ -1,3 +1,72 @@
fn main() {
println!("Hello, world!");
#![warn(clippy::all, rust_2018_idioms)]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
// When compiling natively:
#[cfg(not(target_arch = "wasm32"))]
fn main() -> eframe::Result {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([400.0, 300.0])
.with_min_inner_size([300.0, 220.0])
.with_icon(
// NOTE: Adding an icon is optional
eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..])
.expect("Failed to load icon"),
),
..Default::default()
};
eframe::run_native(
"eframe template",
native_options,
Box::new(|cc| Ok(Box::new(wallet::TemplateApp::new(cc)))),
)
}
// When compiling to web using trunk:
#[cfg(target_arch = "wasm32")]
fn main() {
use eframe::wasm_bindgen::JsCast as _;
// Redirect `log` message to `console.log` and friends:
eframe::WebLogger::init(log::LevelFilter::Debug).ok();
let web_options = eframe::WebOptions::default();
wasm_bindgen_futures::spawn_local(async {
let document = web_sys::window()
.expect("No window")
.document()
.expect("No document");
let canvas = document
.get_element_by_id("the_canvas_id")
.expect("Failed to find the_canvas_id")
.dyn_into::<web_sys::HtmlCanvasElement>()
.expect("the_canvas_id was not a HtmlCanvasElement");
let start_result = eframe::WebRunner::new()
.start(
canvas,
web_options,
Box::new(|cc| Ok(Box::new(wallet::TemplateApp::new(cc)))),
)
.await;
// Remove the loading text and spinner:
if let Some(loading_text) = document.get_element_by_id("loading_text") {
match start_result {
Ok(_) => {
loading_text.remove();
}
Err(e) => {
loading_text.set_inner_html(
"<p> The app has crashed. See the developer console for details. </p>",
);
panic!("Failed to start eframe: {e:?}");
}
}
}
});
}

View File

@ -1,168 +0,0 @@
use std::sync::Arc;
use ratatui::prelude::*;
use ratatui::widgets::{Clear, Wrap};
use ratatui::{
buffer::Buffer,
layout::Rect,
symbols::border,
widgets::{Block, List, Paragraph, Widget},
};
use super::center;
#[derive(Clone, Debug)]
pub enum RenderBuffer {
List {
list: Vec<String>,
index: usize,
prefix: &'static str,
},
String(String),
Select(Arc<Vec<String>>, usize),
}
#[derive(Debug, PartialEq, Clone)]
pub enum RenderTarget {
All,
CliInput,
CliOutput,
PopUp
}
#[derive(Debug)]
pub struct Pane {
pub title: Option<String>,
pub target: RenderTarget,
pub buffer: RenderBuffer,
pub focused: bool,
pub scroll: i16,
pub max_scroll: i16,
}
impl Pane {
pub fn new(
title: Option<String>,
target: RenderTarget,
buffer: RenderBuffer,
focused: bool,
) -> Self {
Self {
title,
target,
buffer,
focused,
scroll: 0,
max_scroll: 0,
}
}
}
impl Widget for &mut Pane {
fn render(self, area: Rect, buf: &mut Buffer) {
let block = Block::bordered()
.title({
if let Some(t) = &self.title {
t.clone()
} else {
Default::default()
}
})
.border_set(border::PLAIN)
.border_style({
if self.focused {
Style::new().green()
} else {
Style::new().white()
}
});
let inner_area = block.inner(area);
let content_width = inner_area.width as usize;
let content_height = inner_area.height as usize;
match &self.buffer {
RenderBuffer::String(s) => {
let wrapped_lines = s
.lines()
.map(|line| {
if line.is_empty() {
1
} else {
(line.len() + content_width - 1) / { content_width + (content_width == 0) as usize }
}
})
.sum::<usize>();
self.max_scroll = if wrapped_lines > content_height {
(wrapped_lines - content_height) as i16
} else {
0
};
let scroll_offset = self.max_scroll.saturating_sub(self.scroll) as u16;
Paragraph::new(s.clone())
.wrap(Wrap::default())
.left_aligned()
.block(block)
.scroll((scroll_offset as u16, 0))
.render(area, buf);
}
RenderBuffer::Select(list, idx) => {
let rect = center(area, Constraint::Percentage(60), Constraint::Percentage(60));
Clear.render(rect, buf);
self.max_scroll = if list.len() > content_height {
(list.len() - content_height) as i16
} else {
0
};
let scroll_offset = self.max_scroll.saturating_sub(self.scroll) as u16;
let list_w = List::new(
list
.iter()
.skip(scroll_offset as usize)
.take(content_height)
.enumerate()
.map(|(i, s)| {
Line::from(format!(
"{}{}",
"",
textwrap::fill(s, content_width.saturating_sub(2))
)).style(if i + scroll_offset as usize == *idx {
Style::new().fg(Color::Blue).bg(Color::Green)
} else {
Style::default()
})
}),
)
.block(block);
Widget::render(list_w, rect, buf);
}
RenderBuffer::List { list, prefix, .. } => {
self.max_scroll = if list.len() > content_height {
(list.len() - content_height) as i16
} else {
0
};
let scroll_offset = self.max_scroll.saturating_sub(self.scroll) as u16;
let list_w = List::new(
list
.iter()
.skip(scroll_offset as usize)
.take(content_height)
.map(|s| {
format!(
"{}{}",
prefix,
textwrap::fill(s, content_width.saturating_sub(2))
)
}),
)
.block(block);
Widget::render(list_w, area, buf);
}
}
}
}

View File

@ -1,302 +0,0 @@
use std::sync::Arc;
use crossterm::event::KeyCode;
use ratatui::{Frame, buffer::Buffer, layout::Rect, widgets::Widget};
use vlogger::*;
use super::*;
#[derive(Debug, Clone)]
pub enum InputMode {
Input,
PopUp(Arc<Vec<String>>, String, usize),
}
#[derive(Debug)]
pub struct Renderer {
buffer: String,
exit: bool,
layout: RenderLayout,
mode: InputMode,
}
#[derive(Clone, Debug)]
pub enum RenderCommand {
StringToPaneId {
str: String,
pane: RenderTarget,
},
StringToPaneFocused {
str: String,
},
KeyInput(KeyCode),
ListMove {
pane: RenderTarget,
index: usize,
},
ChangeLayout(RenderLayoutKind),
ClearPane,
/// Mouse Events
MouseClickLeft(u16, u16),
MouseScrollUp,
MouseScrollDown,
SetMode(InputMode),
Exit,
}
#[allow(dead_code)]
impl Renderer {
pub fn new(layout: RenderLayoutKind) -> Self {
Self {
buffer: String::new(),
exit: false,
layout: RenderLayout::generate(layout),
mode: InputMode::Input,
}
}
fn log(&mut self, msg: String) {
self.apply(RenderCommand::StringToPaneFocused(msg))
}
pub fn draw(&mut self, frame: &mut Frame) {
frame.render_widget(self, frame.area());
}
fn exit(&mut self) {
log!(DEBUG, "Renderer Exit");
self.exit = true;
}
fn buffer_extend<S: AsRef<str>>(&mut self, input: S) {
self.buffer.push_str(input.as_ref());
}
fn input_pane(&mut self) -> Option<&mut Pane> {
self
.layout
.panes
.iter_mut()
.find(|p| p.target == RenderTarget::CliInput)
}
fn get_pane(&mut self, pane: RenderTarget) -> Option<&mut Pane> {
self.layout.panes.iter_mut().find(|p| p.target == pane)
}
fn focused(&mut self) -> Option<&mut Pane> {
self.layout.panes.iter_mut().find(|p| p.focused == true)
}
pub fn rects(&self, area: Rect) -> std::rc::Rc<[Rect]> {
self.layout.rects(area)
}
pub fn handle_mouse_click_left(&mut self, x: u16, y: u16, rects: std::rc::Rc<[Rect]>) {
for (i, r) in rects.iter().enumerate() {
if r.contains(ratatui::layout::Position { x, y }) {
self.layout.panes[i].focused = true;
} else {
self.layout.panes[i].focused = false;
}
}
}
pub fn handle_scroll_up(&mut self) {
if let Some(p) = self.focused() {
if p.scroll < p.max_scroll {
p.scroll += 1;
}
}
}
pub fn handle_scroll_down(&mut self) {
if let Some(p) = self.focused() {
if p.scroll > i16::MIN {
p.scroll -= 1;
}
}
}
pub fn handle_char_input(&mut self, c: char) {
if let Some(p) = self.input_pane() {
if let RenderBuffer::List { list, index, .. } = &mut p.buffer {
list[*index].push(c);
}
}
}
pub fn handle_backspace(&mut self) {
if let Some(p) = self.input_pane() {
if let RenderBuffer::List { list, index, .. } = &mut p.buffer {
list[*index].pop();
}
}
}
pub fn handle_enter(&mut self) {
if let Some(p) = self.input_pane() {
if let RenderBuffer::List { list, index, .. } = &mut p.buffer {
list.push(String::new());
*index += 1;
}
}
}
pub fn handle_arrow_key(&mut self, key: KeyCode) {
if let Some(pane) = self.focused() {
match &mut pane.target {
RenderTarget::CliInput => {}
RenderTarget::CliOutput => {}
RenderTarget::PopUp => {
match &mut pane.buffer {
RenderBuffer::Select(content, idx) => {
self.log(msg!(DEBUG, "idx before: {idx}"));
match key {
KeyCode::Up => { *idx = idx.saturating_sub(1) }
KeyCode::Down => {
if *idx < content.len().saturating_sub(1) {
*idx += 1;
}
}
_ => {}
}
self.log(msg!(DEBUG, "idx after: {idx}"))
}
_ => {}
}
}
_ => {}
}
}
}
pub fn list_move(&mut self, pane: RenderTarget, index: usize) {
if let Some(p) = self.get_pane(pane) {
if let RenderBuffer::List {
list, index: idx, ..
} = &mut p.buffer
{
if index > 0 && index < list.len() {
list[*idx] = list[index].clone();
}
}
}
}
pub fn render_string_to_focused(&mut self, str: String) {
if let Some(p) = self.focused() {
match &mut p.buffer {
RenderBuffer::List { list, index, .. } => {
list.push(str);
*index += 1;
}
RenderBuffer::String(s) => s.push_str(&str),
_ => {}
}
}
}
pub fn render_string_to_id(&mut self, str: String, pane: RenderTarget) {
if let Some(p) = self.layout.panes.iter_mut().find(|p| p.target == pane) {
match &mut p.buffer {
RenderBuffer::List { list, index, .. } => {
list.push(str);
*index += 1;
}
RenderBuffer::String(s) => s.push_str(&str),
_ => {}
}
}
}
fn clear_focus(&mut self) {
while let Some(focused) = self.focused() {
focused.focused = false;
}
}
pub fn clear_pane(&mut self, pane: RenderTarget) {
if matches!(pane, RenderTarget::All) {
for p in self.layout.panes.iter_mut() {
match &mut p.buffer {
RenderBuffer::List { list, index, .. } => {
list.clear();
*index = 0;
list.push(String::new());
}
RenderBuffer::String(s) => s.clear(),
_ => {}
}
}
} else if let Some(p) = self.layout.panes.iter_mut().find(|p| p.target == pane) {
match &mut p.buffer {
RenderBuffer::List { list, index, .. } => {
list.clear();
*index = 0;
list.push(String::new());
}
RenderBuffer::String(s) => s.clear(),
_ => {}
}
}
}
pub fn apply(&mut self, mes: RenderCommand, area: Rect) {
let rects = self.layout.rects(area);
match mes {
RenderCommand::MouseClickLeft(x, y) => self.handle_mouse_click_left(x, y, rects),
RenderCommand::MouseScrollUp => self.handle_scroll_up(),
RenderCommand::MouseScrollDown => self.handle_scroll_down(),
RenderCommand::KeyInput(k) => match k {
KeyCode::Char(c) => self.handle_char_input(c),
KeyCode::Backspace => self.handle_backspace(),
KeyCode::Enter => self.handle_enter(),
KeyCode::Up | KeyCode::Down | KeyCode::Left | KeyCode::Right => self.handle_arrow_key(k),
_ => {}
},
RenderCommand::ListMove { pane, index } => self.list_move(pane, index),
RenderCommand::StringToPaneFocused { str } => self.render_string_to_focused(str),
RenderCommand::StringToPaneId { str, pane } => self.render_string_to_id(str, pane),
RenderCommand::Exit => self.exit(),
RenderCommand::ChangeLayout(l) => self.layout = RenderLayout::generate(l),
RenderCommand::ClearPane => self.clear_pane(RenderTarget::All),
RenderCommand::SetMode(mode) => {
match &mode {
InputMode::Input => {
if let InputMode::PopUp(..) = self.mode {
self.layout.panes.pop();
}
}
InputMode::PopUp(content, title, ..) => {
self.clear_focus();
let pane = Pane::new(
Some(title.to_string()),
RenderTarget::PopUp,
RenderBuffer::Select(content.clone(), 0),
true,
);
self.layout.panes.push(pane);
}
}
self.mode = mode
},
}
}
}
impl Widget for &mut Renderer {
fn render(self, area: Rect, buf: &mut Buffer) {
let rects = self.layout.rects(area);
for (i, p) in self.layout.panes.iter_mut().enumerate() {
if p.target == RenderTarget::PopUp {
p.render(area, buf);
} else {
p.render(rects[i], buf)
}
}
}
}