Initial commit
This commit is contained in:
parent
7045abcbc4
commit
8928401233
8 changed files with 330 additions and 0 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -20,3 +20,8 @@ Cargo.lock
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "sortingTUI"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
color-eyre = "0.6.3"
|
||||||
|
crossterm = "0.28.1"
|
||||||
|
rand = "0.9.1"
|
||||||
|
ratatui = "0.29.0"
|
38
src/algorithm/insertSort.rs
Normal file
38
src/algorithm/insertSort.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::drawableVec::DrawableVec;
|
||||||
|
|
||||||
|
use color_eyre::Result;
|
||||||
|
pub async fn sort(list: &mut DrawableVec) -> Result<()> {
|
||||||
|
for index in 0..list.len() {
|
||||||
|
let mut j = index;
|
||||||
|
while j > 0 && list.lessThan(j, j - 1) {
|
||||||
|
list.swap(j, j - 1).await?;
|
||||||
|
j -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
mod tests {
|
||||||
|
use crate::GuiHookVec::NonGuiVec;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! aw {
|
||||||
|
($e:expr) => {
|
||||||
|
tokio_test::block_on($e)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insertsort_correct() {
|
||||||
|
let mut list:NonGuiVec = aw!(SortingList::new(1000,0.0));
|
||||||
|
aw!(insertSort(&mut list));
|
||||||
|
assert_eq!( list.isSorted(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
3
src/algorithm/mod.rs
Normal file
3
src/algorithm/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
use crate::drawableVec::DrawableVec;
|
||||||
|
|
||||||
|
pub mod insertSort;
|
59
src/drawableVec.rs
Normal file
59
src/drawableVec.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use color_eyre::{eyre::Ok, Result};
|
||||||
|
use ratatui::{
|
||||||
|
DefaultTerminal, Frame,
|
||||||
|
crossterm::event::{self, Event, KeyCode, KeyEventKind},
|
||||||
|
layout::{Constraint, Direction, Layout},
|
||||||
|
style::{Color, Style, Stylize},
|
||||||
|
text::Line,
|
||||||
|
widgets::{Bar, BarChart, BarGroup, Block},
|
||||||
|
};
|
||||||
|
use tokio::{sync::mpsc::{UnboundedReceiver, UnboundedSender}, time::sleep};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
algorithm::{self, insertSort::{self, sort}},
|
||||||
|
graphics::{self, vertical_barchart}, tui::{self, Message},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum SortingMessage{
|
||||||
|
Tick
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DrawableVec {
|
||||||
|
vec: Vec<u32>,
|
||||||
|
event_rx: UnboundedReceiver<SortingMessage>,
|
||||||
|
event_tx: UnboundedSender<Message>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawableVec {
|
||||||
|
pub fn new(event_tx: UnboundedSender<Message>, event_rx: UnboundedReceiver<SortingMessage>, vec: Vec<u32>) -> DrawableVec {
|
||||||
|
return DrawableVec {
|
||||||
|
vec,
|
||||||
|
event_rx,
|
||||||
|
event_tx: event_tx.clone()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(mut self) -> Result<()> {
|
||||||
|
insertSort::sort(&mut self).await?;
|
||||||
|
sleep(Duration::from_secs(100000)).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
return self.vec.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn swap(&mut self, a: usize, b: usize) -> Result<()> {
|
||||||
|
self.vec.swap(a, b);
|
||||||
|
self.event_tx.send(Message::SwapEvent(a, b))?;
|
||||||
|
|
||||||
|
self.event_rx.recv().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lessThan(&self, a: usize, b: usize) -> bool {
|
||||||
|
self.vec[a] < self.vec[b]
|
||||||
|
}
|
||||||
|
}
|
39
src/graphics.rs
Normal file
39
src/graphics.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use ratatui::{
|
||||||
|
crossterm::event::{self, Event, KeyCode, KeyEventKind},
|
||||||
|
layout::{Constraint, Direction, Layout},
|
||||||
|
style::{Color, Style, Stylize},
|
||||||
|
text::Line,
|
||||||
|
widgets::{Bar, BarChart, BarGroup, Block},
|
||||||
|
DefaultTerminal, Frame,
|
||||||
|
};
|
||||||
|
/// Create a vertical bar chart from the temperatures data.
|
||||||
|
pub fn vertical_barchart(temperatures: &[u32]) -> BarChart {
|
||||||
|
let bars: Vec<Bar> = temperatures
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(hour, value)| vertical_bar(hour, value))
|
||||||
|
.collect();
|
||||||
|
let title = Line::from("Weather (Vertical)").centered();
|
||||||
|
BarChart::default()
|
||||||
|
.data(BarGroup::default().bars(&bars))
|
||||||
|
.block(Block::new().title(title))
|
||||||
|
.bar_width(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vertical_bar(hour: usize, temperature: &u32) -> Bar {
|
||||||
|
Bar::default()
|
||||||
|
.value(u64::from(*temperature))
|
||||||
|
.label(Line::from(format!("{hour:>02}:00")))
|
||||||
|
.text_value(format!("{temperature:>3}°"))
|
||||||
|
.style(temperature_style(*temperature))
|
||||||
|
.value_style(temperature_style(*temperature).reversed())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// create a yellow to red value based on the value (50-90)
|
||||||
|
pub fn temperature_style(value: u32) -> Style {
|
||||||
|
let green = (255.0 * (1.0 - f64::from((value % 10)) / 40.0)) as u8;
|
||||||
|
let color = Color::Rgb(255, green, 0);
|
||||||
|
Style::new().fg(color)
|
||||||
|
}
|
33
src/main.rs
Normal file
33
src/main.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use color_eyre::Result;
|
||||||
|
use drawableVec::DrawableVec;
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use ratatui::{
|
||||||
|
crossterm::event::{self, Event, KeyCode, KeyEventKind},
|
||||||
|
layout::{Constraint, Direction, Layout},
|
||||||
|
style::{Color, Style, Stylize},
|
||||||
|
text::Line,
|
||||||
|
widgets::{Bar, BarChart, BarGroup, Block},
|
||||||
|
DefaultTerminal, Frame,
|
||||||
|
};
|
||||||
|
use tui::Tui;
|
||||||
|
|
||||||
|
mod graphics;
|
||||||
|
mod drawableVec;
|
||||||
|
|
||||||
|
mod algorithm;
|
||||||
|
mod tui;
|
||||||
|
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
color_eyre::install()?;
|
||||||
|
let mut binding = Tui::new()?;
|
||||||
|
binding.run().await?;
|
||||||
|
|
||||||
|
ratatui::restore();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
142
src/tui.rs
Normal file
142
src/tui.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
use std::{time::Duration};
|
||||||
|
|
||||||
|
use crossterm::event::{Event, KeyCode, KeyEventKind, MouseEventKind};
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use ratatui::{layout::{Constraint, Layout}, prelude::CrosstermBackend, style::Stylize, Terminal};
|
||||||
|
use tokio::{sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, task::JoinHandle, time};
|
||||||
|
use color_eyre::{eyre::Ok, Result};
|
||||||
|
pub enum Message {
|
||||||
|
Quit,
|
||||||
|
Tick,
|
||||||
|
Render,
|
||||||
|
StartSimulation,
|
||||||
|
SwapEvent(usize, usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Tui{
|
||||||
|
terminal: Terminal<CrosstermBackend<std::io::Stdout>>,
|
||||||
|
tick_rate: f64,
|
||||||
|
frame_rate: f64,
|
||||||
|
event_rx: UnboundedReceiver<Message>,
|
||||||
|
event_tx: UnboundedSender<Message>,
|
||||||
|
sorting_tx: UnboundedSender<SortingMessage>,
|
||||||
|
should_exit: bool,
|
||||||
|
vec: Vec<u32>,
|
||||||
|
}
|
||||||
|
use crate::{drawableVec::{self, DrawableVec, SortingMessage}, graphics::vertical_barchart};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum UpdateCommand {
|
||||||
|
None,
|
||||||
|
Quit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tui{
|
||||||
|
pub fn new() -> Result<Self>{
|
||||||
|
let terminal = ratatui::init();
|
||||||
|
let (event_tx, event_rx):(UnboundedSender<Message>,UnboundedReceiver<Message>) = mpsc::unbounded_channel();
|
||||||
|
let (sorting_tx, sorting_rx) = mpsc::unbounded_channel();
|
||||||
|
let mut vec = (Vec::from_iter(1..100));
|
||||||
|
vec.shuffle(&mut rand::rng());
|
||||||
|
let drawableVec = DrawableVec::new(event_tx.clone(),sorting_rx, vec.clone());
|
||||||
|
|
||||||
|
let sorter = tokio::spawn(drawableVec.run());
|
||||||
|
|
||||||
|
|
||||||
|
Ok(Self{
|
||||||
|
terminal,
|
||||||
|
tick_rate: 10.,
|
||||||
|
frame_rate: 60.,
|
||||||
|
event_rx,
|
||||||
|
event_tx,
|
||||||
|
sorting_tx,
|
||||||
|
should_exit: false,
|
||||||
|
vec: vec,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(&mut self) -> Result<()>{
|
||||||
|
let tick_rate = Duration::from_secs_f64(1.0 / self.tick_rate);
|
||||||
|
let frame_rate = Duration::from_secs_f64(1.0 / self.frame_rate);
|
||||||
|
let mut tick_interval = time::interval(tick_rate);
|
||||||
|
let mut frame_interval = time::interval(frame_rate);
|
||||||
|
while !self.should_exit {
|
||||||
|
tokio::select! {
|
||||||
|
_tick = tick_interval.tick() => {
|
||||||
|
self.event_tx.send(Message::Tick)?;
|
||||||
|
self.sorting_tx.send(SortingMessage::Tick)?
|
||||||
|
}
|
||||||
|
_frame = frame_interval.tick() => {
|
||||||
|
self.event_tx.send(Message::Render)?
|
||||||
|
}
|
||||||
|
Some(message) = self.event_rx.recv() => {
|
||||||
|
match self.update(message).await? {
|
||||||
|
UpdateCommand::Quit => return {
|
||||||
|
self.should_exit = true;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
UpdateCommand::None => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Result::Ok(ready) = tokio::task::spawn_blocking(|| crossterm::event::poll(Duration::from_millis(100))) => {
|
||||||
|
match ready? {
|
||||||
|
true => {
|
||||||
|
let event = crossterm::event::read()?;
|
||||||
|
self.handle_event(event)?;
|
||||||
|
}
|
||||||
|
false => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(&mut self, message: Message) -> Result<UpdateCommand> {
|
||||||
|
match message {
|
||||||
|
Message::Quit => Ok(UpdateCommand::Quit),
|
||||||
|
Message::Tick => {
|
||||||
|
Ok(UpdateCommand::None)
|
||||||
|
}
|
||||||
|
Message::Render => {
|
||||||
|
self.view()?;
|
||||||
|
Ok(UpdateCommand::None)
|
||||||
|
}
|
||||||
|
Message::StartSimulation => todo!(),
|
||||||
|
Message::SwapEvent(a,b) => {self.vec.swap(a, b); Ok(UpdateCommand::None)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn view(&mut self) -> Result<()> {
|
||||||
|
self.terminal.draw(|frame| {
|
||||||
|
let [title, vertical, horizontal] = Layout::vertical([
|
||||||
|
Constraint::Length(1),
|
||||||
|
ratatui::layout::Constraint::Fill(1),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
])
|
||||||
|
.spacing(1)
|
||||||
|
.areas(frame.area());
|
||||||
|
frame.render_widget("Sorting!".bold().into_centered_line(), title);
|
||||||
|
frame.render_widget(vertical_barchart(&self.vec), horizontal);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_event(&self, event: Event) -> Result<()> {
|
||||||
|
match event {
|
||||||
|
Event::Key(key) => {
|
||||||
|
if key.kind == KeyEventKind::Press && key.code == KeyCode::Esc {
|
||||||
|
self.event_tx.send(Message::Quit)?;
|
||||||
|
}else if key.kind == KeyEventKind::Press && key.code == KeyCode:: Enter {
|
||||||
|
self.event_tx.send(Message::StartSimulation)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue