tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
⎇ 168cd187519c0ad575d3ce63baee2f164c38d9d1
llimphi-despacho/src/lib.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
chore: refresco desde el monorepo — 128 miembros, los 76 widgets y los 11 módulos
1//! **«Mandale este mensaje al bucle»** — sin decir cuál bucle.
2//!
3//! Un `llimphi_ui::Handle<Msg>` es dos cosas pegadas: la capacidad de despachar
4//! un mensaje, y el bucle de escritorio que lo hospeda. Todo lo que quiere la
5//! primera se lleva la segunda de arriba, y con ella winit, wgpu y el backend
6//! `vello` — que es **excluyente** con el de una app enjaulada.
7//!
8//! Eso ya mordió, y de la forma más tonta: `llimphi-motion` existe para
9//! interpolar números y depende de `llimphi-ui` por **una línea**
10//! (`handle.dispatch`), así que los 39 crates que animan un color quedaban
11//! atados al escritorio.
12//!
13//! Acá está esa línea sola. Es el mismo movimiento que ya se hizo dos veces en
14//! esta familia —`llimphi-entrada` para el input, `llimphi-reproductor` para el
15//! dibujo—: una capacidad no tiene por qué saber quién la hospeda.
16//!
17//! ## Qué NO es
18//!
19//! No es un ejecutor ni una cola. `despachar` puede encolar, mandar por un canal
20//! o escribir en un socket; quien lo implementa decide. Lo único que promete es
21//! que el mensaje llega al `update` de alguien, alguna vez.
22
23#![no_std]
24#![forbid(unsafe_code)]
25
26extern crate alloc;
27
28/// Algo que sabe hacerle llegar un `Msg` a un bucle Elm.
29///
30/// Deliberadamente sin supertraits: `Send`/`Sync`/`Clone` se piden **donde hacen
31/// falta** (animar necesita clonar hacia un hilo; despachar desde el `update` no
32/// necesita nada). Ponerlos acá obligaría a todo implementador a cumplirlos
33/// aunque su caso no los use.
34pub trait Despachador<Msg> {
35 /// Hacele llegar `msg` al bucle. No bloquea ni garantiza cuándo.
36 fn despachar(&self, msg: Msg);
37
38 /// Corré `trabajo` **fuera del bucle** y despachá lo que devuelva.
39 ///
40 /// Es la otra mitad de lo que una app le pide a su anfitrión: guardar un
41 /// archivo, esperar una respuesta del LSP. La firma es la de
42 /// `llimphi_ui::Handle::spawn` a propósito — quien la tenía no cambia.
43 ///
44 /// **El default corre el trabajo AHÍ MISMO**, y eso es correcto aunque
45 /// bloquee: un anfitrión sin hilos —o un test— tiene que ejecutar el efecto
46 /// igual. Lo que no sería correcto es tirarlo en silencio y que el archivo
47 /// no se guarde sin que nadie se entere.
48 fn lanzar(&self, trabajo: alloc::boxed::Box<dyn FnOnce() -> Msg + Send + 'static>) {
49 self.despachar(trabajo())
50 }
51}
52
53/// Un despachador detrás de un puntero sigue siendo un despachador.
54///
55/// Existe para que `Arc<dyn Despachador<Msg>>` —la forma que necesita quien
56/// quiera guardarlo y clonarlo hacia un hilo— se pueda usar donde se pide un
57/// `D: Despachador<Msg>`. Sin este impl, cada consumidor escribiría su propio
58/// reenvío.
59impl<Msg, D: Despachador<Msg> + ?Sized> Despachador<Msg> for &D {
60 fn despachar(&self, msg: Msg) {
61 (**self).despachar(msg)
62 }
63 fn lanzar(&self, trabajo: alloc::boxed::Box<dyn FnOnce() -> Msg + Send + 'static>) {
64 (**self).lanzar(trabajo)
65 }
66}
67
68impl<Msg, D: Despachador<Msg> + ?Sized> Despachador<Msg> for alloc::sync::Arc<D> {
69 fn despachar(&self, msg: Msg) {
70 (**self).despachar(msg)
71 }
72 fn lanzar(&self, trabajo: alloc::boxed::Box<dyn FnOnce() -> Msg + Send + 'static>) {
73 (**self).lanzar(trabajo)
74 }
75}
76
77/// El que tira todo lo que le mandan.
78///
79/// Para tests y para el arranque, donde el bucle todavía no existe. Está acá y
80/// no en cada crate porque un «despachador de mentira» escrito a las apuradas
81/// suele ser el que se cuela en producción: si va a haber uno, que sea éste, con
82/// el nombre diciendo lo que hace.
83#[derive(Debug, Clone, Copy, Default)]
84pub struct AlVacio;
85
86impl<Msg> Despachador<Msg> for AlVacio {
87 fn despachar(&self, _msg: Msg) {}
88}