1use crate::app::state::App;
8use crate::debug_hook::DebugHook;
9use crate::world::find_world_jsonl;
10
11pub fn run_debug(json_path: Option<&str>, port: u16) -> std::io::Result<()> {
15 let debug_hook: Box<dyn DebugHook> = match crate::debug::DebugServer::start(port) {
16 Ok(srv) => Box::new(srv),
17 Err(e) => {
18 eprintln!("error: could not start debug server: {e}");
19 return Err(e);
20 }
21 };
22 run_interpreted(json_path, Some(debug_hook))
23}
24
25fn resolve_world_path(json_path: Option<&str>) -> std::io::Result<String> {
33 match json_path {
34 Some(p) if std::path::Path::new(p).exists() => Ok(p.to_string()),
35 Some(p) => Err(std::io::Error::new(
36 std::io::ErrorKind::NotFound,
37 format!("world file not found: {p}"),
38 )),
39 None => find_world_jsonl(None),
40 }
41}
42
43pub(crate) fn run_interpreted(
48 json_path: Option<&str>,
49 debug: Option<Box<dyn DebugHook>>,
50) -> std::io::Result<()> {
51 concinnity_engine::app::run::init_logging();
52
53 let resolved = resolve_world_path(json_path)?;
54 let json_path = resolved.as_str();
55
56 concinnity_engine::app::dev_flags::set_world_jsonl_path(Some(json_path.to_string()));
60
61 let mut app = App::new();
62 *app.world_mut() = crate::build_world_from_path(json_path).map_err(|e| {
63 tracing::error!("Could not build world from {json_path}: {e}");
64 e
65 })?;
66
67 start_app(app, debug)
68}
69
70pub(crate) fn start_app(
75 mut app: App,
76 mut debug: Option<Box<dyn DebugHook>>,
77) -> std::io::Result<()> {
78 use crate::app::runloop;
79
80 let shutdown = app.shutdown_token();
81 runloop::install_ctrlc_handler(&app);
82
83 if let Some(hook) = debug.as_mut() {
86 hook.attach_shutdown(shutdown.clone());
87 }
88
89 #[cfg(target_os = "macos")]
93 let renders = concinnity_engine::ecs::renders(app.world());
94
95 #[cfg(target_os = "macos")]
98 if renders {
99 runloop::activate_app_macos();
100 }
101
102 if let Err(e) = app.start() {
103 tracing::error!("failed to start app: {e}");
106 return Err(std::io::Error::other(format!("failed to start app: {e}")));
107 }
108
109 let on_tick = |app: &mut App| {
113 if let Some(hook) = debug.as_deref_mut() {
114 hook.tick(app.world_mut());
115 hook.apply_world_swap(app);
116 }
117 };
118
119 #[cfg(target_os = "macos")]
120 runloop::run_loop(&mut app, renders, on_tick);
121 #[cfg(not(target_os = "macos"))]
122 runloop::run_loop(&mut app, false, on_tick);
123
124 Ok(())
125}
126
127#[cfg(test)]
128mod tests {
129 use super::resolve_world_path;
130
131 #[test]
132 fn an_existing_path_is_taken_as_given() {
133 let dir = tempfile::tempdir().expect("temp dir");
134 let world = dir.path().join("scene.jsonl");
135 std::fs::write(&world, "").expect("write");
136 let given = world.to_string_lossy().into_owned();
137 assert_eq!(resolve_world_path(Some(&given)).expect("resolves"), given);
138 }
139
140 #[test]
143 fn a_missing_path_errors_rather_than_falling_back() {
144 let dir = tempfile::tempdir().expect("temp dir");
145 let missing = dir.path().join("absent.jsonl");
146 let given = missing.to_string_lossy().into_owned();
147 let err = resolve_world_path(Some(&given)).expect_err("missing world is an error");
148 assert_eq!(err.kind(), std::io::ErrorKind::NotFound);
149 assert!(err.to_string().contains("absent.jsonl"), "{err}");
150 }
151}