Compare commits

..

3 Commits

Author SHA1 Message Date
Priec
feb22d270c flake for rust 2026-01-19 15:31:08 +01:00
Priec
d7f35690e3 working with strings to register a page, needs redesign 2026-01-19 13:10:14 +01:00
Priec
33002f89a6 default is no_std heapless 2026-01-18 12:10:43 +01:00
5 changed files with 148 additions and 106 deletions

50
Cargo.lock generated
View File

@@ -3,22 +3,19 @@
version = 4 version = 4
[[package]] [[package]]
name = "allocator-api2" name = "byteorder"
version = "0.2.21" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "equivalent" name = "hash32"
version = "1.0.2" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
[[package]] "byteorder",
name = "foldhash" ]
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
@@ -26,14 +23,35 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [ dependencies = [
"allocator-api2", "rustc-std-workspace-alloc",
"equivalent",
"foldhash",
] ]
[[package]] [[package]]
name = "tui_orchestrator" name = "heapless"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed"
dependencies = [
"hash32",
"stable_deref_trait",
]
[[package]]
name = "pages-tui"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"hashbrown", "hashbrown",
"heapless",
] ]
[[package]]
name = "rustc-std-workspace-alloc"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d441c3b2ebf55cebf796bfdc265d67fa09db17b7bb6bd4be75c509e1e8fec3"
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"

View File

@@ -1,16 +1,17 @@
[package] [package]
name = "tui_orchestrator" name = "pages-tui"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[features] [features]
default = ["std"] default = []
std = [] std = ["alloc"]
alloc = ["hashbrown"] alloc = ["dep:hashbrown"]
sequences = ["alloc"] sequences = ["alloc"]
[dependencies] [dependencies]
hashbrown = { version = "0.15", optional = true } hashbrown = { version = "0.15", optional = true, default-features = false, features = ["alloc"] }
heapless = { version = "0.9.2", default-features = false }
[dev-dependencies] [dev-dependencies]

49
flake.nix Normal file
View File

@@ -0,0 +1,49 @@
{
description = "Komp AC - Kompress Accounting";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
mermaid-cli
# Rust toolchain
rustc
cargo
rustfmt
clippy
cargo-watch
rust-analyzer
cargo-tarpaulin
cargo-flamegraph
rust-code-analysis
# C build tools (for your linker issue)
gcc
binutils
pkg-config
# OpenSSL for crypto dependencies
openssl
openssl.dev
];
shellHook = ''
export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH"
export OPENSSL_DIR="${pkgs.openssl.dev}"
export OPENSSL_LIB_DIR="${pkgs.openssl.out}/lib"
export OPENSSL_INCLUDE_DIR="${pkgs.openssl.dev}/include"
echo "🦀 Rust development environment loaded"
'';
};
}
);
}

View File

@@ -1,25 +1,28 @@
use super::action::{Action, ComponentAction}; use super::action::{Action, ComponentAction};
use super::key::Key; use super::key::Key;
#[cfg(feature = "alloc")]
use hashbrown::HashSet;
/// Maps keys to actions. /// Maps keys to actions.
/// ///
/// When `alloc` feature is enabled, uses HashMap for O(1) lookup. /// When `alloc` feature is enabled, uses HashMap for O(1) lookup.
/// Without `alloc`, falls back to Vec with O(n) lookup. /// Without `alloc`, uses heapless::LinearMap with fixed capacity N.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Bindings<A: Action> { pub struct Bindings<A: Action, const N: usize = 32> {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
bindings: hashbrown::HashMap<Key, A>, bindings: hashbrown::HashMap<Key, A>,
#[cfg(not(feature = "alloc"))] #[cfg(not(feature = "alloc"))]
bindings: alloc::vec::Vec<(Key, A)>, bindings: heapless::LinearMap<Key, A, N>,
} }
impl<A: Action> Bindings<A> { impl<A: Action, const N: usize> Bindings<A, N> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
bindings: hashbrown::HashMap::new(), bindings: hashbrown::HashMap::new(),
#[cfg(not(feature = "alloc"))] #[cfg(not(feature = "alloc"))]
bindings: alloc::vec::Vec::new(), bindings: heapless::LinearMap::new(),
} }
} }
@@ -30,19 +33,14 @@ impl<A: Action> Bindings<A> {
} }
#[cfg(not(feature = "alloc"))] #[cfg(not(feature = "alloc"))]
{ {
self.bindings.push((key, action)); // V heapless verzii ignorujeme chybu pri plnej kapacite,
// alebo by ste mohli vrátiť Result.
let _ = self.bindings.insert(key, action);
} }
} }
pub fn get(&self, key: &Key) -> Option<&A> { pub fn get(&self, key: &Key) -> Option<&A> {
#[cfg(feature = "alloc")] self.bindings.get(key)
{
self.bindings.get(key)
}
#[cfg(not(feature = "alloc"))]
{
self.bindings.iter().find(|(k, _)| k == key).map(|(_, a)| a)
}
} }
pub fn remove(&mut self, key: &Key) { pub fn remove(&mut self, key: &Key) {
@@ -52,113 +50,85 @@ impl<A: Action> Bindings<A> {
} }
#[cfg(not(feature = "alloc"))] #[cfg(not(feature = "alloc"))]
{ {
self.bindings.retain(|(k, _)| k != key); self.bindings.remove(key);
} }
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
#[cfg(feature = "alloc")] self.bindings.is_empty()
{
self.bindings.is_empty()
}
#[cfg(not(feature = "alloc"))]
{
self.bindings.is_empty()
}
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
#[cfg(feature = "alloc")] self.bindings.len()
{
self.bindings.len()
}
#[cfg(not(feature = "alloc"))]
{
self.bindings.len()
}
}
pub fn keys(&self) -> alloc::vec::Vec<&Key> {
#[cfg(feature = "alloc")]
{
self.bindings.keys().collect()
}
#[cfg(not(feature = "alloc"))]
{
self.bindings.iter().map(|(k, _)| k).collect()
}
}
#[cfg(not(feature = "alloc"))]
pub fn iter(&self) -> impl Iterator<Item = &(Key, A)> {
self.bindings.iter()
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn keys(&self) -> alloc::vec::Vec<&Key> {
self.bindings.keys().collect()
}
#[cfg(not(feature = "alloc"))]
pub fn keys(&self) -> heapless::Vec<&Key, N> {
let mut v = heapless::Vec::new();
for k in self.bindings.keys() {
let _ = v.push(k);
}
v
}
pub fn iter(&self) -> impl Iterator<Item = (&Key, &A)> { pub fn iter(&self) -> impl Iterator<Item = (&Key, &A)> {
self.bindings.iter() self.bindings.iter()
} }
} }
impl<A: Action> Default for Bindings<A> { impl<A: Action, const N: usize> Default for Bindings<A, N> {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
#[cfg(feature = "sequences")] #[cfg(feature = "sequences")]
impl<A: Action + core::hash::Hash + Eq> Bindings<A> { impl<A: Action + core::hash::Hash + Eq, const N: usize> Bindings<A, N> {
#[cfg(feature = "alloc")]
pub fn bind_sequence(&mut self, keys: alloc::vec::Vec<Key>, action: A) { pub fn bind_sequence(&mut self, keys: alloc::vec::Vec<Key>, action: A) {
#[cfg(feature = "alloc")] for key in keys {
{ self.bindings.insert(key, action.clone());
for key in keys {
self.bindings.insert(key, action.clone());
}
}
#[cfg(not(feature = "alloc"))]
{
for key in keys {
self.bindings.push((key, action.clone()));
}
} }
} }
#[cfg(not(feature = "alloc"))]
pub fn bind_sequence<const K: usize>(&mut self, keys: heapless::Vec<Key, K>, action: A) {
for key in keys {
let _ = self.bindings.insert(key, action.clone());
}
}
#[cfg(feature = "alloc")]
pub fn get_sequences(&self) -> alloc::vec::Vec<&A> { pub fn get_sequences(&self) -> alloc::vec::Vec<&A> {
let mut actions = alloc::vec::Vec::new(); let mut actions = alloc::vec::Vec::new();
let mut seen = HashSet::new(); let mut seen = HashSet::new();
#[cfg(feature = "alloc")] for (_, action) in &self.bindings {
{ if seen.insert(action) {
for (_, action) in &self.bindings { actions.push(action);
if seen.insert(action) {
actions.push(action);
}
} }
} }
#[cfg(not(feature = "alloc"))] actions
{ }
for (_, action) in &self.bindings {
if seen.insert(action) { #[cfg(not(feature = "alloc"))]
actions.push(action); pub fn get_sequences(&self) -> heapless::Vec<&A, N> {
} let mut actions = heapless::Vec::new();
// V no_std/no_alloc bez HashSetu robíme O(n^2) kontrolu unikátnosti
for (_, action) in &self.bindings {
if !actions.iter().any(|&a| a == action) {
let _ = actions.push(action);
} }
} }
actions actions
} }
} }
/// Returns default key bindings for common TUI patterns. pub fn default_bindings() -> Bindings<ComponentAction, 16> {
///
/// Includes:
/// - Tab/Shift+Tab for next/prev navigation
/// - Enter for select
/// - Esc for cancel
/// - Arrow keys for directional movement
/// - Home/End/PageUp/PageDown for scrolling
/// - Backspace/Delete for editing
/// - Ctrl+C as custom quit action
///
/// Use as-is or extend with your own bindings.
pub fn default_bindings() -> Bindings<ComponentAction> {
let mut bindings = Bindings::new(); let mut bindings = Bindings::new();
bindings.bind(Key::tab(), ComponentAction::Next); bindings.bind(Key::tab(), ComponentAction::Next);
bindings.bind(Key::shift_tab(), ComponentAction::Prev); bindings.bind(Key::shift_tab(), ComponentAction::Prev);

View File

@@ -228,4 +228,8 @@ where
pub fn start(&mut self) { pub fn start(&mut self) {
self.running = true; self.running = true;
} }
pub fn current_id(&self) -> Option<&String> {
self.router.current_id()
}
} }