1use std::{convert::Infallible, error::Error, marker::PhantomData};
16
17mod application;
18mod config;
19mod runtime;
20
21pub use application::{
22 AddOns, Application, ApplicationStage, DockingApi, EventContext, ExternalTextureError,
23 ExternalTextureHandle, FrameContext, GpuApi, GpuContext, GpuGeneration, InitContext,
24 InitializedContext, PrepareFrameContext, RunError, ShutdownContext,
25};
26pub use config::{
27 AddOnsConfig, AppConfig, DockingConfig, RedrawMode, Theme, WgpuConfig, WgpuPreset,
28};
29pub use dear_imgui_rs as imgui;
30#[cfg(feature = "test-engine")]
31pub use dear_imgui_test_engine as test_engine;
32pub use wgpu;
33
34pub fn run<A: Application + 'static>(config: AppConfig, application: A) -> Result<(), RunError> {
36 runtime::run(config, application)
37}
38
39pub fn run_frame<F, E>(config: AppConfig, frame: F) -> Result<(), RunError>
50where
51 F: for<'frame> FnMut(&mut FrameContext<'frame>) -> Result<(), E> + 'static,
52 E: Error + 'static,
53{
54 run(config, FrameApplication::<F, E>::new(frame))
55}
56
57struct FrameApplication<F, E> {
58 frame: F,
59 _error: PhantomData<fn() -> E>,
60}
61
62impl<F, E> FrameApplication<F, E>
63where
64 E: Error + 'static,
65{
66 fn new(frame: F) -> Self {
67 Self {
68 frame,
69 _error: PhantomData,
70 }
71 }
72
73 fn invoke<C>(&mut self, context: &mut C) -> Result<(), RunError>
74 where
75 F: FnMut(&mut C) -> Result<(), E>,
76 {
77 (self.frame)(context)
78 .map_err(|source| RunError::application(ApplicationStage::Frame, source))
79 }
80}
81
82impl<F, E> Application for FrameApplication<F, E>
83where
84 F: for<'frame> FnMut(&mut FrameContext<'frame>) -> Result<(), E> + 'static,
85 E: Error + 'static,
86{
87 fn frame(&mut self, context: &mut FrameContext<'_>) -> Result<(), RunError> {
88 self.invoke(context)
89 }
90}
91
92pub fn run_ui<F>(config: AppConfig, mut ui: F) -> Result<(), RunError>
99where
100 F: FnMut(&imgui::Ui) + 'static,
101{
102 run_frame(config, move |context| {
103 (ui)(context.ui());
104 Ok::<(), Infallible>(())
105 })
106}
107
108#[cfg(test)]
109mod tests {
110 use std::{cell::Cell, error::Error as _, rc::Rc};
111
112 use thiserror::Error;
113
114 use super::{ApplicationStage, FrameApplication, RunError};
115
116 #[derive(Debug, Error)]
117 #[error("injected frame failure")]
118 struct FrameFailure;
119
120 #[derive(Default)]
121 struct ProbeFrameContext {
122 exit_requested: bool,
123 }
124
125 #[test]
126 fn frame_application_retains_closure_state_between_calls() {
127 let calls = Rc::new(Cell::new(0));
128 let observed_calls = Rc::clone(&calls);
129 let mut application = FrameApplication::<_, FrameFailure>::new(
130 move |value: &mut usize| -> Result<(), FrameFailure> {
131 let call = observed_calls.get() + 1;
132 observed_calls.set(call);
133 *value += call;
134 Ok(())
135 },
136 );
137 let mut value = 0;
138
139 application.invoke(&mut value).unwrap();
140 application.invoke(&mut value).unwrap();
141
142 assert_eq!(calls.get(), 2);
143 assert_eq!(value, 3);
144 }
145
146 #[test]
147 fn frame_application_preserves_user_error_as_the_stage_source() {
148 let mut application =
149 FrameApplication::<_, FrameFailure>::new(|_context: &mut ()| Err(FrameFailure));
150
151 let mut context = ();
152 let error = application.invoke(&mut context).unwrap_err();
153
154 assert!(matches!(
155 &error,
156 RunError::Application {
157 stage: ApplicationStage::Frame,
158 ..
159 }
160 ));
161 assert!(
162 error
163 .source()
164 .and_then(|source| source.downcast_ref::<FrameFailure>())
165 .is_some()
166 );
167 }
168
169 #[test]
170 fn exit_request_is_successful_control_flow() {
171 let mut application =
172 FrameApplication::<_, FrameFailure>::new(|context: &mut ProbeFrameContext| {
173 context.exit_requested = true;
174 Ok(())
175 });
176 let mut context = ProbeFrameContext::default();
177
178 assert!(application.invoke(&mut context).is_ok());
179 assert!(context.exit_requested);
180 }
181
182 #[test]
183 fn frame_error_remains_primary_after_an_exit_request() {
184 let mut application =
185 FrameApplication::<_, FrameFailure>::new(|context: &mut ProbeFrameContext| {
186 context.exit_requested = true;
187 Err(FrameFailure)
188 });
189 let mut context = ProbeFrameContext::default();
190
191 let error = application.invoke(&mut context).unwrap_err();
192
193 assert!(context.exit_requested);
194 assert_eq!(error.application_stage(), Some(ApplicationStage::Frame));
195 }
196}