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
83pub mod config {
85 use super::project;
86
87 pub const DIR: &str = "configs";
89
90 pub const NODES_DIR: &str = "configs/nodes";
92
93 pub const NODES_MECHA10_DIR: &str = "configs/nodes/@mecha10";
95
96 fn framework_config(category_dir: &str, name: &str) -> String {
98 format!("{category_dir}/@mecha10/{name}/config.json")
99 }
100
101 fn project_config(category_dir: &str, name: &str) -> String {
103 format!("{category_dir}/{name}/config.json")
104 }
105
106 fn config_candidates(category_dir: &str, name: &str) -> [String; 2] {
109 [framework_config(category_dir, name), project_config(category_dir, name)]
110 }
111
112 pub fn framework_node(node_name: &str) -> String {
114 framework_config(NODES_DIR, node_name)
115 }
116
117 pub fn project_node(node_name: &str) -> String {
119 project_config(NODES_DIR, node_name)
120 }
121
122 pub fn framework_node_dir(node_name: &str) -> String {
126 format!("{}/{}", NODES_MECHA10_DIR, node_name)
127 }
128
129 pub fn node_config_candidates(node_name: &str) -> [String; 2] {
132 config_candidates(NODES_DIR, node_name)
133 }
134
135 pub fn framework_behavior(behavior_name: &str) -> String {
138 framework_config(project::BEHAVIORS_DIR, behavior_name)
139 }
140
141 pub fn framework_behavior_dir(behavior_name: &str) -> String {
144 format!("{}/@mecha10/{}", project::BEHAVIORS_DIR, behavior_name)
145 }
146
147 pub fn project_behavior(behavior_name: &str) -> String {
150 project_config(project::BEHAVIORS_DIR, behavior_name)
151 }
152
153 pub fn behavior_config_candidates(behavior_name: &str) -> [String; 2] {
156 [project_behavior(behavior_name), framework_behavior(behavior_name)]
157 }
158}
159
160pub mod docker {
162 pub const DIR: &str = "docker";
164
165 pub const COMPOSE_FILE: &str = "docker/docker-compose.yml";
167
168 pub const COMPOSE_REMOTE_FILE: &str = "docker/docker-compose.remote.yml";
170
171 pub const DOCKERFILE_REMOTE: &str = "docker/Dockerfile.remote";
173
174 pub const ENTRYPOINT_REMOTE: &str = "docker/entrypoint.sh";
176
177 pub const COMPOSE_RL_FILE: &str = "docker/docker-compose.rl.yml";
179
180 pub const DOCKERFILE_RL: &str = "docker/Dockerfile.rl";
182
183 pub const ENTRYPOINT_RL: &str = "docker/entrypoint-rl.sh";
187}
188
189pub mod env {
191 pub const EXAMPLE: &str = ".env.example";
193
194 pub const FILE: &str = ".env";
196}
197
198pub mod meta {
200 pub const README: &str = "README.md";
202
203 pub const GITIGNORE: &str = ".gitignore";
205
206 pub const PACKAGE_JSON: &str = "package.json";
208
209 pub const REQUIREMENTS_TXT: &str = "requirements.txt";
211
212 pub const PYTHON_VERSION: &str = ".python-version";
215}
216
217pub mod rust {
219 pub const CARGO_TOML: &str = "Cargo.toml";
221
222 pub const CARGO_LOCK: &str = "Cargo.lock";
224
225 pub const MAIN_RS: &str = "src/main.rs";
227
228 pub const LIB_RS: &str = "src/lib.rs";
230
231 pub const BUILD_RS: &str = "build.rs";
233
234 pub const RUSTFMT_TOML: &str = "rustfmt.toml";
236}
237
238pub mod model {
240 pub const ONNX_FILE: &str = "model.onnx";
242
243 pub const LABELS_FILE: &str = "labels.txt";
245
246 pub const CONFIG_FILE: &str = "config.json";
248}
249
250pub mod user {
252 use std::path::PathBuf;
253
254 pub const MECHA10_DIR: &str = mecha10_core::fs_utils::MECHA10_DIR;
258
259 pub const CREDENTIALS_FILE: &str = ".mecha10/credentials.json";
263
264 pub const TEMPLATES_DIR: &str = ".mecha10/templates";
266
267 pub const BIN_DIR: &str = ".mecha10/bin";
269
270 pub fn mecha10_dir() -> PathBuf {
272 dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")).join(MECHA10_DIR)
273 }
274
275 pub fn credentials_file() -> PathBuf {
279 mecha10_auth::default_credentials_path()
280 }
281
282 pub fn templates_dir() -> PathBuf {
284 mecha10_dir().join("templates")
285 }
286
287 pub fn bin_dir() -> PathBuf {
289 mecha10_dir().join("bin")
290 }
291
292 pub fn bin(name: &str) -> PathBuf {
294 bin_dir().join(name)
295 }
296
297 pub fn cargo_bin(name: &str) -> PathBuf {
299 home_bin_dir(".cargo", name)
300 }
301
302 pub fn local_bin(name: &str) -> PathBuf {
304 home_bin_dir(".local", name)
305 }
306
307 fn home_bin_dir(home_subdir: &str, name: &str) -> PathBuf {
310 mecha10_core::fs_utils::home_subpath(&[home_subdir, "bin", name])
311 }
312}
313
314pub mod container {
316 pub const PROJECT_ROOT: &str = "/project";
318
319 pub const FRAMEWORK_ROOT: &str = "/mecha10";
321
322 pub const APP_ROOT: &str = "/app";
324
325 pub const REMOTE_CONFIG: &str = "/app/mecha10.json";
327
328 pub fn project_path(relative: &str) -> String {
330 format!("{}/{}", PROJECT_ROOT, relative)
331 }
332
333 pub fn framework_path(relative: &str) -> String {
335 format!("{}/{}", FRAMEWORK_ROOT, relative)
336 }
337}
338
339pub mod framework {
341 pub const PACKAGES_DIR: &str = "packages";
343
344 pub const NODES_DIR: &str = "packages/nodes";
346
347 pub const DRIVERS_DIR: &str = "packages/drivers";
349
350 pub const ROOT_MARKER_DIR: &str = "packages/core";
352
353 pub const RELEASE_BINARY: &str = "target/release/mecha10";
355
356 pub const DEBUG_BINARY: &str = "target/debug/mecha10";
358
359 pub fn node_config(node_name: &str) -> String {
361 format!("packages/nodes/{}/configs/config.json", node_name)
362 }
363}
364
365pub mod urls {
367 pub const MECHA10_API_URL: &str = "https://mecha.industries/api";
370
371 pub fn mecha10_api_base_url() -> String {
375 std::env::var("MECHA10_API_URL")
376 .unwrap_or_else(|_| MECHA10_API_URL.to_string())
377 .trim_end_matches('/')
378 .to_string()
379 }
380
381 pub fn mecha10_api_path(path: &str) -> String {
384 format!("{}/{}", mecha10_api_base_url(), path.trim_start_matches('/'))
385 }
386
387 pub const AUTH_URL: &str = "https://mecha.industries/api/auth";
389}
390
391pub fn target_path(profile: &str, binary: &str) -> String {
393 format!("target/{}/{}", profile, binary)
394}
395
396pub fn target_path_with_triple(target: Option<&str>, profile: &str) -> String {
398 match target {
399 Some(t) => format!("target/{}/{}", t, profile),
400 None => format!("target/{}", profile),
401 }
402}
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407
408 #[test]
409 fn credentials_file_matches_mecha10_auth_source_of_truth() {
410 assert_eq!(user::credentials_file(), mecha10_auth::default_credentials_path());
411 }
412
413 #[test]
416 fn cargo_bin_and_local_bin_join_name_under_their_own_home_subdir() {
417 let home = dirs::home_dir().expect("home dir must resolve in test environment");
418 assert_eq!(
419 user::cargo_bin("mecha10"),
420 home.join(".cargo").join("bin").join("mecha10")
421 );
422 assert_eq!(
423 user::local_bin("mecha10"),
424 home.join(".local").join("bin").join("mecha10")
425 );
426 }
427
428 #[test]
429 fn simulation_project_dirs_share_the_simulation_root() {
430 assert_eq!(project::SIMULATION_MODELS_DIR, "configs/simulation/models");
431 assert_eq!(project::SIMULATION_ENVIRONMENTS_DIR, "configs/simulation/environments");
432 for dir in [project::SIMULATION_MODELS_DIR, project::SIMULATION_ENVIRONMENTS_DIR] {
433 assert!(dir.starts_with(&format!("{}/", project::SIMULATION_DIR)));
434 }
435 }
436
437 #[test]
440 fn rl_tasks_dir_lives_under_configs_rl_not_simulation() {
441 assert_eq!(project::RL_TASKS_DIR, "configs/rl/tasks");
442 assert!(!project::RL_TASKS_DIR.starts_with(&format!("{}/", project::SIMULATION_DIR)));
443 }
444
445 #[test]
446 fn test_config_paths() {
447 assert_eq!(
448 config::framework_node("object-detector"),
449 "configs/nodes/@mecha10/object-detector/config.json"
450 );
451 assert_eq!(config::project_node("camera"), "configs/nodes/camera/config.json");
452 }
453
454 #[test]
455 fn test_framework_node_dir() {
456 assert_eq!(config::framework_node_dir("speaker"), "configs/nodes/@mecha10/speaker");
457 }
458
459 #[test]
460 fn test_framework_node_schema_dir_lives_under_nodes_not_configs_nodes() {
461 assert_eq!(project::framework_node_schema_dir("speaker"), "nodes/@mecha10/speaker");
462 assert_ne!(
463 project::framework_node_schema_dir("speaker"),
464 config::framework_node_dir("speaker"),
465 "schema dir (wave 10) must not collide with the config.json dir"
466 );
467 }
468
469 #[test]
470 fn test_node_config_candidates_order() {
471 assert_eq!(
472 config::node_config_candidates("teleop"),
473 [
474 "configs/nodes/@mecha10/teleop/config.json".to_string(),
475 "configs/nodes/teleop/config.json".to_string(),
476 ]
477 );
478 }
479
480 #[test]
481 fn test_behavior_config_paths() {
482 assert_eq!(
483 config::framework_behavior("idle_wander"),
484 "configs/behaviors/@mecha10/idle_wander/config.json"
485 );
486 assert_eq!(
487 config::project_behavior("idle_wander"),
488 "configs/behaviors/idle_wander/config.json"
489 );
490 }
491
492 #[test]
493 fn test_framework_behavior_dir() {
494 assert_eq!(
495 config::framework_behavior_dir("idle-wander"),
496 "configs/behaviors/@mecha10/idle-wander"
497 );
498 assert_eq!(
499 config::framework_behavior("idle-wander"),
500 format!("{}/config.json", config::framework_behavior_dir("idle-wander"))
501 );
502 }
503
504 #[test]
505 fn test_behavior_config_candidates_order() {
506 assert_eq!(
507 config::behavior_config_candidates("idle_wander"),
508 [
509 "configs/behaviors/idle_wander/config.json".to_string(),
510 "configs/behaviors/@mecha10/idle_wander/config.json".to_string(),
511 ]
512 );
513 }
514
515 #[test]
516 fn test_target_path() {
517 assert_eq!(target_path("release", "my-node"), "target/release/my-node");
518 assert_eq!(
519 target_path_with_triple(Some("aarch64-unknown-linux-gnu"), "release"),
520 "target/aarch64-unknown-linux-gnu/release"
521 );
522 assert_eq!(target_path_with_triple(None, "debug"), "target/debug");
523 }
524
525 #[test]
526 fn test_container_paths() {
527 assert_eq!(
528 container::project_path("configs/test.json"),
529 "/project/configs/test.json"
530 );
531 assert_eq!(container::framework_path("packages/core"), "/mecha10/packages/core");
532 }
533
534 #[test]
535 fn test_mecha10_api_base_url_default() {
536 let _guard = ENV_MUTEX.lock().unwrap();
537 std::env::remove_var("MECHA10_API_URL");
538 assert_eq!(urls::mecha10_api_base_url(), "https://mecha.industries/api");
539 }
540
541 #[test]
542 fn test_mecha10_api_base_url_env_override() {
543 let _guard = ENV_MUTEX.lock().unwrap();
544 std::env::set_var("MECHA10_API_URL", "http://localhost:4000/api/");
545 assert_eq!(urls::mecha10_api_base_url(), "http://localhost:4000/api");
546 std::env::remove_var("MECHA10_API_URL");
547 }
548
549 #[test]
550 fn test_mecha10_api_path_does_not_double_the_api_segment() {
551 let _guard = ENV_MUTEX.lock().unwrap();
552 std::env::remove_var("MECHA10_API_URL");
553 assert_eq!(
554 urls::mecha10_api_path("/models/catalog"),
555 "https://mecha.industries/api/models/catalog"
556 );
557 }
558
559 #[test]
560 fn test_mecha10_api_path_works_without_a_leading_slash() {
561 let _guard = ENV_MUTEX.lock().unwrap();
562 std::env::remove_var("MECHA10_API_URL");
563 assert_eq!(
564 urls::mecha10_api_path("downloads/model/deepghs/yolos/yolov8n/model.onnx"),
565 "https://mecha.industries/api/downloads/model/deepghs/yolos/yolov8n/model.onnx"
566 );
567 }
568
569 static ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
572}