From f67d5f6a2e4297920c92d30bdaacd8e92f8288a2 Mon Sep 17 00:00:00 2001 From: ModZero Date: Thu, 28 Jul 2022 02:52:31 +0200 Subject: [PATCH] Moved timers into a plugin --- .vscode/tasks.json | 7 ++ src-tauri/src/main.rs | 167 +----------------------------------- src-tauri/src/timers.rs | 185 ++++++++++++++++++++++++++++++++++++++++ src/App.svelte | 5 +- src/app.css | 1 - src/main.ts | 3 - vite.config.ts | 1 - 7 files changed, 198 insertions(+), 171 deletions(-) create mode 100644 src-tauri/src/timers.rs diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c7ad937..bebf952 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -28,6 +28,13 @@ "command": "npm", "problemMatcher": [], "args": ["run", "build"] + }, + { + "label": "ui:format", + "type": "shell", + "command": "npm", + "problemMatcher": [], + "args": ["run", "format"] } ] } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 7b6d5c5..4e184bb 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,174 +1,15 @@ +// Copyright 2022 ModZero. +// SPDX-License-Identifier: AGPL-3.0-or-later #![cfg_attr( all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows" )] -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, - time::{Duration, Instant}, fmt, error::Error, -}; - -use chrono::{DateTime, FixedOffset, Local}; -use tauri::{State, Window, async_runtime::spawn}; -use tokio::time::interval; -use uuid::Uuid; - -#[derive(serde::Serialize, Debug)] -enum TimerError { - NotFound, - NotStarted, -} - -impl fmt::Display for TimerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TimerError::NotFound => write!(f, "timer not found"), - TimerError::NotStarted => write!(f, "timer not started"), - } - } -} - -impl Error for TimerError {} - -#[derive(Clone, Default, serde::Serialize)] -struct Timer { - id: Uuid, - started: Option>, - duration: Duration, - elapsed: Option, - message: String, - #[serde(skip)] - checked: Option, -} - -impl Timer { - fn new(message: &str, duration: Duration) -> Self { - Self { - id: Uuid::new_v4(), - duration, - message: message.to_string(), - ..Default::default() - } - } - - fn complete(&self) -> bool { - self.elapsed.map_or(false, |e| e >= self.duration) - } - - fn start(self) -> Self { - Timer { - started: Some(Local::now().into()), - elapsed: Some(Duration::from_secs(0)), - checked: Some(Instant::now()), - ..self - } - } - - fn tick(self) -> Result { - let now = Instant::now(); - let elapsed = now - match self.checked { - None => return Err(TimerError::NotStarted), - Some(checked) => checked, - }; - - Ok(Timer { - elapsed: self.elapsed.map(|e| e + elapsed), - checked: Some(now), - ..self - }) - } -} - -#[derive(Default)] -struct Timers(Arc>>); - -impl Timers { - fn make_timer(&self, duration: Duration, message: &str) -> Timer { - let timer = Timer::new(message, duration); - - self.0.lock().unwrap().insert(timer.id, timer.clone()); - - timer - } - - fn delete_timer(&self, timer_id: Uuid) -> Option { - self.0.lock().unwrap().get(&timer_id).cloned() - } - - fn start_timer(&self, timer_id: Uuid) -> Result { - let mut timers = self.0.lock().unwrap(); - match timers.get(&timer_id).cloned() { - None => Err(TimerError::NotFound), - Some(t) => { - let started = t.start(); - timers.insert(started.id, started.clone()); - - Ok(started) - } - } - } - - fn tick_timer(&self, timer_id: Uuid) -> Result { - let mut timers = self.0.lock().unwrap(); - match timers.get(&timer_id).cloned() { - None => Err(TimerError::NotFound), - Some(t) => { - let started = t.tick()?; - timers.insert(started.id, started.clone()); - - Ok(started) - } - } - } - -} - -#[tauri::command] -fn make_timer(timers: State<'_, Timers>, duration: Duration, message: &str) -> Timer { - timers.make_timer(duration, message) -} - -#[tauri::command] -fn delete_timer(timers: State<'_, Timers>, timer_id: Uuid) -> Option { - timers.delete_timer(timer_id) -} - -#[tauri::command] -fn start_timer(window: Window, timers: State<'_, Timers>, timer_id: Uuid) -> Result { - let timers = Timers(timers.0.to_owned()); - let timer = timers.start_timer(timer_id)?; - let res = timer.clone(); - - spawn(async move { - let mut interval = interval(Duration::from_secs(1) / 60); - - loop { - interval.tick().await; - match timers.tick_timer(timer_id) { - Err(_) => break, - Ok(timer) => { - if timer.complete() || window.emit("timer-tick", timer).is_err() { - break; - } - } - } - } - - window.emit("timer-done", timer).ok(); - }); - - Ok(res) -} +mod timers; fn main() { tauri::Builder::default() - .manage(Timers(Default::default())) - .invoke_handler(tauri::generate_handler![ - delete_timer, - make_timer, - start_timer - ]) + .plugin(timers::init()) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/timers.rs b/src-tauri/src/timers.rs new file mode 100644 index 0000000..5ec1556 --- /dev/null +++ b/src-tauri/src/timers.rs @@ -0,0 +1,185 @@ +// Copyright 2022 ModZero. +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::{ + collections::HashMap, + error::Error, + fmt, + sync::{Arc, Mutex}, + time::{Duration, Instant}, +}; + +use chrono::{DateTime, FixedOffset, Local}; +use tauri::{ + async_runtime::spawn, + plugin::{Builder, TauriPlugin}, + AppHandle, Runtime, State, Manager, +}; +use tokio::time::interval; +use uuid::Uuid; + +#[derive(serde::Serialize, Debug)] +pub enum TimerError { + NotFound, + NotStarted, +} + +impl fmt::Display for TimerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TimerError::NotFound => write!(f, "timer not found"), + TimerError::NotStarted => write!(f, "timer not started"), + } + } +} + +impl Error for TimerError {} + +#[derive(Clone, Default, serde::Serialize)] +pub struct Timer { + id: Uuid, + started: Option>, + duration: Duration, + elapsed: Option, + message: String, + #[serde(skip)] + checked: Option, +} + +impl Timer { + fn new(message: &str, duration: Duration) -> Self { + Self { + id: Uuid::new_v4(), + duration, + message: message.to_string(), + ..Default::default() + } + } + + fn is_complete(&self) -> bool { + self.elapsed.map_or(false, |e| e >= self.duration) + } + + fn start(self) -> Self { + Timer { + started: Some(Local::now().into()), + elapsed: Some(Duration::from_secs(0)), + checked: Some(Instant::now()), + ..self + } + } + + fn tick(self) -> Result { + let now = Instant::now(); + let elapsed = now + - match self.checked { + None => return Err(TimerError::NotStarted), + Some(checked) => checked, + }; + + Ok(Timer { + elapsed: self.elapsed.map(|e| e + elapsed), + checked: Some(now), + ..self + }) + } +} + +#[derive(Default)] +struct Timers(Arc>>); + +impl Timers { + fn make(&self, duration: Duration, message: &str) -> Timer { + let timer = Timer::new(message, duration); + + self.0.lock().unwrap().insert(timer.id, timer.clone()); + + timer + } + + fn delete(&self, timer_id: Uuid) -> Option { + self.0.lock().unwrap().get(&timer_id).cloned() + } + + fn start(&self, timer_id: Uuid) -> Result { + let mut timers = self.0.lock().unwrap(); + match timers.get(&timer_id).cloned() { + None => Err(TimerError::NotFound), + Some(t) => { + let started = t.start(); + timers.insert(started.id, started.clone()); + + Ok(started) + } + } + } + + fn tick(&self, timer_id: Uuid) -> Result { + let mut timers = self.0.lock().unwrap(); + match timers.get(&timer_id).cloned() { + None => Err(TimerError::NotFound), + Some(t) => { + let started = t.tick()?; + timers.insert(started.id, started.clone()); + + Ok(started) + } + } + } +} + +#[tauri::command] +fn make(timers: State<'_, Timers>, duration: Duration, message: &str) -> Timer { + timers.make(duration, message) +} + +#[tauri::command] +fn delete(timers: State<'_, Timers>, timer_id: Uuid) -> Option { + timers.delete(timer_id) +} + +#[tauri::command] +fn start( + _app: AppHandle, + timers: State<'_, Timers>, + timer_id: Uuid, +) -> Result { + let timers = Timers(timers.0.to_owned()); + let timer = timers.start(timer_id)?; + + spawn(async move { + let mut interval = interval(Duration::from_secs(1) / 60); + + loop { + interval.tick().await; + match timers.tick(timer_id) { + Err(_) => break, + Ok(timer) => { + if timer.is_complete() { + _app.emit_all("timer-done", timer).ok(); + break; + } + if _app.emit_all("timer-tick", timer).is_err() { + break; + } + } + } + } + }); + + Ok(timer) +} + +pub fn init() -> TauriPlugin { + Builder::new("timers") + .invoke_handler(tauri::generate_handler![ + delete, + make, + start + ]) + .setup(|app_handle| { + app_handle.manage(Timers::default()); + Ok(()) + }) + .build() +} diff --git a/src/App.svelte b/src/App.svelte index bb2c68a..ac563b2 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -28,7 +28,6 @@ timerDoneUnlisten = listen("timer-done", (event) => { console.log("Done!", event.payload.id); }); - }); onDestroy(() => { @@ -37,11 +36,11 @@ }); async function startTimer() { - let timer = await invoke("make_timer", { + let timer = await invoke("plugin:timers|make", { duration: { secs: seconds, nanos: 0 }, message: "Hi!", }); - invoke("start_timer", { timerId: timer.id }); + invoke("plugin:timers|start", { timerId: timer.id }); } diff --git a/src/app.css b/src/app.css index 9236889..ed06370 100644 --- a/src/app.css +++ b/src/app.css @@ -36,7 +36,6 @@ h1 { line-height: 1.1; } - #app { position: absolute; max-width: 1280px; diff --git a/src/main.ts b/src/main.ts index fea7b53..7d9d501 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,14 +1,11 @@ // Copyright 2022 ModZero. // SPDX-License-Identifier: AGPL-3.0-or-later - import "./app.css"; import App from "./App.svelte"; - const app = new App({ target: document.getElementById("app"), }); export default app; - diff --git a/vite.config.ts b/vite.config.ts index c6f96a2..72509a6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,6 @@ import { defineConfig } from "vite"; import { svelte } from "@sveltejs/vite-plugin-svelte"; - // https://vitejs.dev/config/ export default defineConfig({ plugins: [svelte()],