From f6ff4aecdcde34e6da74de783f3f14a184ed81a9 Mon Sep 17 00:00:00 2001 From: Huck Boles Date: Tue, 13 Jun 2023 09:04:40 -0500 Subject: [PATCH] reset: gui --- Cargo.lock | 185 ++++++++++++++++++++++-------------------------- Cargo.toml | 5 +- src/action.rs | 83 ---------------------- src/cells.rs | 166 ------------------------------------------- src/graphics.rs | 174 --------------------------------------------- src/lib.rs | 14 ---- src/main.rs | 42 ----------- src/tests.rs | 39 ---------- src/timing.rs | 37 ---------- 9 files changed, 87 insertions(+), 658 deletions(-) delete mode 100644 src/action.rs delete mode 100644 src/cells.rs delete mode 100644 src/graphics.rs delete mode 100644 src/tests.rs delete mode 100644 src/timing.rs diff --git a/Cargo.lock b/Cargo.lock index 7cdf62e..8b0e454 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,39 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ab_glyph" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5110f1c78cf582855d895ecd0746b653db010cec6d9f5575293f27934d980a39" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "atomic_refcell" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79d6dc922a2792b006573f60b2648076355daeae5ce9cb59507e5908c9625d31" + [[package]] name = "autocfg" version = "1.1.0" @@ -24,9 +57,8 @@ checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" name = "cellseq" version = "0.1.0" dependencies = [ - "crossterm", + "egui", "eyre", - "ndarray", "rand", "tokio", ] @@ -38,28 +70,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "crossterm" -version = "0.26.1" +name = "ecolor" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" +checksum = "2e479a7fa3f23d4e794f8b2f8b3568dd4e47886ad1b12c9c095e141cb591eb63" + +[[package]] +name = "egui" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3aef8ec3ae1b772f340170c65bf27d5b8c28f543a0116c844d2ac08d01123e7" dependencies = [ - "bitflags", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", + "ahash", + "epaint", + "nohash-hasher", ] [[package]] -name = "crossterm_winapi" -version = "0.9.0" +name = "emath" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +checksum = "3857d743a6e0741cdd60b622a74c7a36ea75f5f8f11b793b41d905d2c9721a4b" + +[[package]] +name = "epaint" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09333964d4d57f40a85338ba3ca5ed4716070ab184dcfed966b35491c5c64f3b" dependencies = [ - "winapi", + "ab_glyph", + "ahash", + "atomic_refcell", + "ecolor", + "emath", + "nohash-hasher", + "parking_lot", ] [[package]] @@ -114,22 +159,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "log" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "matrixmultiply" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" -dependencies = [ - "autocfg", - "rawpointer", -] - [[package]] name = "mio" version = "0.8.8" @@ -137,51 +166,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", "windows-sys", ] [[package]] -name = "ndarray" -version = "0.15.6" +name = "nohash-hasher" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32" -dependencies = [ - "matrixmultiply", - "num-complex", - "num-integer", - "num-traits", - "rawpointer", -] - -[[package]] -name = "num-complex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "num_cpus" @@ -199,6 +192,15 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "owned_ttf_parser" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4" +dependencies = [ + "ttf-parser", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -282,12 +284,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - [[package]] name = "redox_syscall" version = "0.3.5" @@ -303,27 +299,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "signal-hook" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -390,12 +365,24 @@ dependencies = [ "syn", ] +[[package]] +name = "ttf-parser" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dcf002ae3b32cd25400d6df128c5babec3927cd1eb7ce813cfff20eb6c3746" + [[package]] name = "unicode-ident" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 317d0c8..708e313 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,8 @@ name = "cellseq" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -ndarray = "0.15" rand = "0.8" -crossterm = "0.26" eyre = "0.6" tokio = { version = "1", features = ["full"] } +egui = "0.22" diff --git a/src/action.rs b/src/action.rs deleted file mode 100644 index 5d82789..0000000 --- a/src/action.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::{io::stdout, time::Duration}; - -use super::*; - -use crossterm::{ - cursor::Show, - event::{poll, read, Event, KeyCode}, - execute, - terminal::{disable_raw_mode, Clear, ClearType::All}, -}; -use tokio::sync::mpsc::{self, Receiver, Sender}; - -#[derive(Debug, Clone, Copy)] -pub enum Action { - Exit, - Help, - MoveCursor(Direction), - SwapSide, - Remove, - Select, -} - -#[derive(Debug, Clone, Copy)] -pub enum Direction { - Up, - Down, - Left, - Right, -} - -pub async fn run_keys(snd: Sender) -> Result<()> { - loop { - if poll(Duration::from_millis(100)).unwrap() { - match read().unwrap() { - Event::Resize(_x, _y) => todo!(), - Event::Key(k) => match k.code { - KeyCode::Backspace => todo!(), - KeyCode::Enter => todo!(), - KeyCode::Tab => snd.send(Action::SwapSide).await?, - KeyCode::Esc => snd.send(Action::Exit).await?, - KeyCode::Up => snd.send(Action::MoveCursor(Direction::Up)).await?, - KeyCode::Down => snd.send(Action::MoveCursor(Direction::Down)).await?, - KeyCode::Left => snd.send(Action::MoveCursor(Direction::Left)).await?, - KeyCode::Right => snd.send(Action::MoveCursor(Direction::Right)).await?, - KeyCode::Char(c) => match c { - ' ' => snd.send(Action::Select).await?, - 'q' => snd.send(Action::Exit).await?, - '?' => snd.send(Action::Help).await?, - 'h' => snd.send(Action::MoveCursor(Direction::Left)).await?, - 'j' => snd.send(Action::MoveCursor(Direction::Down)).await?, - 'k' => snd.send(Action::MoveCursor(Direction::Up)).await?, - 'l' => snd.send(Action::MoveCursor(Direction::Right)).await?, - _ => continue, - }, - _ => continue, - }, - _ => continue, - } - } - } -} - -pub async fn run_action(rcv: &mut Receiver) -> Result<()> { - while let Some(action) = rcv.recv().await { - match action { - Action::Exit => exit(), - Action::Help => todo!(), - Action::MoveCursor(_) => todo!(), - Action::SwapSide => todo!(), - Action::Remove => todo!(), - Action::Select => todo!(), - } - } - - Ok(()) -} - -fn exit() { - disable_raw_mode().unwrap(); - execute!(stdout(), Clear(All), Show).unwrap(); - - std::process::exit(0); -} diff --git a/src/cells.rs b/src/cells.rs deleted file mode 100644 index efa4784..0000000 --- a/src/cells.rs +++ /dev/null @@ -1,166 +0,0 @@ -use crossterm::{ - cursor::MoveTo, - queue, - style::{ - Attribute::{Bold, Reset}, - Color::{Black, Green, Grey}, - ContentStyle, Print, SetStyle, - }, -}; -use ndarray::Array2; -use rand::{thread_rng, Rng}; -use std::{ - cell::Cell, - io::{stdout, Write}, - ops::Deref, -}; -use tokio::sync::watch::Receiver; - -use super::*; - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum State { - Dead, - Alive, -} - -#[derive(Debug, Clone)] -pub struct World { - pub map: Array2>, - pub area: Area, -} - -impl World { - pub fn new(area: Area) -> Self { - let width = area.width().try_into().unwrap_or(0); - let height = area.height().try_into().unwrap_or(0); - - let map = Array2::from_elem((height + 1, width + 1), Cell::new(State::Dead)); - Self { map, area } - } - - pub fn randomize(&mut self, val: f64) { - let mut rng = thread_rng(); - for cell in self.map.iter() { - if rng.gen::() > val { - cell.set(State::Alive); - } else { - cell.set(State::Dead); - } - } - } - - fn wrap_walls(&mut self) { - for (hidden, shown) in self - .column(0) - .iter() - .zip(self.column((self.area.width() - 2).try_into().unwrap_or(0))) - { - hidden.set(shown.get()); - } - - for (hidden, shown) in self - .column((self.area.width() - 1).try_into().unwrap_or(0)) - .iter() - .zip(self.column(1)) - { - hidden.set(shown.get()); - } - - for (hidden, shown) in self - .row(0) - .iter() - .zip(self.row((self.area.height() - 2).try_into().unwrap_or(0))) - { - hidden.set(shown.get()); - } - - for (hidden, shown) in self - .row((self.area.height() - 1).try_into().unwrap_or(0)) - .iter() - .zip(self.row(1)) - { - hidden.set(shown.get()); - } - } - - pub fn update(&mut self) { - let mut previous = self.clone(); - previous.wrap_walls(); - for (next, prev) in self - .windows((3, 3)) - .into_iter() - .zip(previous.windows((3, 3))) - { - let count = prev.iter().filter(|x| x.get() == State::Alive).count(); - - let cell = next.get((1, 1)).unwrap(); - - match cell.get() { - State::Dead => { - if count == 3 { - cell.set(State::Alive); - } - } - State::Alive => { - if !(3..=4).contains(&count) { - cell.set(State::Dead); - } - } - } - } - } - - pub fn draw_point(&self, cell: Point) -> Result<()> { - let mut style = ContentStyle { - foreground_color: None, - background_color: Some(Black), - underline_color: None, - attributes: Reset.into(), - }; - let (col, row) = cell.into(); - let char = if let Some(cell) = self.get((row, col)) { - if cell.get() == State::Alive { - style.foreground_color = Some(Green); - style.attributes = Bold.into(); - '●' - } else { - style.background_color = Some(Grey); - '◌' - } - } else { - ' ' - }; - - queue!( - stdout(), - MoveTo(cell.x + self.area.origin.x, cell.y + self.area.origin.y), - SetStyle(style), - Print(char) - )?; - Ok(()) - } -} - -impl Deref for World { - type Target = Array2>; - fn deref(&self) -> &Self::Target { - &self.map - } -} - -pub async fn run_world(clock: &mut Receiver, world: &mut World) -> Result<()> { - loop { - world.update(); - - for x in 1..(world.area.width() - 1).try_into()? { - for y in 1..(world.area.height()).try_into()? { - world.draw_point(Point::new(x, y))?; - } - } - - clock.changed().await?; - - stdout().flush()?; - } -} diff --git a/src/graphics.rs b/src/graphics.rs deleted file mode 100644 index 3020375..0000000 --- a/src/graphics.rs +++ /dev/null @@ -1,174 +0,0 @@ -use super::*; - -use crossterm::{ - cursor::MoveTo, - queue, - style::{ - Attribute::Bold, - Color::{Black, White}, - ContentStyle, Print, SetStyle, - }, -}; -use std::{ - io::{stdout, Write}, - ops::{Add, Sub}, -}; - -#[derive(Clone, Debug, Copy, PartialEq, Eq)] -pub struct Point { - pub x: u16, - pub y: u16, -} - -impl Point { - pub fn new(x: u16, y: u16) -> Self { - Self { x, y } - } -} - -impl Add for Point { - type Output = Self; - fn add(self, rhs: Self) -> Self::Output { - Self { - x: self.x + rhs.x, - y: self.y + rhs.y, - } - } -} - -impl Sub for Point { - type Output = Self; - fn sub(self, rhs: Self) -> Self::Output { - Self { - x: if rhs.x < self.x { self.x - rhs.x } else { 0 }, - y: if rhs.y < self.y { self.y - rhs.y } else { 0 }, - } - } -} - -impl From<(usize, usize)> for Point { - fn from(value: (usize, usize)) -> Self { - Self { - x: value.0.try_into().unwrap_or(0), - y: value.1.try_into().unwrap_or(0), - } - } -} - -impl Into<(usize, usize)> for Point { - fn into(self) -> (usize, usize) { - ( - self.x.try_into().unwrap_or(0), - self.y.try_into().unwrap_or(0), - ) - } -} - -#[derive(Clone, Debug, Copy, PartialEq, Eq)] -pub struct Area { - pub origin: Point, - pub max: Point, -} - -impl Area { - pub fn new(origin: Point, max: Point) -> Self { - Self { origin, max } - } - - pub fn contains(&self, point: Point) -> bool { - point.x >= self.origin.x - && point.y >= self.origin.y - && point.x <= self.max.x - && point.y <= self.max.y - } - - pub fn height(&self) -> u16 { - self.max.y - self.origin.y - } - - pub fn width(&self) -> u16 { - self.max.x - self.origin.x - } - - pub fn draw_outline(&self) -> Result<()> { - let style = ContentStyle { - foreground_color: Some(White), - background_color: Some(Black), - underline_color: None, - attributes: Bold.into(), - }; - - for x in 0..self.width() { - queue!( - stdout(), - MoveTo(x + self.origin.x, self.origin.y), - SetStyle(style), - Print('━'), - MoveTo(x + self.origin.x, self.max.y), - SetStyle(style), - Print('━') - )?; - } - - for y in 0..self.height() { - queue!( - stdout(), - MoveTo(self.origin.x, y + self.origin.y), - SetStyle(style), - Print('┃'), - MoveTo(self.max.x, y + self.origin.y), - SetStyle(style), - Print('┃') - )?; - } - - queue!( - stdout(), - MoveTo(self.origin.x, self.origin.y), - SetStyle(style), - Print('┏'), - MoveTo(self.origin.x, self.max.y), - SetStyle(style), - Print('┗'), - MoveTo(self.max.x, self.origin.y), - SetStyle(style), - Print('┓'), - MoveTo(self.max.x, self.max.y), - SetStyle(style), - Print('┛'), - )?; - - stdout().flush()?; - Ok(()) - } -} - -impl From<(Point, Point)> for Area { - fn from(value: (Point, Point)) -> Self { - Self { - origin: value.0, - max: value.1, - } - } -} - -impl From<(u16, u16, u16, u16)> for Area { - fn from(value: (u16, u16, u16, u16)) -> Self { - Self { - origin: Point::new(value.0, value.1), - max: Point::new(value.2, value.3), - } - } -} - -impl Into<(Point, Point)> for Area { - fn into(self) -> (Point, Point) { - (self.origin, self.max) - } -} - -impl Into<(u16, u16, u16, u16)> for Area { - fn into(self) -> (u16, u16, u16, u16) { - (self.origin.x, self.origin.y, self.max.x, self.max.y) - } -} diff --git a/src/lib.rs b/src/lib.rs index e6d5730..e69de29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +0,0 @@ -mod action; -mod cells; -mod graphics; -mod timing; - -pub use action::*; -pub use cells::*; -pub use graphics::*; -pub use timing::*; - -#[cfg(test)] -mod tests; - -use eyre::Result; diff --git a/src/main.rs b/src/main.rs index eb7d8e4..e69de29 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,42 +0,0 @@ -use crossterm::{ - cursor::{Hide, Show}, - execute, - terminal::{enable_raw_mode, Clear, ClearType::All}, -}; -use eyre::Result; -use std::io::stdout; -use tokio::{ - sync::{mpsc, watch}, - try_join, -}; - -use cellseq::*; - -#[tokio::main] -async fn main() -> Result<()> { - enable_raw_mode()?; - execute!(stdout(), Clear(All), Hide)?; - let (clock_snd, clock_rcv) = watch::channel(0); - let (action_snd, mut action_rcv) = mpsc::channel::(16); - - let mut metro = Metronome::new(clock_snd, 120); - let clock = run_clock(&mut metro); - - let cell_area = Area::new(Point::from((1, 1)), Point::from((60, 25))); - cell_area.draw_outline()?; - let mut map = World::new(cell_area); - map.randomize(0.5); - let mut world_clock = clock_rcv.clone(); - let world = run_world(&mut world_clock, &mut map); - - let input = run_keys(action_snd); - let action = run_action(&mut action_rcv); - - if let Err(e) = try_join!(clock, world, input, action) { - execute!(stdout(), Clear(All), Show)?; - eprintln!("{e}"); - return Err(e); - } else { - Ok(()) - } -} diff --git a/src/tests.rs b/src/tests.rs deleted file mode 100644 index b4351ab..0000000 --- a/src/tests.rs +++ /dev/null @@ -1,39 +0,0 @@ -use super::*; - -#[test] -fn area_tests() -> Result<()> { - let origin = Point::new(5, 5); - let max = Point::new(35, 25); - let area = Area::new(origin, max); - - assert_eq!(area.height(), 20); - assert_eq!(area.width(), 30); - - assert!(area.contains(Point::new(20, 20))); - assert!(area.contains(Point::new(5, 10))); - assert!(area.contains(Point::new(24, 25))); - assert!(area.contains(Point::new(35, 25))); - assert!(area.contains(Point::new(5, 5))); - - assert!(!area.contains(Point::new(0, 10))); - assert!(!area.contains(Point::new(15, 0))); - assert!(!area.contains(Point::new(55, 24))); - assert!(!area.contains(Point::new(15, 60))); - - assert_eq!(area, Area::from((Point::new(5, 5), Point::new(35, 25)))); - assert_eq!(area, Area::from((5, 5, 35, 25))); - assert_ne!(area, Area::from((5, 55, 35, 25))); - Ok(()) -} - -#[test] -fn point_tests() -> Result<()> { - let p1 = Point::new(10, 10); - let p2 = Point::new(15, 30); - - assert_eq!(p2 - p1, Point::new(5, 20)); - assert_eq!(p1 - p2, Point::new(0, 0)); - - assert_eq!(Point::new(5, 5), Point::from((5, 5))); - Ok(()) -} diff --git a/src/timing.rs b/src/timing.rs deleted file mode 100644 index 908da74..0000000 --- a/src/timing.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::*; - -use std::time::Duration; -use tokio::{ - sync::watch::Sender, - time::{interval, Interval}, -}; - -pub struct Metronome { - pub interval: Interval, - pub tick_len: Duration, - pub bpm: usize, - pub clock: Sender, - pub tick: usize, -} - -impl Metronome { - pub fn new(clock: Sender, bpm: usize) -> Self { - let tick_len = Duration::from_millis((60000 / bpm).try_into().unwrap_or(0)); - let interval = interval(tick_len); - Self { - interval, - tick_len, - bpm, - clock, - tick: 0, - } - } -} - -pub async fn run_clock(metro: &mut Metronome) -> Result<()> { - loop { - metro.interval.tick().await; - metro.clock.send(metro.tick)?; - metro.tick += 1; - } -} -- 2.45.2