mirror of
https://codeberg.org/JasterV/teatui.git
synced 2026-04-26 18:10:03 +00:00
feat: provide a new tokio feature for async effects support via tokio
This commit is contained in:
parent
2825688c30
commit
f32c6e9dd9
5 changed files with 239 additions and 29 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
|
@ -1,5 +1,4 @@
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
|
|
@ -7,16 +6,13 @@ on:
|
||||||
- main
|
- main
|
||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
# ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel
|
# ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel
|
||||||
# and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency
|
# and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
fmt:
|
fmt:
|
||||||
name: fmt
|
name: fmt
|
||||||
|
|
@ -85,6 +81,6 @@ jobs:
|
||||||
if: hashFiles('Cargo.lock') == ''
|
if: hashFiles('Cargo.lock') == ''
|
||||||
run: cargo generate-lockfile
|
run: cargo generate-lockfile
|
||||||
- name: cargo test --locked
|
- name: cargo test --locked
|
||||||
run: cargo test --locked --all-features --all-targets
|
run: cargo test --locked --all-targets
|
||||||
- name: Cache Cargo dependencies
|
- name: Cache Cargo dependencies
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
|
||||||
131
Cargo.lock
generated
131
Cargo.lock
generated
|
|
@ -92,6 +92,12 @@ version = "1.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "castaway"
|
name = "castaway"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
|
@ -310,7 +316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -585,7 +591,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -779,6 +785,12 @@ dependencies = [
|
||||||
"siphasher",
|
"siphasher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-atomic"
|
name = "portable-atomic"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
|
|
@ -972,7 +984,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1083,6 +1095,16 @@ version = "1.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
@ -1145,6 +1167,7 @@ dependencies = [
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1271,6 +1294,34 @@ version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.49.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.114",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
|
|
@ -1505,6 +1556,15 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.61.2"
|
version = "0.61.2"
|
||||||
|
|
@ -1514,6 +1574,71 @@ dependencies = [
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.53.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.51.0"
|
version = "0.51.0"
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
[package]
|
[package]
|
||||||
name = "teatui"
|
name = "teatui"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
description = "An elm-like abstraction over Ratatui"
|
description = "An elm-like abstraction over Ratatui"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
tokio = ["dep:tokio"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossterm.workspace = true
|
crossterm.workspace = true
|
||||||
ratatui.workspace = true
|
ratatui.workspace = true
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
|
tokio = { version = "1", features = ["full"], optional = true }
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
//! Actor responsible of processing side effects sent by the update actor.
|
//! Actor responsible of processing side effects sent by the update actor.
|
||||||
use std::sync::mpsc::{Receiver, SendError, Sender};
|
use std::sync::mpsc::{Receiver, SendError, Sender};
|
||||||
|
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum EffectsError<M> {
|
pub enum EffectsError<M> {
|
||||||
#[error("Failed to send message to update process")]
|
#[error("Failed to send message to update process")]
|
||||||
MessageSend(#[from] SendError<M>),
|
MessageSend(#[from] SendError<M>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "tokio"))]
|
||||||
pub(crate) fn run<M, Msg, Eff, F>(
|
pub(crate) fn run<M, Msg, Eff, F>(
|
||||||
effects_fn: F,
|
effects_fn: F,
|
||||||
rx: Receiver<(M, Eff)>,
|
rx: Receiver<(M, Eff)>,
|
||||||
|
|
@ -26,3 +30,39 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
pub(crate) fn run_async<M, Msg, Eff, F, Fut>(
|
||||||
|
effects_fn: F,
|
||||||
|
rx: Receiver<(M, Eff)>,
|
||||||
|
tx: Sender<Msg>,
|
||||||
|
) -> Result<(), EffectsError<Msg>>
|
||||||
|
where
|
||||||
|
M: Send + Sync + 'static,
|
||||||
|
Msg: Send + Sync + 'static,
|
||||||
|
Eff: Send + Sync + 'static,
|
||||||
|
Fut: Future<Output = Option<Msg>> + Send,
|
||||||
|
F: Fn(&M, Eff) -> Fut + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
let rt = tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.expect("Failed to build Tokio reactor for side-effects");
|
||||||
|
|
||||||
|
rt.block_on(async {
|
||||||
|
loop {
|
||||||
|
let Ok((model, effect)) = rx.recv() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We spawn the effect in the tokio reactor so they can run concurrently
|
||||||
|
let fut = effects_fn(&model, effect);
|
||||||
|
|
||||||
|
if let Some(msg) = fut.await {
|
||||||
|
let _ = tx.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ where
|
||||||
/// - A `view` function, responsible for constructing the view from the model.
|
/// - A `view` function, responsible for constructing the view from the model.
|
||||||
///
|
///
|
||||||
/// - An `effects` function responsible for handling side effects.
|
/// - An `effects` function responsible for handling side effects.
|
||||||
|
#[cfg(not(feature = "tokio"))]
|
||||||
pub fn start<M, Msg, Eff, W, IF, UF, VF, EF>(
|
pub fn start<M, Msg, Eff, W, IF, UF, VF, EF>(
|
||||||
init_fn: IF,
|
init_fn: IF,
|
||||||
update_fn: UF,
|
update_fn: UF,
|
||||||
|
|
@ -88,67 +89,110 @@ where
|
||||||
UF: Fn(M, Msg) -> Update<M, Eff> + Send + Sync + 'static,
|
UF: Fn(M, Msg) -> Update<M, Eff> + Send + Sync + 'static,
|
||||||
VF: Fn(&M) -> W + Send + Sync + 'static,
|
VF: Fn(&M) -> W + Send + Sync + 'static,
|
||||||
EF: Fn(&M, Eff) -> Option<Msg> + Send + Sync + 'static,
|
EF: Fn(&M, Eff) -> Option<Msg> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
run_program(init_fn, update_fn, view_fn, move |effects_rx, update_tx| {
|
||||||
|
effects::run(effects_fn, effects_rx, update_tx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts the runtime with asynchronous (Tokio) side effects.
|
||||||
|
#[cfg(feature = "tokio")]
|
||||||
|
pub fn start<M, Msg, Eff, W, IF, UF, VF, EF, Fut>(
|
||||||
|
init_fn: IF,
|
||||||
|
update_fn: UF,
|
||||||
|
view_fn: VF,
|
||||||
|
effects_fn: EF,
|
||||||
|
) -> Result<(), ProgramError<M, Msg, Eff>>
|
||||||
|
where
|
||||||
|
M: Clone + Send + Sync + 'static,
|
||||||
|
Eff: Debug + Send + Sync + 'static,
|
||||||
|
Msg: From<crossterm::event::Event> + Sync + Send + 'static,
|
||||||
|
W: Widget,
|
||||||
|
IF: Fn() -> (M, Option<Eff>) + Send + Sync + 'static,
|
||||||
|
UF: Fn(M, Msg) -> Update<M, Eff> + Send + Sync + 'static,
|
||||||
|
VF: Fn(&M) -> W + Send + Sync + 'static,
|
||||||
|
EF: Fn(&M, Eff) -> Fut + Send + Sync + 'static,
|
||||||
|
Fut: std::future::Future<Output = Option<Msg>> + Send,
|
||||||
|
{
|
||||||
|
run_program(init_fn, update_fn, view_fn, move |effects_rx, update_tx| {
|
||||||
|
effects::run_async(effects_fn, effects_rx, update_tx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal helper to abstract the common actor-spawning logic.
|
||||||
|
fn run_program<M, Msg, Eff, W, IF, UF, VF, SF>(
|
||||||
|
init_fn: IF,
|
||||||
|
update_fn: UF,
|
||||||
|
view_fn: VF,
|
||||||
|
effects_fn: SF,
|
||||||
|
) -> Result<(), ProgramError<M, Msg, Eff>>
|
||||||
|
where
|
||||||
|
M: Clone + Send + Sync + 'static,
|
||||||
|
Eff: Debug + Send + Sync + 'static,
|
||||||
|
Msg: From<crossterm::event::Event> + Sync + Send + 'static,
|
||||||
|
W: Widget,
|
||||||
|
IF: Fn() -> (M, Option<Eff>) + Send + Sync + 'static,
|
||||||
|
UF: Fn(M, Msg) -> Update<M, Eff> + Send + Sync + 'static,
|
||||||
|
VF: Fn(&M) -> W + Send + Sync + 'static,
|
||||||
|
SF: FnOnce(
|
||||||
|
std::sync::mpsc::Receiver<(M, Eff)>,
|
||||||
|
std::sync::mpsc::Sender<Msg>,
|
||||||
|
) -> Result<(), EffectsError<Msg>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
{
|
{
|
||||||
let terminal = ratatui::init();
|
let terminal = ratatui::init();
|
||||||
|
|
||||||
// Channel for signaling when a task completes
|
|
||||||
let (shutdown_tx, shutdown_rx) = channel::<Result<(), ProgramError<M, Msg, Eff>>>();
|
let (shutdown_tx, shutdown_rx) = channel::<Result<(), ProgramError<M, Msg, Eff>>>();
|
||||||
|
|
||||||
// Channels for inter-thread communication
|
|
||||||
let (update_tx, update_rx) = channel::<Msg>();
|
let (update_tx, update_rx) = channel::<Msg>();
|
||||||
let (view_tx, view_rx) = channel::<M>();
|
let (view_tx, view_rx) = channel::<M>();
|
||||||
let (effects_tx, effects_rx) = channel::<(M, Eff)>();
|
let (effects_tx, effects_rx) = channel::<(M, Eff)>();
|
||||||
|
|
||||||
// Spawn order is important.
|
// Spawn View Actor
|
||||||
// If the view actor is started after the update actor, it could happen
|
|
||||||
// that both actors have an out of sync version of the model for a bit.
|
|
||||||
thread::spawn({
|
thread::spawn({
|
||||||
let (model, _) = init_fn();
|
let (model, _) = init_fn();
|
||||||
|
|
||||||
let shutdown_tx = shutdown_tx.clone();
|
let shutdown_tx = shutdown_tx.clone();
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
let result = view::run(model, terminal, view_fn, view_rx)
|
let result =
|
||||||
.map_err(|err| ProgramError::ViewError(err));
|
view::run(model, terminal, view_fn, view_rx).map_err(ProgramError::ViewError);
|
||||||
|
|
||||||
let _ = shutdown_tx.send(result);
|
let _ = shutdown_tx.send(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Spawn Update Actor
|
||||||
thread::spawn({
|
thread::spawn({
|
||||||
let shutdown_tx = shutdown_tx.clone();
|
let shutdown_tx = shutdown_tx.clone();
|
||||||
let (model, effect) = init_fn();
|
let (model, effect) = init_fn();
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
let result = update::run(model, effect, update_fn, update_rx, view_tx, effects_tx)
|
let result = update::run(model, effect, update_fn, update_rx, view_tx, effects_tx)
|
||||||
.map_err(|err| ProgramError::UpdateError(err));
|
.map_err(ProgramError::UpdateError);
|
||||||
|
|
||||||
let _ = shutdown_tx.send(result);
|
let _ = shutdown_tx.send(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Spawn Effects Actor
|
||||||
thread::spawn({
|
thread::spawn({
|
||||||
|
let shutdown_tx = shutdown_tx.clone();
|
||||||
let update_tx = update_tx.clone();
|
let update_tx = update_tx.clone();
|
||||||
let shutdown_tx = shutdown_tx.clone();
|
|
||||||
move || {
|
move || {
|
||||||
let result = effects::run(effects_fn, effects_rx, update_tx)
|
let result = effects_fn(effects_rx, update_tx).map_err(ProgramError::EffectsError);
|
||||||
.map_err(|err| ProgramError::EffectsError(err));
|
|
||||||
|
|
||||||
let _ = shutdown_tx.send(result);
|
let _ = shutdown_tx.send(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Spawn Events Actor
|
||||||
thread::spawn({
|
thread::spawn({
|
||||||
let shutdown_tx = shutdown_tx.clone();
|
let shutdown_tx = shutdown_tx.clone();
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
let result = events::run(update_tx).map_err(|err| ProgramError::EventLoopError(err));
|
let result = events::run(update_tx).map_err(ProgramError::EventLoopError);
|
||||||
let _ = shutdown_tx.send(result);
|
let _ = shutdown_tx.send(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = shutdown_rx.recv().ok();
|
let result = shutdown_rx.recv().ok();
|
||||||
|
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue