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
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.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