mecha10_cli_core/
paths.rs1#![allow(dead_code)]
6
7pub const PROJECT_CONFIG: &str = "mecha10.json";
9
10pub mod project {
12 pub const NODES_DIR: &str = "nodes";
14
15 pub const NODES_MECHA10_DIR: &str = "nodes/@mecha10";
18
19 pub fn framework_node_schema_dir(node_name: &str) -> String {
22 format!("{}/{}", NODES_MECHA10_DIR, node_name)
23 }
24
25 pub const DRIVERS_DIR: &str = "drivers";
27
28 pub const TYPES_DIR: &str = "types";
30
31 pub const BEHAVIORS_DIR: &str = "configs/behaviors";
35
36 pub const MODELS_DIR: &str = "models";
38
39 pub const MODELS_CATALOG_DIR: &str = "models/catalog";
42
43 pub const LOGS_DIR: &str = "logs";
45
46 pub const SIMULATION_DIR: &str = "configs/simulation";
50
51 pub const SIMULATION_MODELS_DIR: &str = "configs/simulation/models";
53
54 pub const SIMULATION_ENVIRONMENTS_DIR: &str = "configs/simulation/environments";
56
57 pub const SIMULATION_IMAGES_DIR: &str = "configs/simulation/images";
60
61 pub const RL_TASKS_DIR: &str = "configs/rl/tasks";
65
66 pub const SIMULATION_OUTPUT_DIR: &str = "configs/simulation/output";
68
69 pub const TARGET_DIR: &str = "target";
71
72 pub const SRC_DIR: &str = "src";
74
75 pub const STATE_DIR: &str = ".mecha10";
78
79 pub const DEV_PREFLIGHT_CACHE_FILE: &str = ".mecha10/dev-preflight-cache.json";
81
82 pub const ASSETS_CACHE_DIR: &str = ".mecha10/assets";
85}
86
87pub mod config {
89 use super::project;
90
91 pub const DIR: &str = "configs";
93
94 pub const NODES_DIR: &str = "configs/nodes";
96
97 pub const NODES_MECHA10_DIR: &str = "configs/nodes/@mecha10";
99
100 fn framework_config(category_dir: &str, name: &str) -> String {
102 format!("{category_dir}/@mecha10/{name}/config.json")
103 }
104
105 fn project_config(category_dir: &str, name: &str) -> String {
107 format!("{category_dir}/{name}/config.json")
108 }
109
110 fn config_candidates(category_dir: &str, name: &str) -> [String; 2] {
113 [framework_config(category_dir, name), project_config(category_dir, name)]
114 }
115
116 pub fn framework_node(node_name: &str) -> String {
118 framework_config(NODES_DIR, node_name)
119 }
120
121 pub fn project_node(node_name: &str) -> String {
123 project_config(NODES_DIR, node_name)
124 }
125
126 pub fn framework_node_dir(node_name: &str) -> String {
130 format!("{}/{}", NODES_MECHA10_DIR, node_name)
131 }
132
133 pub fn node_config_candidates(node_name: &str) -> [String; 2] {
136 config_candidates(NODES_DIR, node_name)
137 }
138
139 pub fn framework_behavior(behavior_name: &str) -> String {
142 framework_config(project::BEHAVIORS_DIR, behavior_name)
143 }
144
145 pub fn framework_behavior_dir(behavior_name: &str) -> String {
148 format!("{}/@mecha10/{}", project::BEHAVIORS_DIR, behavior_name)
149 }
150
151 pub fn project_behavior(behavior_name: &str) -> String {
154 project_config(project::BEHAVIORS_DIR, behavior_name)
155 }
156
157 pub fn behavior_config_candidates(behavior_name: &str) -> [String; 2] {
160 [project_behavior(behavior_name), framework_behavior(behavior_name)]
161 }
162}
163
164pub mod docker {
166 pub const DIR: &str = "docker";
168
169 pub const COMPOSE_FILE: &str = "docker/docker-compose.yml";
171
172 pub const COMPOSE_REMOTE_FILE: &str = "docker/docker-compose.remote.yml";
174
175 pub const DOCKERFILE_REMOTE: &str = "docker/Dockerfile.remote";
177
178 pub const ENTRYPOINT_REMOTE: &str = "docker/entrypoint.sh";
180
181 pub const COMPOSE_RL_FILE: &str = "docker/docker-compose.rl.yml";
183
184 pub const DOCKERFILE_RL: &str = "docker/Dockerfile.rl";
186
187 pub const ENTRYPOINT_RL: &str = "docker/entrypoint-rl.sh";
191}
192
193pub mod env {
195 pub const EXAMPLE: &str = ".env.example";
197
198 pub const FILE: &str = ".env";
200}
201
202pub mod meta {
204 pub const README: &str = "README.md";
206
207 pub const GITIGNORE: &str = ".gitignore";
209
210 pub const PACKAGE_JSON: &str = "package.json";
212
213 pub const REQUIREMENTS_TXT: &str = "requirements.txt";
215
216 pub const PYTHON_VERSION: &str = ".python-version";
219}
220
221pub mod rust {
223 pub const CARGO_TOML: &str = "Cargo.toml";
225
226 pub const CARGO_LOCK: &str = "Cargo.lock";
228
229 pub const MAIN_RS: &str = "src/main.rs";
231
232 pub const LIB_RS: &str = "src/lib.rs";
234
235 pub const BUILD_RS: &str = "build.rs";
237
238 pub const RUSTFMT_TOML: &str = "rustfmt.toml";
240}
241
242pub mod model {
244 pub const ONNX_FILE: &str = "model.onnx";
246
247 pub const LABELS_FILE: &str = "labels.txt";
249
250 pub const CONFIG_FILE: &str = "config.json";
252}
253
254pub mod user {
256 use std::path::PathBuf;
257
258 pub const MECHA10_DIR: &str = mecha10_core::fs_utils::MECHA10_DIR;
262
263 pub const CREDENTIALS_FILE: &str = ".mecha10/credentials.json";
267
268 pub const TEMPLATES_DIR: &str = ".mecha10/templates";
270
271 pub const BIN_DIR: &str = ".mecha10/bin";
273
274 pub fn mecha10_dir() -> PathBuf {
276 dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")).join(MECHA10_DIR)
277 }
278
279 pub fn credentials_file() -> PathBuf {
283 mecha10_auth::default_credentials_path()
284 }
285
286 pub fn templates_dir() -> PathBuf {
288 mecha10_dir().join("templates")
289 }
290
291 pub fn bin_dir() -> PathBuf {
293 mecha10_dir().join("bin")
294 }
295
296 pub fn bin(name: &str) -> PathBuf {
298 bin_dir().join(name)
299 }
300
301 pub fn cargo_bin(name: &str) -> PathBuf {
303 home_bin_dir(".cargo", name)
304 }
305
306 pub fn local_bin(name: &str) -> PathBuf {
308 home_bin_dir(".local", name)
309 }
310
311 fn home_bin_dir(home_subdir: &str, name: &str) -> PathBuf {
314 mecha10_core::fs_utils::home_subpath(&[home_subdir, "bin", name])
315 }
316}
317
318pub mod container {
320 pub const PROJECT_ROOT: &str = "/project";
322
323 pub const FRAMEWORK_ROOT: &str = "/mecha10";
325
326 pub const APP_ROOT: &str = "/app";
328
329 pub const REMOTE_CONFIG: &str = "/app/mecha10.json";
331
332 pub fn project_path(relative: &str) -> String {
334 format!("{}/{}", PROJECT_ROOT, relative)
335 }
336
337 pub fn framework_path(relative: &str) -> String {
339 format!("{}/{}", FRAMEWORK_ROOT, relative)
340 }
341}
342
343pub mod framework {
345 pub const PACKAGES_DIR: &str = "packages";
347
348 pub const NODES_DIR: &str = "packages/nodes";
350
351 pub const DRIVERS_DIR: &str = "packages/drivers";
353
354 pub const ROOT_MARKER_DIR: &str = "packages/core";
356
357 pub const RELEASE_BINARY: &str = "target/release/mecha10";
359
360 pub const DEBUG_BINARY: &str = "target/debug/mecha10";
362
363 pub fn node_config(node_name: &str) -> String {
365 format!("packages/nodes/{}/configs/config.json", node_name)
366 }
367}
368
369pub mod urls {
371 pub const MECHA10_API_URL: &str = "https://mecha.industries/api";
374
375 pub fn mecha10_api_base_url() -> String {
379 std::env::var("MECHA10_API_URL")
380 .unwrap_or_else(|_| MECHA10_API_URL.to_string())
381 .trim_end_matches('/')
382 .to_string()
383 }
384
385 pub fn mecha10_api_path(path: &str) -> String {
388 format!("{}/{}", mecha10_api_base_url(), path.trim_start_matches('/'))
389 }
390
391 pub const AUTH_URL: &str = "https://mecha.industries/api/auth";
393}
394
395pub fn target_path(profile: &str, binary: &str) -> String {
397 format!("target/{}/{}", profile, binary)
398}
399
400pub fn target_path_with_triple(target: Option<&str>, profile: &str) -> String {
402 match target {
403 Some(t) => format!("target/{}/{}", t, profile),
404 None => format!("target/{}", profile),
405 }
406}
407
408#[cfg(test)]
409mod tests {
410 use super::*;
411
412 #[test]
413 fn credentials_file_matches_mecha10_auth_source_of_truth() {
414 assert_eq!(user::credentials_file(), mecha10_auth::default_credentials_path());
415 }
416
417 #[test]
420 fn cargo_bin_and_local_bin_join_name_under_their_own_home_subdir() {
421 let home = dirs::home_dir().expect("home dir must resolve in test environment");
422 assert_eq!(
423 user::cargo_bin("mecha10"),
424 home.join(".cargo").join("bin").join("mecha10")
425 );
426 assert_eq!(
427 user::local_bin("mecha10"),
428 home.join(".local").join("bin").join("mecha10")
429 );
430 }
431
432 #[test]
433 fn simulation_project_dirs_share_the_simulation_root() {
434 assert_eq!(project::SIMULATION_MODELS_DIR, "configs/simulation/models");
435 assert_eq!(project::SIMULATION_ENVIRONMENTS_DIR, "configs/simulation/environments");
436 for dir in [project::SIMULATION_MODELS_DIR, project::SIMULATION_ENVIRONMENTS_DIR] {
437 assert!(dir.starts_with(&format!("{}/", project::SIMULATION_DIR)));
438 }
439 }
440
441 #[test]
444 fn rl_tasks_dir_lives_under_configs_rl_not_simulation() {
445 assert_eq!(project::RL_TASKS_DIR, "configs/rl/tasks");
446 assert!(!project::RL_TASKS_DIR.starts_with(&format!("{}/", project::SIMULATION_DIR)));
447 }
448
449 #[test]
450 fn test_config_paths() {
451 assert_eq!(
452 config::framework_node("object-detector"),
453 "configs/nodes/@mecha10/object-detector/config.json"
454 );
455 assert_eq!(config::project_node("camera"), "configs/nodes/camera/config.json");
456 }
457
458 #[test]
459 fn test_framework_node_dir() {
460 assert_eq!(config::framework_node_dir("speaker"), "configs/nodes/@mecha10/speaker");
461 }
462
463 #[test]
464 fn test_framework_node_schema_dir_lives_under_nodes_not_configs_nodes() {
465 assert_eq!(project::framework_node_schema_dir("speaker"), "nodes/@mecha10/speaker");
466 assert_ne!(
467 project::framework_node_schema_dir("speaker"),
468 config::framework_node_dir("speaker"),
469 "schema dir (wave 10) must not collide with the config.json dir"
470 );
471 }
472
473 #[test]
474 fn test_node_config_candidates_order() {
475 assert_eq!(
476 config::node_config_candidates("teleop"),
477 [
478 "configs/nodes/@mecha10/teleop/config.json".to_string(),
479 "configs/nodes/teleop/config.json".to_string(),
480 ]
481 );
482 }
483
484 #[test]
485 fn test_behavior_config_paths() {
486 assert_eq!(
487 config::framework_behavior("idle_wander"),
488 "configs/behaviors/@mecha10/idle_wander/config.json"
489 );
490 assert_eq!(
491 config::project_behavior("idle_wander"),
492 "configs/behaviors/idle_wander/config.json"
493 );
494 }
495
496 #[test]
497 fn test_framework_behavior_dir() {
498 assert_eq!(
499 config::framework_behavior_dir("idle_wander"),
500 "configs/behaviors/@mecha10/idle_wander"
501 );
502 assert_eq!(
503 config::framework_behavior("idle_wander"),
504 format!("{}/config.json", config::framework_behavior_dir("idle_wander"))
505 );
506 }
507
508 #[test]
509 fn test_behavior_config_candidates_order() {
510 assert_eq!(
511 config::behavior_config_candidates("idle_wander"),
512 [
513 "configs/behaviors/idle_wander/config.json".to_string(),
514 "configs/behaviors/@mecha10/idle_wander/config.json".to_string(),
515 ]
516 );
517 }
518
519 #[test]
520 fn test_target_path() {
521 assert_eq!(target_path("release", "my-node"), "target/release/my-node");
522 assert_eq!(
523 target_path_with_triple(Some("aarch64-unknown-linux-gnu"), "release"),
524 "target/aarch64-unknown-linux-gnu/release"
525 );
526 assert_eq!(target_path_with_triple(None, "debug"), "target/debug");
527 }
528
529 #[test]
530 fn test_container_paths() {
531 assert_eq!(
532 container::project_path("configs/test.json"),
533 "/project/configs/test.json"
534 );
535 assert_eq!(container::framework_path("packages/core"), "/mecha10/packages/core");
536 }
537
538 #[test]
539 fn test_mecha10_api_base_url_default() {
540 let _guard = ENV_MUTEX.lock().unwrap();
541 std::env::remove_var("MECHA10_API_URL");
542 assert_eq!(urls::mecha10_api_base_url(), "https://mecha.industries/api");
543 }
544
545 #[test]
546 fn test_mecha10_api_base_url_env_override() {
547 let _guard = ENV_MUTEX.lock().unwrap();
548 std::env::set_var("MECHA10_API_URL", "http://localhost:4000/api/");
549 assert_eq!(urls::mecha10_api_base_url(), "http://localhost:4000/api");
550 std::env::remove_var("MECHA10_API_URL");
551 }
552
553 #[test]
554 fn test_mecha10_api_path_does_not_double_the_api_segment() {
555 let _guard = ENV_MUTEX.lock().unwrap();
556 std::env::remove_var("MECHA10_API_URL");
557 assert_eq!(
558 urls::mecha10_api_path("/models/catalog"),
559 "https://mecha.industries/api/models/catalog"
560 );
561 }
562
563 #[test]
564 fn test_mecha10_api_path_works_without_a_leading_slash() {
565 let _guard = ENV_MUTEX.lock().unwrap();
566 std::env::remove_var("MECHA10_API_URL");
567 assert_eq!(
568 urls::mecha10_api_path("downloads/model/deepghs/yolos/yolov8n/model.onnx"),
569 "https://mecha.industries/api/downloads/model/deepghs/yolos/yolov8n/model.onnx"
570 );
571 }
572
573 static ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
576}