ushell 0.4.0

Embedded shell over serial connection
Documentation
use core::str::FromStr;

use crate::heapless::String;
use crate::uluru::LRUCache;

pub trait History<const CMD_LEN: usize> {
    fn reset(&mut self);
    fn push(&mut self, command: &str) -> Result<(), ()>;
    fn go_back(&mut self) -> Option<String<CMD_LEN>>;
    fn go_forward(&mut self) -> Option<String<CMD_LEN>>;
}

pub struct NoHistory;

impl<const CMD_LEN: usize> History<CMD_LEN> for NoHistory {
    fn reset(&mut self) {}

    fn push(&mut self, _command: &str) -> Result<(), ()> {
        Ok(())
    }

    fn go_back(&mut self) -> Option<String<CMD_LEN>> {
        None
    }

    fn go_forward(&mut self) -> Option<String<CMD_LEN>> {
        None
    }
}

#[derive(Default)]
pub struct LRUHistory<const CMD_LEN: usize, const CAP: usize> {
    history: LRUCache<String<CMD_LEN>, CAP>,
    cursor: usize,
}

impl<const CMD_LEN: usize, const CAP: usize> History<CMD_LEN> for LRUHistory<CMD_LEN, CAP> {
    fn reset(&mut self) {
        self.cursor = 0;
        self.history = LRUCache::default();
    }

    fn push(&mut self, command: &str) -> Result<(), ()> {
        if !command.is_empty()
            && CAP > 0
            && self.history.find(|item| item.as_str() == command).is_none()
        {
            let history_entry = String::from_str(command)?;
            self.history.insert(history_entry);
        }
        self.cursor = 0;
        Ok(())
    }

    fn go_back(&mut self) -> Option<String<CMD_LEN>> {
        if self.history.len() == 0 || self.cursor == self.history.len() {
            None
        } else {
            let cursor = self.cursor;
            self.cursor += 1;
            self.history.get(cursor).cloned()
        }
    }

    fn go_forward(&mut self) -> Option<String<CMD_LEN>> {
        if self.cursor == 0 || self.history.len() == 0 {
            None
        } else {
            self.cursor -= 1;
            let cursor = self.cursor;
            self.history.get(cursor).cloned()
        }
    }
}