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 DATASET_JOBS_DIR: &str = "configs/datasets/jobs";
78
79 pub const TARGET_DIR: &str = "target";
81
82 pub const SRC_DIR: &str = "src";
84
85 pub const STATE_DIR: &str = ".mecha10";
88
89 pub const DEV_PREFLIGHT_CACHE_FILE: &str = ".mecha10/dev-preflight-cache.json";
91
92 pub const ASSETS_CACHE_DIR: &str = ".mecha10/assets";
95}
96
97pub mod config {
99 use super::project;
100
101 pub const DIR: &str = "configs";
103
104 pub const NODES_DIR: &str = "configs/nodes";
106
107 pub const NODES_MECHA10_DIR: &str = "configs/nodes/@mecha10";
109
110 fn framework_config(category_dir: &str, name: &str) -> String {
112 format!("{category_dir}/@mecha10/{name}/config.json")
113 }
114
115 fn project_config(category_dir: &str, name: &str) -> String {
117 format!("{category_dir}/{name}/config.json")
118 }
119
120 fn config_candidates(category_dir: &str, name: &str) -> [String; 2] {
123 [framework_config(category_dir, name), project_config(category_dir, name)]
124 }
125
126 pub fn framework_node(node_name: &str) -> String {
128 framework_config(NODES_DIR, node_name)
129 }
130
131 pub fn project_node(node_name: &str) -> String {
133 project_config(NODES_DIR, node_name)
134 }
135
136 pub fn framework_node_dir(node_name: &str) -> String {
140 format!("{}/{}", NODES_MECHA10_DIR, node_name)
141 }
142
143 pub fn node_config_candidates(node_name: &str) -> [String; 2] {
146 config_candidates(NODES_DIR, node_name)
147 }
148
149 pub fn framework_behavior(behavior_name: &str) -> String {
152 framework_config(project::BEHAVIORS_DIR, behavior_name)
153 }
154
155 pub fn framework_behavior_dir(behavior_name: &str) -> String {
158 format!("{}/@mecha10/{}", project::BEHAVIORS_DIR, behavior_name)
159 }
160
161 pub fn project_behavior(behavior_name: &str) -> String {
164 project_config(project::BEHAVIORS_DIR, behavior_name)
165 }
166
167 pub fn behavior_config_candidates(behavior_name: &str) -> [String; 2] {
170 [project_behavior(behavior_name), framework_behavior(behavior_name)]
171 }
172}
173
174pub mod docker {
176 pub const DIR: &str = "docker";
178
179 pub const COMPOSE_FILE: &str = "docker/docker-compose.yml";
181
182 pub const COMPOSE_REMOTE_FILE: &str = "docker/docker-compose.remote.yml";
184
185 pub const DOCKERFILE_REMOTE: &str = "docker/Dockerfile.remote";
187
188 pub const ENTRYPOINT_REMOTE: &str = "docker/entrypoint.sh";
190
191 pub const COMPOSE_RL_FILE: &str = "docker/docker-compose.rl.yml";
193
194 pub const DOCKERFILE_RL: &str = "docker/Dockerfile.rl";
196
197 pub const ENTRYPOINT_RL: &str = "docker/entrypoint-rl.sh";
201}
202
203pub mod env {
205 pub const EXAMPLE: &str = ".env.example";
207
208 pub const FILE: &str = ".env";
210}
211
212pub mod meta {
214 pub const README: &str = "README.md";
216
217 pub const GITIGNORE: &str = ".gitignore";
219
220 pub const PACKAGE_JSON: &str = "package.json";
222
223 pub const REQUIREMENTS_TXT: &str = "requirements.txt";
225
226 pub const PYTHON_VERSION: &str = ".python-version";
229}
230
231pub mod rust {
233 pub const CARGO_TOML: &str = "Cargo.toml";
235
236 pub const CARGO_LOCK: &str = "Cargo.lock";
238
239 pub const MAIN_RS: &str = "src/main.rs";
241
242 pub const LIB_RS: &str = "src/lib.rs";
244
245 pub const BUILD_RS: &str = "build.rs";
247
248 pub const RUSTFMT_TOML: &str = "rustfmt.toml";
250}
251
252pub mod model {
254 pub const ONNX_FILE: &str = "model.onnx";
256
257 pub const LABELS_FILE: &str = "labels.txt";
259
260 pub const CONFIG_FILE: &str = "config.json";
262}
263
264pub mod user {
266 use std::path::PathBuf;
267
268 pub const MECHA10_DIR: &str = mecha10_core::fs_utils::MECHA10_DIR;
272
273 pub const CREDENTIALS_FILE: &str = ".mecha10/credentials.json";
277
278 pub const TEMPLATES_DIR: &str = ".mecha10/templates";
280
281 pub const BIN_DIR: &str = ".mecha10/bin";
283
284 pub fn mecha10_dir() -> PathBuf {
286 dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")).join(MECHA10_DIR)
287 }
288
289 pub fn credentials_file() -> PathBuf {
293 mecha10_auth::default_credentials_path()
294 }
295
296 pub fn templates_dir() -> PathBuf {
298 mecha10_dir().join("templates")
299 }
300
301 pub fn bin_dir() -> PathBuf {
303 mecha10_dir().join("bin")
304 }
305
306 pub fn bin(name: &str) -> PathBuf {
308 bin_dir().join(name)
309 }
310
311 pub fn onnxruntime_dir() -> PathBuf {
317 mecha10_dir().join("onnxruntime")
318 }
319
320 pub fn onnxruntime_dylib_path() -> PathBuf {
329 onnxruntime_dir().join("libonnxruntime.dylib")
330 }
331
332 pub fn cargo_bin(name: &str) -> PathBuf {
334 home_bin_dir(".cargo", name)
335 }
336
337 pub fn local_bin(name: &str) -> PathBuf {
339 home_bin_dir(".local", name)
340 }
341
342 fn home_bin_dir(home_subdir: &str, name: &str) -> PathBuf {
345 mecha10_core::fs_utils::home_subpath(&[home_subdir, "bin", name])
346 }
347}
348
349pub mod container {
351 pub const PROJECT_ROOT: &str = "/project";
353
354 pub const FRAMEWORK_ROOT: &str = "/mecha10";
356
357 pub const APP_ROOT: &str = "/app";
359
360 pub const REMOTE_CONFIG: &str = "/app/mecha10.json";
362
363 pub fn project_path(relative: &str) -> String {
365 format!("{}/{}", PROJECT_ROOT, relative)
366 }
367
368 pub fn framework_path(relative: &str) -> String {
370 format!("{}/{}", FRAMEWORK_ROOT, relative)
371 }
372}
373
374pub mod framework {
376 pub const PACKAGES_DIR: &str = "packages";
378
379 pub const NODES_DIR: &str = "packages/nodes";
381
382 pub const DRIVERS_DIR: &str = "packages/drivers";
384
385 pub const ROOT_MARKER_DIR: &str = "packages/core";
387
388 pub const RELEASE_BINARY: &str = "target/release/mecha10";
390
391 pub const DEBUG_BINARY: &str = "target/debug/mecha10";
393
394 pub fn node_config(node_name: &str) -> String {
396 format!("packages/nodes/{}/configs/config.json", node_name)
397 }
398}
399
400pub mod urls {
402 pub const MECHA10_API_URL: &str = "https://mecha.industries/api";
405
406 pub fn mecha10_api_base_url() -> String {
410 std::env::var("MECHA10_API_URL")
411 .unwrap_or_else(|_| MECHA10_API_URL.to_string())
412 .trim_end_matches('/')
413 .to_string()
414 }
415
416 pub fn mecha10_api_path(path: &str) -> String {
419 format!("{}/{}", mecha10_api_base_url(), path.trim_start_matches('/'))
420 }
421
422 pub const AUTH_URL: &str = "https://mecha.industries/api/auth";
424}
425
426pub fn target_path(profile: &str, binary: &str) -> String {
428 format!("target/{}/{}", profile, binary)
429}
430
431pub fn target_path_with_triple(target: Option<&str>, profile: &str) -> String {
433 match target {
434 Some(t) => format!("target/{}/{}", t, profile),
435 None => format!("target/{}", profile),
436 }
437}
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442
443 #[test]
444 fn credentials_file_matches_mecha10_auth_source_of_truth() {
445 assert_eq!(user::credentials_file(), mecha10_auth::default_credentials_path());
446 }
447
448 #[test]
451 fn cargo_bin_and_local_bin_join_name_under_their_own_home_subdir() {
452 let home = dirs::home_dir().expect("home dir must resolve in test environment");
453 assert_eq!(
454 user::cargo_bin("mecha10"),
455 home.join(".cargo").join("bin").join("mecha10")
456 );
457 assert_eq!(
458 user::local_bin("mecha10"),
459 home.join(".local").join("bin").join("mecha10")
460 );
461 }
462
463 #[test]
467 fn onnxruntime_dylib_path_lives_under_the_mecha10_dir() {
468 assert_eq!(user::onnxruntime_dir(), user::mecha10_dir().join("onnxruntime"));
469 assert_eq!(
470 user::onnxruntime_dylib_path(),
471 user::mecha10_dir().join("onnxruntime").join("libonnxruntime.dylib")
472 );
473 }
474
475 #[test]
476 fn simulation_project_dirs_share_the_simulation_root() {
477 assert_eq!(project::SIMULATION_MODELS_DIR, "configs/simulation/models");
478 assert_eq!(project::SIMULATION_ENVIRONMENTS_DIR, "configs/simulation/environments");
479 for dir in [project::SIMULATION_MODELS_DIR, project::SIMULATION_ENVIRONMENTS_DIR] {
480 assert!(dir.starts_with(&format!("{}/", project::SIMULATION_DIR)));
481 }
482 }
483
484 #[test]
487 fn rl_tasks_dir_lives_under_configs_rl_not_simulation() {
488 assert_eq!(project::RL_TASKS_DIR, "configs/rl/tasks");
489 assert!(!project::RL_TASKS_DIR.starts_with(&format!("{}/", project::SIMULATION_DIR)));
490 }
491
492 #[test]
496 fn dataset_jobs_dir_does_not_live_under_the_rl_train_job_catalog() {
497 assert_eq!(project::DATASET_JOBS_DIR, "configs/datasets/jobs");
498 assert!(!project::DATASET_JOBS_DIR.starts_with("configs/rl/jobs"));
499 }
500
501 #[test]
502 fn test_config_paths() {
503 assert_eq!(
504 config::framework_node("object-detector"),
505 "configs/nodes/@mecha10/object-detector/config.json"
506 );
507 assert_eq!(config::project_node("camera"), "configs/nodes/camera/config.json");
508 }
509
510 #[test]
511 fn test_framework_node_dir() {
512 assert_eq!(config::framework_node_dir("speaker"), "configs/nodes/@mecha10/speaker");
513 }
514
515 #[test]
516 fn test_framework_node_schema_dir_lives_under_nodes_not_configs_nodes() {
517 assert_eq!(project::framework_node_schema_dir("speaker"), "nodes/@mecha10/speaker");
518 assert_ne!(
519 project::framework_node_schema_dir("speaker"),
520 config::framework_node_dir("speaker"),
521 "schema dir (wave 10) must not collide with the config.json dir"
522 );
523 }
524
525 #[test]
526 fn test_node_config_candidates_order() {
527 assert_eq!(
528 config::node_config_candidates("teleop"),
529 [
530 "configs/nodes/@mecha10/teleop/config.json".to_string(),
531 "configs/nodes/teleop/config.json".to_string(),
532 ]
533 );
534 }
535
536 #[test]
537 fn test_behavior_config_paths() {
538 assert_eq!(
539 config::framework_behavior("idle_wander"),
540 "configs/behaviors/@mecha10/idle_wander/config.json"
541 );
542 assert_eq!(
543 config::project_behavior("idle_wander"),
544 "configs/behaviors/idle_wander/config.json"
545 );
546 }
547
548 #[test]
549 fn test_framework_behavior_dir() {
550 assert_eq!(
551 config::framework_behavior_dir("idle_wander"),
552 "configs/behaviors/@mecha10/idle_wander"
553 );
554 assert_eq!(
555 config::framework_behavior("idle_wander"),
556 format!("{}/config.json", config::framework_behavior_dir("idle_wander"))
557 );
558 }
559
560 #[test]
561 fn test_behavior_config_candidates_order() {
562 assert_eq!(
563 config::behavior_config_candidates("idle_wander"),
564 [
565 "configs/behaviors/idle_wander/config.json".to_string(),
566 "configs/behaviors/@mecha10/idle_wander/config.json".to_string(),
567 ]
568 );
569 }
570
571 #[test]
572 fn test_target_path() {
573 assert_eq!(target_path("release", "my-node"), "target/release/my-node");
574 assert_eq!(
575 target_path_with_triple(Some("aarch64-unknown-linux-gnu"), "release"),
576 "target/aarch64-unknown-linux-gnu/release"
577 );
578 assert_eq!(target_path_with_triple(None, "debug"), "target/debug");
579 }
580
581 #[test]
582 fn test_container_paths() {
583 assert_eq!(
584 container::project_path("configs/test.json"),
585 "/project/configs/test.json"
586 );
587 assert_eq!(container::framework_path("packages/core"), "/mecha10/packages/core");
588 }
589
590 #[test]
591 fn test_mecha10_api_base_url_default() {
592 let _guard = ENV_MUTEX.lock().unwrap();
593 std::env::remove_var("MECHA10_API_URL");
594 assert_eq!(urls::mecha10_api_base_url(), "https://mecha.industries/api");
595 }
596
597 #[test]
598 fn test_mecha10_api_base_url_env_override() {
599 let _guard = ENV_MUTEX.lock().unwrap();
600 std::env::set_var("MECHA10_API_URL", "http://localhost:4000/api/");
601 assert_eq!(urls::mecha10_api_base_url(), "http://localhost:4000/api");
602 std::env::remove_var("MECHA10_API_URL");
603 }
604
605 #[test]
606 fn test_mecha10_api_path_does_not_double_the_api_segment() {
607 let _guard = ENV_MUTEX.lock().unwrap();
608 std::env::remove_var("MECHA10_API_URL");
609 assert_eq!(
610 urls::mecha10_api_path("/models/catalog"),
611 "https://mecha.industries/api/models/catalog"
612 );
613 }
614
615 #[test]
616 fn test_mecha10_api_path_works_without_a_leading_slash() {
617 let _guard = ENV_MUTEX.lock().unwrap();
618 std::env::remove_var("MECHA10_API_URL");
619 assert_eq!(
620 urls::mecha10_api_path("downloads/model/deepghs/yolos/yolov8n/model.onnx"),
621 "https://mecha.industries/api/downloads/model/deepghs/yolos/yolov8n/model.onnx"
622 );
623 }
624
625 static ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
628}