1use std::thread::{self, JoinHandle};
2
3use crossbeam_channel::{self, Receiver, Sender};
4use fj_interop::processed_shape::ProcessedShape;
5use fj_operations::shape_processor::ShapeProcessor;
6
7use crate::{Error, HostCommand, Model, Watcher};
8
9pub(crate) struct EventLoopClosed;
13
14pub(crate) struct HostThread {
15 shape_processor: ShapeProcessor,
16 model_event_tx: Sender<ModelEvent>,
17 command_tx: Sender<HostCommand>,
18 command_rx: Receiver<HostCommand>,
19}
20
21impl HostThread {
22 pub(crate) fn spawn(
24 shape_processor: ShapeProcessor,
25 event_loop_proxy: Sender<ModelEvent>,
26 ) -> (Sender<HostCommand>, JoinHandle<Result<(), EventLoopClosed>>) {
27 let (command_tx, command_rx) = crossbeam_channel::unbounded();
28 let command_tx_2 = command_tx.clone();
29
30 let host_thread = Self {
31 shape_processor,
32 model_event_tx: event_loop_proxy,
33 command_tx,
34 command_rx,
35 };
36
37 let join_handle = host_thread.spawn_thread();
38
39 (command_tx_2, join_handle)
40 }
41
42 fn spawn_thread(mut self) -> JoinHandle<Result<(), EventLoopClosed>> {
43 thread::Builder::new()
44 .name("host".to_string())
45 .spawn(move || -> Result<(), EventLoopClosed> {
46 let mut model: Option<Model> = None;
47 let mut _watcher: Option<Watcher> = None;
48
49 while let Ok(command) = self.command_rx.recv() {
50 match command {
51 HostCommand::LoadModel(new_model) => {
52 match Watcher::watch_model(
59 new_model.watch_path(),
60 self.command_tx.clone(),
61 ) {
62 Ok(watcher) => {
63 _watcher = Some(watcher);
64 self.send_event(ModelEvent::StartWatching)?;
65 }
66
67 Err(err) => {
68 self.send_event(ModelEvent::Error(err))?;
69 continue;
70 }
71 }
72 self.process_model(&new_model)?;
73 model = Some(new_model);
74 }
75 HostCommand::TriggerEvaluation => {
76 self.send_event(ModelEvent::ChangeDetected)?;
77 if let Some(model) = &model {
78 self.process_model(model)?;
79 }
80 }
81 }
82 }
83
84 Ok(())
85 })
86 .expect("Cannot create OS thread for host")
87 }
88
89 fn process_model(&mut self, model: &Model) -> Result<(), EventLoopClosed> {
91 let evaluation = match model.evaluate() {
92 Ok(evaluation) => evaluation,
93
94 Err(err) => {
95 self.send_event(ModelEvent::Error(err))?;
96 return Ok(());
97 }
98 };
99
100 self.send_event(ModelEvent::Evaluated)?;
101
102 if let Some(warn) = evaluation.warning {
103 self.send_event(ModelEvent::Warning(warn))?;
104 }
105
106 match self.shape_processor.process(&evaluation.shape) {
107 Ok(shape) => self.send_event(ModelEvent::ProcessedShape(shape))?,
108
109 Err(err) => {
110 self.send_event(ModelEvent::Error(err.into()))?;
111 }
112 }
113
114 Ok(())
115 }
116
117 fn send_event(&mut self, event: ModelEvent) -> Result<(), EventLoopClosed> {
119 self.model_event_tx
120 .send(event)
121 .map_err(|_| EventLoopClosed)?;
122
123 Ok(())
124 }
125}
126
127#[derive(Debug)]
129pub enum ModelEvent {
130 StartWatching,
132
133 ChangeDetected,
135
136 Evaluated,
138
139 ProcessedShape(ProcessedShape),
141
142 Warning(String),
144
145 Error(Error),
147}