Gleam + Relm4 foreign node POC over Erlang distribution
0

Configure Feed

Select the types of activity you want to include in your feed.

gleamtk / ui / src / main.rs
2.5 kB 80 lines
1//! Relm4 foreign node (Erlang distribution peer) for the gleamtk POC. 2//! 3//! Registers with epmd, accepts BEAM connections, and maps distribution 4//! messages onto Relm4 `Input`s. UI events go back as Erlang terms. 5 6mod app; 7mod dist; 8mod protocol; 9 10use std::sync::OnceLock; 11 12use app::{AppModel, AppMsg}; 13use clap::Parser; 14use dist::{DistConfig, Outbound}; 15use relm4::prelude::*; 16use relm4::MessageBroker; 17 18/// Global broker so the distribution thread can inject Relm4 inputs. 19pub static BROKER: MessageBroker<AppMsg> = MessageBroker::new(); 20 21/// Outbound events from the UI thread → dist thread. 22pub static OUTBOUND: OnceLock<async_channel::Sender<Outbound>> = OnceLock::new(); 23 24#[derive(Parser, Debug)] 25#[command(name = "gleamtk-ui", about = "Relm4 foreign node for Gleam")] 26struct Args { 27 /// Local node name, e.g. ui@127.0.0.1 28 #[arg(long, default_value = "ui@127.0.0.1")] 29 local: String, 30 31 /// Erlang cookie (must match the Gleam/BEAM node) 32 #[arg(long, default_value = "gleamtk")] 33 cookie: String, 34 35 /// Registered name Gleam should send to (`{gui, Node} ! Msg`) 36 #[arg(long, default_value = "gui")] 37 register: String, 38 39 /// Publish as a visible node (vs hidden) 40 #[arg(long, default_value_t = true)] 41 published: bool, 42} 43 44fn main() { 45 let args = Args::parse(); 46 47 let (out_tx, out_rx) = async_channel::unbounded::<Outbound>(); 48 OUTBOUND.set(out_tx).expect("OUTBOUND already set"); 49 50 let cfg = DistConfig { 51 local_node: args 52 .local 53 .parse() 54 .unwrap_or_else(|e| panic!("bad --local node name: {e}")), 55 cookie: args.cookie, 56 register_as: args.register, 57 published: args.published, 58 }; 59 60 // Distribution server on a dedicated OS thread (smol runtime). 61 std::thread::Builder::new() 62 .name("erl-dist".into()) 63 .spawn(move || { 64 if let Err(e) = dist::run(cfg, out_rx) { 65 eprintln!("[dist] fatal: {e}"); 66 BROKER.send(AppMsg::DistDied(e.to_string())); 67 } 68 }) 69 .expect("spawn dist thread"); 70 71 // Don't pass clap flags to GApplication (it treats unknown options as fatal). 72 let prog = std::env::args() 73 .next() 74 .unwrap_or_else(|| "gleamtk-ui".into()); 75 let app = RelmApp::new("org.gleamtk.poc") 76 .with_broker(&BROKER) 77 .with_args(vec![prog]); 78 app.allow_multiple_instances(true); 79 app.run::<AppModel>(()); 80}