default is no_std heapless

This commit is contained in:
Priec
2026-01-18 12:10:43 +01:00
parent ad9bb78fc8
commit 33002f89a6
3 changed files with 95 additions and 106 deletions

50
Cargo.lock generated
View File

@@ -3,22 +3,19 @@
version = 4
[[package]]
name = "allocator-api2"
version = "0.2.21"
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "equivalent"
version = "1.0.2"
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "hashbrown"
@@ -26,14 +23,35 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
"rustc-std-workspace-alloc",
]
[[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"
dependencies = [
"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]
name = "tui_orchestrator"
name = "pages-tui"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[features]
default = ["std"]
std = []
alloc = ["hashbrown"]
default = []
std = ["alloc"]
alloc = ["dep:hashbrown"]
sequences = ["alloc"]
[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]

View File

@@ -1,25 +1,28 @@
use super::action::{Action, ComponentAction};
use super::key::Key;
#[cfg(feature = "alloc")]
use hashbrown::HashSet;
/// Maps keys to actions.
///
/// 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)]
pub struct Bindings<A: Action> {
pub struct Bindings<A: Action, const N: usize = 32> {
#[cfg(feature = "alloc")]
bindings: hashbrown::HashMap<Key, A>,
#[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 {
Self {
#[cfg(feature = "alloc")]
bindings: hashbrown::HashMap::new(),
#[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"))]
{
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> {
#[cfg(feature = "alloc")]
{
self.bindings.get(key)
}
#[cfg(not(feature = "alloc"))]
{
self.bindings.iter().find(|(k, _)| k == key).map(|(_, a)| a)
}
self.bindings.get(key)
}
pub fn remove(&mut self, key: &Key) {
@@ -52,113 +50,85 @@ impl<A: Action> Bindings<A> {
}
#[cfg(not(feature = "alloc"))]
{
self.bindings.retain(|(k, _)| k != key);
self.bindings.remove(key);
}
}
pub fn is_empty(&self) -> bool {
#[cfg(feature = "alloc")]
{
self.bindings.is_empty()
}
#[cfg(not(feature = "alloc"))]
{
self.bindings.is_empty()
}
self.bindings.is_empty()
}
pub fn len(&self) -> usize {
#[cfg(feature = "alloc")]
{
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()
self.bindings.len()
}
#[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)> {
self.bindings.iter()
}
}
impl<A: Action> Default for Bindings<A> {
impl<A: Action, const N: usize> Default for Bindings<A, N> {
fn default() -> Self {
Self::new()
}
}
#[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) {
#[cfg(feature = "alloc")]
{
for key in keys {
self.bindings.insert(key, action.clone());
}
}
#[cfg(not(feature = "alloc"))]
{
for key in keys {
self.bindings.push((key, action.clone()));
}
for key in keys {
self.bindings.insert(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> {
let mut actions = alloc::vec::Vec::new();
let mut seen = HashSet::new();
#[cfg(feature = "alloc")]
{
for (_, action) in &self.bindings {
if seen.insert(action) {
actions.push(action);
}
for (_, action) in &self.bindings {
if seen.insert(action) {
actions.push(action);
}
}
#[cfg(not(feature = "alloc"))]
{
for (_, action) in &self.bindings {
if seen.insert(action) {
actions.push(action);
}
actions
}
#[cfg(not(feature = "alloc"))]
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
}
}
/// Returns default key bindings for common TUI patterns.
///
/// 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> {
pub fn default_bindings() -> Bindings<ComponentAction, 16> {
let mut bindings = Bindings::new();
bindings.bind(Key::tab(), ComponentAction::Next);
bindings.bind(Key::shift_tab(), ComponentAction::Prev);