Skip to main content

mecha10_cli_core/
paths.rs

1//! Centralized path definitions for the CLI
2//!
3//! All hardcoded paths should be defined here to ensure consistency
4//! and make it easy to update paths across the codebase.
5//!
6//! # Organization
7//!
8//! - **Project paths**: Paths relative to project root (mecha10.json location)
9//! - **User paths**: Paths in user's home directory (~/.mecha10)
10//! - **Docker paths**: Paths used inside Docker containers
11//! - **Framework paths**: Paths within the mecha10 framework source
12
13// Allow dead code during migration - paths will be used incrementally
14#![allow(dead_code)]
15
16use std::path::{Path, PathBuf};
17
18// =============================================================================
19// Project Structure - Paths relative to project root
20// =============================================================================
21
22/// Project configuration file
23pub const PROJECT_CONFIG: &str = "mecha10.json";
24
25/// Project directories
26pub mod project {
27    /// Node source code directory
28    pub const NODES_DIR: &str = "nodes";
29
30    /// Driver source code directory
31    pub const DRIVERS_DIR: &str = "drivers";
32
33    /// Types directory
34    pub const TYPES_DIR: &str = "types";
35
36    /// Behavior trees directory
37    pub const BEHAVIORS_DIR: &str = "behaviors";
38
39    /// Assets directory
40    pub const ASSETS_DIR: &str = "assets";
41
42    /// Assets images subdirectory
43    pub const ASSETS_IMAGES_DIR: &str = "assets/images";
44
45    /// Models directory (ONNX models, etc.)
46    pub const MODELS_DIR: &str = "models";
47
48    /// Logs directory
49    pub const LOGS_DIR: &str = "logs";
50
51    /// Simulation directory
52    pub const SIMULATION_DIR: &str = "simulation";
53
54    /// Simulation models directory
55    pub const SIMULATION_MODELS_DIR: &str = "simulation/models";
56
57    /// Simulation environments directory
58    pub const SIMULATION_ENVIRONMENTS_DIR: &str = "simulation/environments";
59
60    /// Get path to a model's config file
61    pub fn model_config(model_name: &str) -> String {
62        format!("simulation/models/{}/model.json", model_name)
63    }
64
65    /// Get path to an environment's config file
66    pub fn environment_config(env_name: &str) -> String {
67        format!("simulation/environments/{}/environment.json", env_name)
68    }
69
70    /// Simulation output directory (generated robot/scene files)
71    pub const SIMULATION_OUTPUT_DIR: &str = "simulation/output";
72
73    /// Cargo build target directory
74    pub const TARGET_DIR: &str = "target";
75
76    /// Source directory
77    pub const SRC_DIR: &str = "src";
78}
79
80/// Configuration paths within project
81pub mod config {
82    /// Base configs directory
83    pub const DIR: &str = "configs";
84
85    /// Node configs base directory
86    pub const NODES_DIR: &str = "configs/nodes";
87
88    /// Framework node configs (@mecha10 scope)
89    pub const NODES_MECHA10_DIR: &str = "configs/nodes/@mecha10";
90
91    /// Local node configs (@local scope)
92    pub const NODES_LOCAL_DIR: &str = "configs/nodes/@local";
93
94    /// Simulation configs directory
95    pub const SIMULATION_DIR: &str = "configs/simulation";
96
97    /// Simulation config file
98    pub const SIMULATION_CONFIG: &str = "configs/simulation/config.json";
99
100    /// Get config path for a framework node
101    pub fn framework_node(node_name: &str) -> String {
102        format!("configs/nodes/@mecha10/{}/config.json", node_name)
103    }
104
105    /// Get config path for a local node
106    pub fn local_node(node_name: &str) -> String {
107        format!("configs/nodes/@local/{}/config.json", node_name)
108    }
109
110    /// Get config path for a project node (plain name)
111    pub fn project_node(node_name: &str) -> String {
112        format!("configs/nodes/{}/config.json", node_name)
113    }
114}
115
116/// Docker-related paths within project
117pub mod docker {
118    /// Docker directory
119    pub const DIR: &str = "docker";
120
121    /// Main docker-compose file
122    pub const COMPOSE_FILE: &str = "docker/docker-compose.yml";
123
124    /// Remote nodes docker-compose file
125    pub const COMPOSE_REMOTE_FILE: &str = "docker/docker-compose.remote.yml";
126
127    /// Remote nodes Dockerfile
128    pub const DOCKERFILE_REMOTE: &str = "docker/Dockerfile.remote";
129
130    /// Robot builder Dockerfile (for cross-compilation)
131    pub const DOCKERFILE_ROBOT_BUILDER: &str = "docker/robot-builder.Dockerfile";
132}
133
134/// Environment files
135pub mod env {
136    /// Environment example file
137    pub const EXAMPLE: &str = ".env.example";
138
139    /// Environment file
140    pub const FILE: &str = ".env";
141}
142
143/// Meta files (README, gitignore, etc.)
144pub mod meta {
145    /// README file
146    pub const README: &str = "README.md";
147
148    /// Git ignore file
149    pub const GITIGNORE: &str = ".gitignore";
150
151    /// Package.json for Node.js tooling
152    pub const PACKAGE_JSON: &str = "package.json";
153
154    /// Python requirements
155    pub const REQUIREMENTS_TXT: &str = "requirements.txt";
156
157    /// Lint config
158    pub const LS_LINT_CONFIG: &str = ".ls-lint.yml";
159}
160
161/// Rust project files
162pub mod rust {
163    /// Cargo workspace manifest
164    pub const CARGO_TOML: &str = "Cargo.toml";
165
166    /// Cargo lock file
167    pub const CARGO_LOCK: &str = "Cargo.lock";
168
169    /// Main entry point
170    pub const MAIN_RS: &str = "src/main.rs";
171
172    /// Library entry point
173    pub const LIB_RS: &str = "src/lib.rs";
174
175    /// Build script
176    pub const BUILD_RS: &str = "build.rs";
177
178    /// Rustfmt config
179    pub const RUSTFMT_TOML: &str = "rustfmt.toml";
180
181    /// Cargo config directory
182    pub const CARGO_CONFIG_DIR: &str = ".cargo";
183
184    /// Cargo config file
185    pub const CARGO_CONFIG: &str = ".cargo/config.toml";
186}
187
188/// Model files within a model directory
189pub mod model {
190    /// ONNX model file
191    pub const ONNX_FILE: &str = "model.onnx";
192
193    /// Labels file
194    pub const LABELS_FILE: &str = "labels.txt";
195
196    /// Model config file
197    pub const CONFIG_FILE: &str = "config.json";
198}
199
200// =============================================================================
201// User Home Paths - Paths in ~/.mecha10
202// =============================================================================
203
204/// User-level paths in home directory
205pub mod user {
206    use std::path::PathBuf;
207
208    /// Base mecha10 directory in user home
209    pub const MECHA10_DIR: &str = ".mecha10";
210
211    /// Credentials file
212    pub const CREDENTIALS_FILE: &str = ".mecha10/credentials.json";
213
214    /// Templates cache directory
215    pub const TEMPLATES_DIR: &str = ".mecha10/templates";
216
217    /// Simulation assets cache directory
218    pub const SIMULATION_DIR: &str = ".mecha10/simulation";
219
220    /// Current simulation assets symlink
221    pub const SIMULATION_CURRENT: &str = ".mecha10/simulation/current";
222
223    /// Simulation version file
224    pub const SIMULATION_VERSION: &str = ".mecha10/simulation/version";
225
226    /// Binary installation directory
227    pub const BIN_DIR: &str = ".mecha10/bin";
228
229    /// Get the base mecha10 directory path
230    pub fn mecha10_dir() -> PathBuf {
231        dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")).join(MECHA10_DIR)
232    }
233
234    /// Get the templates cache directory
235    pub fn templates_dir() -> PathBuf {
236        mecha10_dir().join("templates")
237    }
238
239    /// Get the simulation assets directory
240    pub fn simulation_dir() -> PathBuf {
241        mecha10_dir().join("simulation")
242    }
243
244    /// Get the current simulation assets path
245    pub fn simulation_current() -> PathBuf {
246        mecha10_dir().join("simulation/current")
247    }
248
249    /// Get the binary directory
250    pub fn bin_dir() -> PathBuf {
251        mecha10_dir().join("bin")
252    }
253
254    /// Get path to a binary in the mecha10 bin directory
255    pub fn bin(name: &str) -> PathBuf {
256        bin_dir().join(name)
257    }
258
259    /// Get path to a binary in cargo bin directory
260    pub fn cargo_bin(name: &str) -> PathBuf {
261        mecha10_core::fs_utils::home_subpath(&[".cargo", "bin", name])
262    }
263
264    /// Get path to a binary in local bin directory
265    pub fn local_bin(name: &str) -> PathBuf {
266        mecha10_core::fs_utils::home_subpath(&[".local", "bin", name])
267    }
268}
269
270// =============================================================================
271// Docker Container Paths - Paths used inside containers
272// =============================================================================
273
274/// Paths used inside Docker containers
275pub mod container {
276    /// Project mount point in container
277    pub const PROJECT_ROOT: &str = "/project";
278
279    /// Framework mount point in container
280    pub const FRAMEWORK_ROOT: &str = "/mecha10";
281
282    /// App directory (for mecha10-remote)
283    pub const APP_ROOT: &str = "/app";
284
285    /// Config file in mecha10-remote container
286    pub const REMOTE_CONFIG: &str = "/app/mecha10.json";
287
288    /// Get project-relative path inside container
289    pub fn project_path(relative: &str) -> String {
290        format!("{}/{}", PROJECT_ROOT, relative)
291    }
292
293    /// Get framework-relative path inside container
294    pub fn framework_path(relative: &str) -> String {
295        format!("{}/{}", FRAMEWORK_ROOT, relative)
296    }
297}
298
299// =============================================================================
300// Framework Paths - Paths within mecha10 framework source
301// =============================================================================
302
303/// Paths within the mecha10 framework source
304pub mod framework {
305    /// Packages directory
306    pub const PACKAGES_DIR: &str = "packages";
307
308    /// Nodes package directory
309    pub const NODES_DIR: &str = "packages/nodes";
310
311    /// Drivers package directory
312    pub const DRIVERS_DIR: &str = "packages/drivers";
313
314    /// Services package directory
315    pub const SERVICES_DIR: &str = "packages/services";
316
317    /// Simulation package directory
318    pub const SIMULATION_DIR: &str = "packages/simulation";
319
320    /// Simulation models directory
321    pub const SIMULATION_MODELS_DIR: &str = "packages/simulation/models";
322
323    /// Simulation environments directory
324    pub const SIMULATION_ENVIRONMENTS_DIR: &str = "packages/simulation/environments";
325
326    /// Robot tasks environments directory (for environment matching)
327    pub const ROBOT_TASKS_DIR: &str = "packages/simulation/environments/robot-tasks";
328
329    /// Robot tasks catalog file
330    pub const ROBOT_TASKS_CATALOG: &str = "packages/simulation/environments/robot-tasks/catalog.json";
331
332    /// Python taskrunner package
333    pub const TASKRUNNER_DIR: &str = "packages/taskrunner";
334
335    /// Python RL trainer package (hosts the MuJoCo simulation backend, `mecha10_rl.mujoco`)
336    pub const RL_TRAINER_DIR: &str = "packages/rl-trainer";
337
338    /// Marker directory used to detect the mecha10 framework root
339    pub const ROOT_MARKER_DIR: &str = "packages/core";
340
341    /// Release binary
342    pub const RELEASE_BINARY: &str = "target/release/mecha10";
343
344    /// Debug binary
345    pub const DEBUG_BINARY: &str = "target/debug/mecha10";
346
347    /// Node runner release binary
348    pub const NODE_RUNNER_RELEASE: &str = "target/release/mecha10-node-runner";
349
350    /// Node runner debug binary
351    pub const NODE_RUNNER_DEBUG: &str = "target/debug/mecha10-node-runner";
352
353    /// Get node config path within framework
354    pub fn node_config(node_name: &str) -> String {
355        format!("packages/nodes/{}/configs/config.json", node_name)
356    }
357}
358
359// =============================================================================
360// External URLs
361// =============================================================================
362
363/// External service URLs
364pub mod urls {
365    /// Default base URL for the public `mecha10-api` service.
366    ///
367    /// The CLI never talks to the private homelab Minio instance directly - all
368    /// framework/runtime artifact downloads (CLI/launcher/node binaries, simulation
369    /// assets, templates, remote-image manifests, ML models) are proxied through
370    /// `mecha10-api`'s `/api/downloads/*` routes (see `packages/mecha10-api/src/catalog/downloads`),
371    /// which run *inside* the private network and stream bytes back out.
372    pub const MECHA10_API_URL: &str = "https://mecha.industries/api";
373
374    /// Build the `mecha10-api` base URL (e.g. `https://mecha.industries/api`).
375    ///
376    /// Override at runtime with the `MECHA10_API_URL` env var (e.g. to point at a
377    /// local `mecha10-api` instance during development).
378    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    /// Default authentication URL
386    pub const AUTH_URL: &str = "https://mecha.industries/api/auth";
387}
388
389// =============================================================================
390// Helper Functions
391// =============================================================================
392
393/// Build a target output path
394pub fn target_path(profile: &str, binary: &str) -> String {
395    format!("target/{}/{}", profile, binary)
396}
397
398/// Build a target output path with optional target triple
399pub fn target_path_with_triple(target: Option<&str>, profile: &str) -> String {
400    match target {
401        Some(t) => format!("target/{}/{}", t, profile),
402        None => format!("target/{}", profile),
403    }
404}
405
406/// Get the project root by finding mecha10.json
407pub fn find_project_root(start: &Path) -> Option<PathBuf> {
408    let mut current = start.to_path_buf();
409    loop {
410        if current.join(PROJECT_CONFIG).exists() {
411            return Some(current);
412        }
413        if !current.pop() {
414            return None;
415        }
416    }
417}
418
419/// Check if a directory is a mecha10 project
420pub fn is_project_dir(dir: &Path) -> bool {
421    dir.join(PROJECT_CONFIG).exists()
422}
423
424#[cfg(test)]
425mod tests {
426    use super::*;
427
428    #[test]
429    fn test_config_paths() {
430        assert_eq!(
431            config::framework_node("object-detector"),
432            "configs/nodes/@mecha10/object-detector/config.json"
433        );
434        assert_eq!(
435            config::local_node("my-node"),
436            "configs/nodes/@local/my-node/config.json"
437        );
438        assert_eq!(config::project_node("camera"), "configs/nodes/camera/config.json");
439    }
440
441    #[test]
442    fn test_target_path() {
443        assert_eq!(target_path("release", "my-node"), "target/release/my-node");
444        assert_eq!(
445            target_path_with_triple(Some("aarch64-unknown-linux-gnu"), "release"),
446            "target/aarch64-unknown-linux-gnu/release"
447        );
448        assert_eq!(target_path_with_triple(None, "debug"), "target/debug");
449    }
450
451    #[test]
452    fn test_container_paths() {
453        assert_eq!(
454            container::project_path("configs/test.json"),
455            "/project/configs/test.json"
456        );
457        assert_eq!(container::framework_path("packages/core"), "/mecha10/packages/core");
458    }
459
460    #[test]
461    fn test_mecha10_api_base_url_default() {
462        // Guard against interference from other tests running in the same process.
463        let _guard = ENV_MUTEX.lock().unwrap();
464        std::env::remove_var("MECHA10_API_URL");
465        assert_eq!(urls::mecha10_api_base_url(), "https://mecha.industries/api");
466    }
467
468    #[test]
469    fn test_mecha10_api_base_url_env_override() {
470        let _guard = ENV_MUTEX.lock().unwrap();
471        std::env::set_var("MECHA10_API_URL", "http://localhost:4000/api/");
472        assert_eq!(urls::mecha10_api_base_url(), "http://localhost:4000/api");
473        std::env::remove_var("MECHA10_API_URL");
474    }
475
476    /// Serializes tests that mutate process-wide env vars, since `cargo test` runs
477    /// tests in this module concurrently by default.
478    static ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
479}