diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a29e468c9..8c85c32bb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v7 - name: Setup Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@1.97.1 with: components: rustfmt, clippy @@ -59,6 +59,15 @@ jobs: - name: Run Clippy run: cargo clippy --all --all-targets --all-features -- -D warnings + - name: Install Hawk + run: | + curl --proto '=https' --tlsv1.2 -LsSf \ + https://github.com/astral-sh/hawk/releases/download/0.1.10/cargo-hawk-installer.sh | sh + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + + - name: Run Hawk + run: cargo +1.97.1 hawk check -D warnings + - name: Check Rust-owned OpenAPI artifacts env: DATABASE_URL: sqlite:db/sqlite/dev.db diff --git a/.gitignore b/.gitignore index c2669a028..e3639b8cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Agent notes +PAPERCUTS.md + # Rust build artifacts /target/ /torc-server/target/ diff --git a/docs/src/contributing.md b/docs/src/contributing.md index 2fc83157e..074dd7572 100644 --- a/docs/src/contributing.md +++ b/docs/src/contributing.md @@ -13,11 +13,8 @@ cd torc 2. **Install Rust and dependencies:** -Make sure you have Rust 1.85 or later installed: - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -``` +Make sure you have Rust 1.95 or later installed. Hawk requires its analysis to run with the exact +compiler toolchain it was built against, currently Rust 1.97.1. 3. **Install cargo-nextest:** @@ -37,7 +34,15 @@ cargo install sqlx-cli --no-default-features --features sqlite cargo install cargo-release ``` -6. **Set up the database:** +6. **Install Hawk:** + +```bash +rustup toolchain install 1.97.1 +curl --proto '=https' --tlsv1.2 -LsSf \ + https://github.com/astral-sh/hawk/releases/download/0.1.10/cargo-hawk-installer.sh | sh +``` + +7. **Set up the database:** ```bash # Create .env file @@ -47,7 +52,7 @@ echo "DATABASE_URL=sqlite:torc.db" > .env sqlx migrate run --source torc-server/migrations ``` -7. **Build and test:** +8. **Build and test:** ```bash cargo build @@ -69,6 +74,9 @@ cargo clippy --all --all-targets --all-features -- -D warnings # Run all checks cargo fmt --check && cargo clippy --all --all-targets --all-features -- -D warnings + +# Check public APIs with Hawk (run with the pinned Hawk toolchain) +cargo +1.97.1 hawk check -D warnings ``` ### Adding Tests @@ -121,6 +129,7 @@ git commit -m "Add feature: description" cargo nextest run --all-features cargo fmt --check cargo clippy --all-targets --all-features -- -D warnings +cargo +1.97.1 hawk check -D warnings ``` 4. **Push to your fork:** diff --git a/hawk.toml b/hawk.toml new file mode 100644 index 000000000..7f60a4b5e --- /dev/null +++ b/hawk.toml @@ -0,0 +1,59 @@ +# Torc ships these binaries as part of its release artifacts. Keep the production +# surface explicit so Hawk can distinguish supported entry points from internal code. +[[production]] +package = "torc" +bin = "torc" +reason = "shipped unified CLI" + +[[production]] +package = "torc" +bin = "torc-openapi" +reason = "shipped OpenAPI utility" + +[[production]] +package = "torc" +bin = "torc-server" +reason = "shipped server binary" + +[[production]] +package = "torc" +bin = "torc-htpasswd" +reason = "shipped authentication utility" + +[[production]] +package = "torc" +bin = "torc-mcp-server" +reason = "shipped MCP server" + +[[production]] +package = "torc" +bin = "torc-dash" +reason = "shipped dashboard" + +[[production]] +package = "torc" +bin = "torc-slurm-job-runner" +reason = "shipped Slurm job runner" + +# These model aliases are part of the crate-root API retained for downstream users, +# even though Torc's in-workspace binaries do not import them across the crate boundary. +[[override]] +lint = "hawk::unnecessary_public" +crate = "torc" +item = "GetReadyJobRequirementsResponse" +level = "allow" +reason = "backward-compatible crate-root model re-export" + +[[override]] +lint = "hawk::unnecessary_public" +crate = "torc" +item = "ListLocalSchedulersResponse" +level = "allow" +reason = "backward-compatible crate-root model re-export" + +[[override]] +lint = "hawk::unnecessary_public" +crate = "torc" +item = "LocalSchedulerModel" +level = "allow" +reason = "backward-compatible crate-root model re-export" diff --git a/src/client.rs b/src/client.rs index 90f416153..b403152d5 100644 --- a/src/client.rs +++ b/src/client.rs @@ -14,7 +14,7 @@ pub mod async_cli_command; pub mod commands; pub mod errors; pub mod resource_correction; -pub mod ro_crate_utils; +pub(crate) mod ro_crate_utils; // Re-export config from the top-level module for backwards compatibility #[cfg(feature = "config")] @@ -29,7 +29,7 @@ pub mod remote; pub mod report_models; pub mod resource_monitor; pub mod scheduler_plan; -pub mod slurm_utils; +pub(crate) mod slurm_utils; pub mod sse_client; pub mod utils; pub mod version_check; @@ -47,8 +47,8 @@ pub use apis::{ system_api, tasks_api, user_data_api, workflow_actions_api, workflows_api, }; pub use hpc::{ - HpcDetection, HpcInterface, HpcJobInfo, HpcJobStats, HpcJobStatus, HpcManager, HpcPartition, - HpcProfile, HpcProfileRegistry, HpcType, SlurmInterface, create_hpc_interface, + HpcDetection, HpcInterface, HpcJobInfo, HpcJobStats, HpcJobStatus, HpcPartition, HpcProfile, + HpcProfileRegistry, SlurmInterface, }; pub use job_runner::JobRunner; // JobModel is re-exported from models (which re-exports from crate::models) @@ -59,11 +59,7 @@ pub use workflow_spec::{ }; // Report model types for inter-command data sharing -pub use report_models::{ - JobResultRecord, ResourceUtilizationReport, ResourceViolation, ResultsReport, -}; +pub use report_models::{ResourceUtilizationReport, ResourceViolation}; // Version checking utilities -pub use version_check::{ - ServerInfo, VersionCheckResult, VersionMismatchSeverity, check_and_warn, check_version, -}; +pub use version_check::{ServerInfo, VersionCheckResult, VersionMismatchSeverity, check_version}; diff --git a/src/client/async_cli_command.rs b/src/client/async_cli_command.rs index 7b929cd6d..30378c5d4 100644 --- a/src/client/async_cli_command.rs +++ b/src/client/async_cli_command.rs @@ -212,7 +212,7 @@ impl AsyncCliCommand { /// Returns the Slurm step name, if running inside an allocation. /// Set after `start()` is called. - pub fn step_name(&self) -> Option<&str> { + fn step_name(&self) -> Option<&str> { self.step_name.as_deref() } @@ -534,7 +534,7 @@ impl AsyncCliCommand { /// Returns the Slurm accounting stats collected for this job step, if any. /// Only populated when the job ran inside a Slurm allocation and sacct succeeded. - pub fn take_slurm_stats(&mut self) -> Option { + pub(crate) fn take_slurm_stats(&mut self) -> Option { self.slurm_stats.take() } @@ -582,7 +582,7 @@ impl AsyncCliCommand { /// // exit_code will be 143 (128 + 15) if killed by SIGTERM on Unix /// ``` #[cfg(unix)] - pub fn send_sigterm(&mut self) -> Result<(), Box> { + fn send_sigterm(&mut self) -> Result<(), Box> { if let Some(ref child) = self.handle { let pid = child.id(); debug!("Sending SIGTERM to job {} (PID {})", self.job_id, pid); @@ -656,7 +656,10 @@ impl AsyncCliCommand { /// **Note**: This method does not wait for the process to exit. Call /// [`wait_for_completion()`] afterwards to wait for the process and capture its exit code. #[cfg(unix)] - pub fn send_signal(&mut self, signal_name: &str) -> Result<(), Box> { + pub(crate) fn send_signal( + &mut self, + signal_name: &str, + ) -> Result<(), Box> { if let Some(ref child) = self.handle { let pid = child.id(); let signal = match signal_name { @@ -714,7 +717,7 @@ impl AsyncCliCommand { /// This is a forceful termination that cannot be caught or ignored by the process. /// Use this as a last resort after graceful termination has failed. #[cfg(unix)] - pub fn send_sigkill(&mut self) -> Result<(), Box> { + pub(crate) fn send_sigkill(&mut self) -> Result<(), Box> { if let Some(ref child) = self.handle { let pid = child.id(); debug!("Sending SIGKILL to job {} (PID {})", self.job_id, pid); @@ -842,7 +845,7 @@ impl AsyncCliCommand { /// Process ID of the job, once `start()` has spawned it. In Slurm mode this /// is the PID of the `srun` process, not of the job itself, which runs /// under slurmstepd. - pub fn pid(&self) -> Option { + pub(crate) fn pid(&self) -> Option { self.pid } diff --git a/src/client/commands.rs b/src/client/commands.rs index 13ffcbfe4..484e16947 100644 --- a/src/client/commands.rs +++ b/src/client/commands.rs @@ -2,7 +2,7 @@ pub mod access_groups; pub mod admin; pub mod compute_nodes; pub mod config; -pub mod diagnose; +pub(crate) mod diagnose; pub mod events; pub mod failure_handlers; pub mod files; @@ -11,7 +11,7 @@ pub mod job_dependencies; pub mod jobs; pub mod logs; pub mod orphan_detection; -pub mod output; +pub(crate) mod output; pub mod pagination; pub mod reconcile; pub mod recover; @@ -23,7 +23,7 @@ pub mod ro_crate; pub mod scheduled_compute_nodes; pub mod self_update; pub mod slurm; -pub mod table_format; +pub(crate) mod table_format; pub mod tasks; pub mod user_data; pub mod watch; @@ -114,19 +114,19 @@ pub fn select_workflow_interactively( } /// Helper function to get user name from parameter or environment variables -pub fn get_user_name(user: &Option) -> String { +fn get_user_name(user: &Option) -> String { if user.is_some() { return user.as_deref().unwrap().to_string(); } get_env_user_name() } -pub fn get_env_user_name() -> String { +pub(crate) fn get_env_user_name() -> String { crate::get_username() } /// Truncate string to specified length -pub fn truncate_string(s: &str, max_len: usize) -> String { +fn truncate_string(s: &str, max_len: usize) -> String { if s.len() <= max_len { s.to_string() } else { @@ -135,7 +135,7 @@ pub fn truncate_string(s: &str, max_len: usize) -> String { } /// Print API errors in a user-friendly way -pub fn print_error(operation: &str, error: &crate::client::apis::Error) { +pub(crate) fn print_error(operation: &str, error: &crate::client::apis::Error) { match error { crate::client::apis::Error::Reqwest(e) => { eprintln!("Network error while {}: {}", operation, e); diff --git a/src/client/commands/diagnose.rs b/src/client/commands/diagnose.rs index c8529d747..a2bd4c66a 100644 --- a/src/client/commands/diagnose.rs +++ b/src/client/commands/diagnose.rs @@ -80,7 +80,7 @@ struct PackingDiagnosis { } /// Entry point for `torc workflows diagnose`. -pub fn diagnose_packing(config: &Configuration, workflow_id: Option, format: &str) { +pub(crate) fn diagnose_packing(config: &Configuration, workflow_id: Option, format: &str) { let user = get_env_user_name(); let workflow_id = match workflow_id { Some(id) => id, diff --git a/src/client/commands/hpc.rs b/src/client/commands/hpc.rs index dd100a56c..d0fad0fd2 100644 --- a/src/client/commands/hpc.rs +++ b/src/client/commands/hpc.rs @@ -16,7 +16,9 @@ use super::table_format::{display_csv, display_table_with_count}; /// Create an HPC profile registry with built-in profiles and user-defined profiles from config /// /// This is a public version for use by other modules (e.g., main.rs for submit command) -pub fn create_registry_with_config_public(hpc_config: &ClientHpcConfig) -> HpcProfileRegistry { +pub(crate) fn create_registry_with_config_public( + hpc_config: &ClientHpcConfig, +) -> HpcProfileRegistry { create_registry_with_config(hpc_config) } diff --git a/src/client/commands/jobs.rs b/src/client/commands/jobs.rs index 51f1f241f..3d6a385dd 100644 --- a/src/client/commands/jobs.rs +++ b/src/client/commands/jobs.rs @@ -1514,7 +1514,7 @@ pub fn create_jobs_from_file( } /// Get the current job count for a workflow -pub fn get_current_job_count( +fn get_current_job_count( config: &Configuration, workflow_id: i64, ) -> Result> { @@ -2166,7 +2166,7 @@ fn handle_reset_job_status( } /// Get existing job names to avoid duplicates -pub fn get_existing_job_names( +fn get_existing_job_names( config: &Configuration, workflow_id: i64, ) -> Result, Box> { diff --git a/src/client/commands/logs.rs b/src/client/commands/logs.rs index 76a31e6cd..a1e8f65e7 100644 --- a/src/client/commands/logs.rs +++ b/src/client/commands/logs.rs @@ -354,10 +354,10 @@ impl std::fmt::Display for ErrorSeverity { /// A detected error in a log file #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DetectedError { - pub file: String, - pub line_number: usize, + pub(crate) file: String, + pub(crate) line_number: usize, pub pattern_name: String, - pub severity: ErrorSeverity, + pub(crate) severity: ErrorSeverity, pub line_content: String, } @@ -365,9 +365,9 @@ pub struct DetectedError { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LogAnalysisResult { /// Workflow ID that was analyzed - pub workflow_id: Option, + workflow_id: Option, /// Number of log files parsed - pub files_parsed: usize, + pub(crate) files_parsed: usize, /// Total number of errors detected pub error_count: usize, /// Total number of warnings detected @@ -375,9 +375,9 @@ pub struct LogAnalysisResult { /// All detected errors pub errors: Vec, /// Errors grouped by file - pub errors_by_file: HashMap>, + pub(crate) errors_by_file: HashMap>, /// Error counts by pattern type - pub errors_by_type: HashMap, + pub(crate) errors_by_type: HashMap, } impl LogAnalysisResult { diff --git a/src/client/commands/orphan_detection.rs b/src/client/commands/orphan_detection.rs index cc395186f..888ff29be 100644 --- a/src/client/commands/orphan_detection.rs +++ b/src/client/commands/orphan_detection.rs @@ -28,36 +28,36 @@ use crate::models; /// - Negative, clearly distinguishing it from normal exit codes /// - Related to signal convention (128 is the base for signal exits) /// - Easy to identify in logs and results -pub const ORPHANED_JOB_RETURN_CODE: i64 = -128; +pub(crate) const ORPHANED_JOB_RETURN_CODE: i64 = -128; /// Result of orphan cleanup operation #[derive(Debug, Clone, Serialize)] pub struct OrphanCleanupResult { /// Number of jobs failed due to terminated Slurm allocations - pub slurm_jobs_failed: usize, + pub(crate) slurm_jobs_failed: usize, /// Number of pending Slurm allocations that were cleaned up - pub pending_allocations_cleaned: usize, + pub(crate) pending_allocations_cleaned: usize, /// Number of running jobs failed due to no active compute nodes - pub running_jobs_failed: usize, + pub(crate) running_jobs_failed: usize, /// Number of compute nodes deactivated because their Slurm allocation is gone pub compute_nodes_deactivated: usize, /// Details of each orphaned job that was failed #[serde(skip_serializing_if = "Vec::is_empty")] - pub failed_job_details: Vec, + pub(crate) failed_job_details: Vec, } /// Details about an orphaned job that was failed #[derive(Debug, Clone, Serialize)] pub struct OrphanedJobDetail { - pub job_id: i64, - pub job_name: String, - pub reason: String, - pub slurm_job_id: Option, + pub(crate) job_id: i64, + pub(crate) job_name: String, + pub(crate) reason: String, + pub(crate) slurm_job_id: Option, } impl OrphanCleanupResult { /// Returns true if any cleanup was performed - pub fn any_cleaned(&self) -> bool { + pub(crate) fn any_cleaned(&self) -> bool { self.slurm_jobs_failed > 0 || self.pending_allocations_cleaned > 0 || self.running_jobs_failed > 0 @@ -65,7 +65,7 @@ impl OrphanCleanupResult { } /// Total number of jobs that were failed - pub fn total_jobs_failed(&self) -> usize { + pub(crate) fn total_jobs_failed(&self) -> usize { self.slurm_jobs_failed + self.running_jobs_failed } } @@ -780,7 +780,7 @@ fn deactivate_orphaned_compute_nodes( /// itself). `slurm_job_id` is used only for logging context. /// /// Returns the number of compute nodes that were deactivated. -pub fn deactivate_compute_nodes_for_scheduled_node( +pub(crate) fn deactivate_compute_nodes_for_scheduled_node( config: &Configuration, workflow_id: i64, scheduled_compute_node_id: i64, diff --git a/src/client/commands/output.rs b/src/client/commands/output.rs index 1899fd34a..953cd30b3 100644 --- a/src/client/commands/output.rs +++ b/src/client/commands/output.rs @@ -19,7 +19,7 @@ use serde::Serialize; /// ```ignore /// print_json(&job, "job"); /// ``` -pub fn print_json(value: &T, type_name: &str) { +pub(crate) fn print_json(value: &T, type_name: &str) { match serde_json::to_string_pretty(value) { Ok(json) => println!("{}", json), Err(e) => { @@ -46,7 +46,7 @@ pub fn print_json(value: &T, type_name: &str) { /// print_json_wrapped(&jobs, "jobs"); /// // Outputs: {"items": [...]} /// ``` -pub fn print_json_wrapped(items: &[T], type_name: &str) { +pub(crate) fn print_json_wrapped(items: &[T], type_name: &str) { let output = serde_json::json!({ "items": items }); print_json(&output, type_name); } @@ -137,7 +137,7 @@ pub fn print_error_json_and_exit(message: &str, details: Option(format: &str, value: &T, type_name: &str) -> bool { +pub(crate) fn print_if_json(format: &str, value: &T, type_name: &str) -> bool { match format { "json" => { print_json(value, type_name); @@ -158,7 +158,11 @@ pub fn print_if_json(format: &str, value: &T, type_name: &str) -> /// /// # Returns /// `true` if JSON was printed, `false` if caller should handle table format -pub fn print_wrapped_if_json(format: &str, items: &[T], type_name: &str) -> bool { +pub(crate) fn print_wrapped_if_json( + format: &str, + items: &[T], + type_name: &str, +) -> bool { if format == "json" { print_json_wrapped(items, type_name); true diff --git a/src/client/commands/pagination/base.rs b/src/client/commands/pagination/base.rs index 551b1ac38..63033b100 100644 --- a/src/client/commands/pagination/base.rs +++ b/src/client/commands/pagination/base.rs @@ -40,9 +40,9 @@ pub trait PaginationParams { /// This wraps the API response with the essential pagination metadata. pub struct PaginatedResponse { /// The items in this page (None if empty) - pub items: Vec, + pub(crate) items: Vec, /// Whether there are more pages available - pub has_more: bool, + pub(crate) has_more: bool, } /// Trait for types that can be paginated. @@ -92,7 +92,7 @@ impl PaginatedIterator { /// * `config` - API configuration /// * `params` - Resource-specific parameters /// * `initial_limit` - Page size for each API call (default: MAX_RECORD_TRANSFER_COUNT) - pub fn new( + pub(crate) fn new( config: apis::configuration::Configuration, params: T::Params, initial_limit: Option, @@ -164,7 +164,7 @@ impl Iterator for PaginatedIterator { /// Helper function to collect all paginated results into a Vec. /// /// This is a convenience function for when you need all results at once. -pub fn paginate( +fn paginate( config: &apis::configuration::Configuration, params: T::Params, ) -> Result, apis::Error> { diff --git a/src/client/commands/pagination/compute_nodes.rs b/src/client/commands/pagination/compute_nodes.rs index 2ad58bfa1..77d68851f 100644 --- a/src/client/commands/pagination/compute_nodes.rs +++ b/src/client/commands/pagination/compute_nodes.rs @@ -13,59 +13,59 @@ use crate::models::ComputeNodeModel; #[derive(Debug, Clone, Default)] pub struct ComputeNodeListParams { /// Workflow ID to list compute nodes from - pub workflow_id: i64, + workflow_id: i64, /// Pagination offset - pub offset: i64, + offset: i64, /// Maximum number of records to return - pub limit: Option, + limit: Option, /// Field to sort by - pub sort_by: Option, + sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + reverse_sort: Option, /// Filter by hostname - pub hostname: Option, + hostname: Option, /// Filter by active status - pub is_active: Option, + is_active: Option, /// Filter by scheduled compute node ID - pub scheduled_compute_node_id: Option, + scheduled_compute_node_id: Option, } impl ComputeNodeListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + pub(crate) fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + pub(crate) fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_hostname(mut self, hostname: String) -> Self { + fn with_hostname(mut self, hostname: String) -> Self { self.hostname = Some(hostname); self } - pub fn with_is_active(mut self, is_active: bool) -> Self { + pub(crate) fn with_is_active(mut self, is_active: bool) -> Self { self.is_active = Some(is_active); self } - pub fn with_scheduled_compute_node_id(mut self, id: i64) -> Self { + pub(crate) fn with_scheduled_compute_node_id(mut self, id: i64) -> Self { self.scheduled_compute_node_id = Some(id); self } @@ -122,7 +122,7 @@ impl Paginatable for ComputeNodeModel { } /// Type alias for the compute nodes iterator -pub type ComputeNodesIterator = PaginatedIterator; +type ComputeNodesIterator = PaginatedIterator; /// Create a lazy iterator for compute nodes that fetches pages on-demand. /// @@ -133,7 +133,7 @@ pub type ComputeNodesIterator = PaginatedIterator; /// /// # Returns /// An iterator that yields `Result` items -pub fn iter_compute_nodes( +fn iter_compute_nodes( config: &apis::configuration::Configuration, workflow_id: i64, params: ComputeNodeListParams, @@ -153,7 +153,7 @@ pub fn iter_compute_nodes( /// # Returns /// `Result, Error>` containing all compute nodes or an error #[allow(clippy::result_large_err)] -pub fn paginate_compute_nodes( +pub(crate) fn paginate_compute_nodes( config: &apis::configuration::Configuration, workflow_id: i64, params: ComputeNodeListParams, diff --git a/src/client/commands/pagination/events.rs b/src/client/commands/pagination/events.rs index 5876fa932..cf76328dd 100644 --- a/src/client/commands/pagination/events.rs +++ b/src/client/commands/pagination/events.rs @@ -13,45 +13,45 @@ use crate::models::EventModel; #[derive(Debug, Clone, Default)] pub struct EventListParams { /// Workflow ID to list events from - pub workflow_id: i64, + pub(crate) workflow_id: i64, /// Pagination offset - pub offset: i64, + pub(crate) offset: i64, /// Maximum number of events to return - pub limit: Option, + pub(crate) limit: Option, /// Field to sort by - pub sort_by: Option, + pub(crate) sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + pub(crate) reverse_sort: Option, /// Filter by category - pub category: Option, + pub(crate) category: Option, } impl EventListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + pub(crate) fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + pub(crate) fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_category(mut self, category: String) -> Self { + pub(crate) fn with_category(mut self, category: String) -> Self { self.category = Some(category); self } @@ -107,7 +107,7 @@ impl Paginatable for EventModel { } /// Type alias for the events iterator -pub type EventsIterator = PaginatedIterator; +type EventsIterator = PaginatedIterator; /// Create a lazy iterator for events that fetches pages on-demand. /// @@ -118,7 +118,7 @@ pub type EventsIterator = PaginatedIterator; /// /// # Returns /// An iterator that yields `Result` items -pub fn iter_events( +fn iter_events( config: &apis::configuration::Configuration, workflow_id: i64, params: EventListParams, @@ -138,7 +138,7 @@ pub fn iter_events( /// # Returns /// `Result, Error>` containing all events or an error #[allow(clippy::result_large_err)] -pub fn paginate_events( +pub(crate) fn paginate_events( config: &apis::configuration::Configuration, workflow_id: i64, params: EventListParams, diff --git a/src/client/commands/pagination/files.rs b/src/client/commands/pagination/files.rs index 6c7d319a3..0e0fbf612 100644 --- a/src/client/commands/pagination/files.rs +++ b/src/client/commands/pagination/files.rs @@ -13,56 +13,56 @@ use crate::models::FileModel; #[derive(Debug, Clone, Default)] pub struct FileListParams { /// Workflow ID to list files from - pub workflow_id: i64, + pub(crate) workflow_id: i64, /// Filter by job ID that produced the files - pub produced_by_job_id: Option, + pub(crate) produced_by_job_id: Option, /// Pagination offset - pub offset: i64, + pub(crate) offset: i64, /// Maximum number of files to return - pub limit: Option, + pub(crate) limit: Option, /// Field to sort by - pub sort_by: Option, + pub(crate) sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + pub(crate) reverse_sort: Option, /// Filter by file name - pub name: Option, + pub(crate) name: Option, /// Filter by file path - pub path: Option, + pub(crate) path: Option, /// Filter by output status - pub is_output: Option, + pub(crate) is_output: Option, } impl FileListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_produced_by_job_id(mut self, job_id: i64) -> Self { + pub(crate) fn with_produced_by_job_id(mut self, job_id: i64) -> Self { self.produced_by_job_id = Some(job_id); self } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + pub(crate) fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + pub(crate) fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_is_output(mut self, is_output: bool) -> Self { + pub(crate) fn with_is_output(mut self, is_output: bool) -> Self { self.is_output = Some(is_output); self } @@ -120,7 +120,7 @@ impl Paginatable for FileModel { } /// Type alias for the files iterator -pub type FilesIterator = PaginatedIterator; +pub(crate) type FilesIterator = PaginatedIterator; /// Create a lazy iterator for files that fetches pages on-demand. /// @@ -133,7 +133,7 @@ pub type FilesIterator = PaginatedIterator; /// /// # Returns /// An iterator that yields `Result` items -pub fn iter_files( +pub(crate) fn iter_files( config: &apis::configuration::Configuration, workflow_id: i64, params: FileListParams, @@ -153,7 +153,7 @@ pub fn iter_files( /// # Returns /// `Result, Error>` containing all files or an error #[allow(clippy::result_large_err)] -pub fn paginate_files( +pub(crate) fn paginate_files( config: &apis::configuration::Configuration, workflow_id: i64, params: FileListParams, diff --git a/src/client/commands/pagination/job_dependencies.rs b/src/client/commands/pagination/job_dependencies.rs index 01b24cb3a..efcc4fa7d 100644 --- a/src/client/commands/pagination/job_dependencies.rs +++ b/src/client/commands/pagination/job_dependencies.rs @@ -13,28 +13,28 @@ use crate::models::JobDependencyModel; #[derive(Debug, Clone, Default)] pub struct JobDependencyListParams { /// Workflow ID to list dependencies from - pub workflow_id: i64, + workflow_id: i64, /// Pagination offset - pub offset: i64, + offset: i64, /// Maximum number of records to return - pub limit: Option, + limit: Option, /// Field to sort by - pub sort_by: Option, + sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + reverse_sort: Option, } impl JobDependencyListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } @@ -88,10 +88,10 @@ impl Paginatable for JobDependencyModel { } /// Type alias for the job dependencies iterator -pub type JobDependenciesIterator = PaginatedIterator; +type JobDependenciesIterator = PaginatedIterator; /// Create a lazy iterator for job-to-job dependencies that fetches pages on-demand. -pub fn iter_job_dependencies( +fn iter_job_dependencies( config: &apis::configuration::Configuration, workflow_id: i64, params: JobDependencyListParams, @@ -103,7 +103,7 @@ pub fn iter_job_dependencies( /// Collect all job-to-job dependencies into a vector using lazy iteration internally. #[allow(clippy::result_large_err)] -pub fn paginate_job_dependencies( +pub(crate) fn paginate_job_dependencies( config: &apis::configuration::Configuration, workflow_id: i64, params: JobDependencyListParams, diff --git a/src/client/commands/pagination/job_file_relationships.rs b/src/client/commands/pagination/job_file_relationships.rs index dd9c98510..d46c8f1b5 100644 --- a/src/client/commands/pagination/job_file_relationships.rs +++ b/src/client/commands/pagination/job_file_relationships.rs @@ -13,28 +13,28 @@ use crate::models::JobFileRelationshipModel; #[derive(Debug, Clone, Default)] pub struct JobFileRelationshipListParams { /// Workflow ID to list relationships from - pub workflow_id: i64, + workflow_id: i64, /// Pagination offset - pub offset: i64, + offset: i64, /// Maximum number of records to return - pub limit: Option, + limit: Option, /// Field to sort by - pub sort_by: Option, + sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + reverse_sort: Option, } impl JobFileRelationshipListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } @@ -88,10 +88,10 @@ impl Paginatable for JobFileRelationshipModel { } /// Type alias for the job-file relationships iterator -pub type JobFileRelationshipsIterator = PaginatedIterator; +type JobFileRelationshipsIterator = PaginatedIterator; /// Create a lazy iterator for job-file relationships that fetches pages on-demand. -pub fn iter_job_file_relationships( +fn iter_job_file_relationships( config: &apis::configuration::Configuration, workflow_id: i64, params: JobFileRelationshipListParams, @@ -103,7 +103,7 @@ pub fn iter_job_file_relationships( /// Collect all job-file relationships into a vector using lazy iteration internally. #[allow(clippy::result_large_err)] -pub fn paginate_job_file_relationships( +pub(crate) fn paginate_job_file_relationships( config: &apis::configuration::Configuration, workflow_id: i64, params: JobFileRelationshipListParams, diff --git a/src/client/commands/pagination/job_user_data_relationships.rs b/src/client/commands/pagination/job_user_data_relationships.rs index f44dedb15..72cb079d8 100644 --- a/src/client/commands/pagination/job_user_data_relationships.rs +++ b/src/client/commands/pagination/job_user_data_relationships.rs @@ -13,28 +13,28 @@ use crate::models::JobUserDataRelationshipModel; #[derive(Debug, Clone, Default)] pub struct JobUserDataRelationshipListParams { /// Workflow ID to list relationships from - pub workflow_id: i64, + workflow_id: i64, /// Pagination offset - pub offset: i64, + offset: i64, /// Maximum number of records to return - pub limit: Option, + limit: Option, /// Field to sort by - pub sort_by: Option, + sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + reverse_sort: Option, } impl JobUserDataRelationshipListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } @@ -88,10 +88,10 @@ impl Paginatable for JobUserDataRelationshipModel { } /// Type alias for the job-user_data relationships iterator -pub type JobUserDataRelationshipsIterator = PaginatedIterator; +type JobUserDataRelationshipsIterator = PaginatedIterator; /// Create a lazy iterator for job-user_data relationships that fetches pages on-demand. -pub fn iter_job_user_data_relationships( +fn iter_job_user_data_relationships( config: &apis::configuration::Configuration, workflow_id: i64, params: JobUserDataRelationshipListParams, @@ -103,7 +103,7 @@ pub fn iter_job_user_data_relationships( /// Collect all job-user_data relationships into a vector using lazy iteration internally. #[allow(clippy::result_large_err)] -pub fn paginate_job_user_data_relationships( +pub(crate) fn paginate_job_user_data_relationships( config: &apis::configuration::Configuration, workflow_id: i64, params: JobUserDataRelationshipListParams, diff --git a/src/client/commands/pagination/jobs.rs b/src/client/commands/pagination/jobs.rs index a0a47a720..53f3e45db 100644 --- a/src/client/commands/pagination/jobs.rs +++ b/src/client/commands/pagination/jobs.rs @@ -13,82 +13,82 @@ use crate::models::{JobModel, JobStatus}; #[derive(Debug, Clone, Default)] pub struct JobListParams { /// Workflow ID to list jobs from - pub workflow_id: i64, + pub(crate) workflow_id: i64, /// Filter by job status - pub status: Option, + pub(crate) status: Option, /// Filter by file ID that the job needs - pub needs_file_id: Option, + pub(crate) needs_file_id: Option, /// Filter by upstream job ID - pub upstream_job_id: Option, + pub(crate) upstream_job_id: Option, /// Pagination offset - pub offset: i64, + pub(crate) offset: i64, /// Maximum number of jobs to return - pub limit: Option, + pub(crate) limit: Option, /// Field to sort by - pub sort_by: Option, + pub(crate) sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + pub(crate) reverse_sort: Option, /// Include job relationships in response - pub include_relationships: Option, + pub(crate) include_relationships: Option, /// Filter by active compute node ID - pub active_compute_node_id: Option, + pub(crate) active_compute_node_id: Option, /// Filter by job provenance. `Some(true)` returns only jobs with /// `origin IS NOT NULL` (failure-handler retries and `spawn_jobs` /// children); `Some(false)` returns only originally-declared jobs. - pub origin_is_set: Option, + pub(crate) origin_is_set: Option, } impl JobListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_status(mut self, status: JobStatus) -> Self { + pub(crate) fn with_status(mut self, status: JobStatus) -> Self { self.status = Some(status); self } - pub fn with_needs_file_id(mut self, file_id: i64) -> Self { + pub(crate) fn with_needs_file_id(mut self, file_id: i64) -> Self { self.needs_file_id = Some(file_id); self } - pub fn with_upstream_job_id(mut self, job_id: i64) -> Self { + pub(crate) fn with_upstream_job_id(mut self, job_id: i64) -> Self { self.upstream_job_id = Some(job_id); self } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + pub(crate) fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + pub(crate) fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_include_relationships(mut self, include: bool) -> Self { + pub(crate) fn with_include_relationships(mut self, include: bool) -> Self { self.include_relationships = Some(include); self } - pub fn with_active_compute_node_id(mut self, id: i64) -> Self { + pub(crate) fn with_active_compute_node_id(mut self, id: i64) -> Self { self.active_compute_node_id = Some(id); self } - pub fn with_origin_is_set(mut self, origin_is_set: bool) -> Self { + fn with_origin_is_set(mut self, origin_is_set: bool) -> Self { self.origin_is_set = Some(origin_is_set); self } @@ -150,7 +150,7 @@ impl Paginatable for JobModel { } /// Type alias for the jobs iterator -pub type JobsIterator = PaginatedIterator; +pub(crate) type JobsIterator = PaginatedIterator; /// Create a lazy iterator for jobs that fetches pages on-demand. /// @@ -163,7 +163,7 @@ pub type JobsIterator = PaginatedIterator; /// /// # Returns /// An iterator that yields `Result` items -pub fn iter_jobs( +pub(crate) fn iter_jobs( config: &apis::configuration::Configuration, workflow_id: i64, params: JobListParams, @@ -183,7 +183,7 @@ pub fn iter_jobs( /// # Returns /// `Result, Error>` containing all jobs or an error #[allow(clippy::result_large_err)] -pub fn paginate_jobs( +pub(crate) fn paginate_jobs( config: &apis::configuration::Configuration, workflow_id: i64, params: JobListParams, diff --git a/src/client/commands/pagination/resource_requirements.rs b/src/client/commands/pagination/resource_requirements.rs index 2cf1b6eb8..b72a581c1 100644 --- a/src/client/commands/pagination/resource_requirements.rs +++ b/src/client/commands/pagination/resource_requirements.rs @@ -14,87 +14,87 @@ use crate::time_utils::duration_string_to_seconds; #[derive(Debug, Clone, Default)] pub struct ResourceRequirementsListParams { /// Workflow ID to list resource requirements from - pub workflow_id: i64, + pub(crate) workflow_id: i64, /// Filter by job ID - pub job_id: Option, + pub(crate) job_id: Option, /// Pagination offset - pub offset: i64, + pub(crate) offset: i64, /// Maximum number of records to return - pub limit: Option, + pub(crate) limit: Option, /// Field to sort by - pub sort_by: Option, + pub(crate) sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + pub(crate) reverse_sort: Option, /// Filter by name - pub name: Option, + pub(crate) name: Option, /// Filter by memory - pub memory: Option, + pub(crate) memory: Option, /// Filter by number of CPUs - pub num_cpus: Option, + pub(crate) num_cpus: Option, /// Filter by number of GPUs - pub num_gpus: Option, + pub(crate) num_gpus: Option, /// Filter by number of nodes - pub num_nodes: Option, + pub(crate) num_nodes: Option, /// Filter by runtime - pub runtime: Option, + pub(crate) runtime: Option, } impl ResourceRequirementsListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_job_id(mut self, job_id: i64) -> Self { + fn with_job_id(mut self, job_id: i64) -> Self { self.job_id = Some(job_id); self } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + pub(crate) fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + pub(crate) fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_name(mut self, name: String) -> Self { + fn with_name(mut self, name: String) -> Self { self.name = Some(name); self } - pub fn with_memory(mut self, memory: String) -> Self { + fn with_memory(mut self, memory: String) -> Self { self.memory = Some(memory); self } - pub fn with_num_cpus(mut self, num_cpus: i64) -> Self { + fn with_num_cpus(mut self, num_cpus: i64) -> Self { self.num_cpus = Some(num_cpus); self } - pub fn with_num_gpus(mut self, num_gpus: i64) -> Self { + fn with_num_gpus(mut self, num_gpus: i64) -> Self { self.num_gpus = Some(num_gpus); self } - pub fn with_num_nodes(mut self, num_nodes: i64) -> Self { + fn with_num_nodes(mut self, num_nodes: i64) -> Self { self.num_nodes = Some(num_nodes); self } - pub fn with_runtime(mut self, runtime: String) -> Self { + fn with_runtime(mut self, runtime: String) -> Self { self.runtime = Some(runtime); self } @@ -162,7 +162,7 @@ impl Paginatable for ResourceRequirementsModel { } /// Type alias for the resource requirements iterator -pub type ResourceRequirementsIterator = PaginatedIterator; +type ResourceRequirementsIterator = PaginatedIterator; /// Create a lazy iterator for resource requirements that fetches pages on-demand. /// @@ -173,7 +173,7 @@ pub type ResourceRequirementsIterator = PaginatedIterator` items -pub fn iter_resource_requirements( +fn iter_resource_requirements( config: &apis::configuration::Configuration, workflow_id: i64, params: ResourceRequirementsListParams, @@ -193,7 +193,7 @@ pub fn iter_resource_requirements( /// # Returns /// `Result, Error>` containing all resource requirements or an error #[allow(clippy::result_large_err)] -pub fn paginate_resource_requirements( +pub(crate) fn paginate_resource_requirements( config: &apis::configuration::Configuration, workflow_id: i64, params: ResourceRequirementsListParams, diff --git a/src/client/commands/pagination/results.rs b/src/client/commands/pagination/results.rs index bb1318be3..4b1f6d5a8 100644 --- a/src/client/commands/pagination/results.rs +++ b/src/client/commands/pagination/results.rs @@ -13,80 +13,80 @@ use crate::models::{JobStatus, ResultModel}; #[derive(Debug, Clone, Default)] pub struct ResultListParams { /// Workflow ID to list results from - pub workflow_id: i64, + pub(crate) workflow_id: i64, /// Filter by job ID - pub job_id: Option, + pub(crate) job_id: Option, /// Filter by run ID - pub run_id: Option, + pub(crate) run_id: Option, /// Pagination offset - pub offset: i64, + pub(crate) offset: i64, /// Maximum number of records to return - pub limit: Option, + pub(crate) limit: Option, /// Field to sort by - pub sort_by: Option, + pub(crate) sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + pub(crate) reverse_sort: Option, /// Filter by return code - pub return_code: Option, + pub(crate) return_code: Option, /// Filter by status - pub status: Option, + pub(crate) status: Option, /// Include all runs - pub all_runs: Option, + pub(crate) all_runs: Option, /// Filter by compute node ID - pub compute_node_id: Option, + pub(crate) compute_node_id: Option, } impl ResultListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_job_id(mut self, job_id: i64) -> Self { + pub(crate) fn with_job_id(mut self, job_id: i64) -> Self { self.job_id = Some(job_id); self } - pub fn with_run_id(mut self, run_id: i64) -> Self { + pub(crate) fn with_run_id(mut self, run_id: i64) -> Self { self.run_id = Some(run_id); self } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + pub(crate) fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + pub(crate) fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_return_code(mut self, return_code: i64) -> Self { + pub(crate) fn with_return_code(mut self, return_code: i64) -> Self { self.return_code = Some(return_code); self } - pub fn with_status(mut self, status: JobStatus) -> Self { + pub(crate) fn with_status(mut self, status: JobStatus) -> Self { self.status = Some(status); self } - pub fn with_all_runs(mut self, all_runs: bool) -> Self { + pub(crate) fn with_all_runs(mut self, all_runs: bool) -> Self { self.all_runs = Some(all_runs); self } - pub fn with_compute_node_id(mut self, compute_node_id: i64) -> Self { + pub(crate) fn with_compute_node_id(mut self, compute_node_id: i64) -> Self { self.compute_node_id = Some(compute_node_id); self } @@ -146,7 +146,7 @@ impl Paginatable for ResultModel { } /// Type alias for the results iterator -pub type ResultsIterator = PaginatedIterator; +type ResultsIterator = PaginatedIterator; /// Create a lazy iterator for results that fetches pages on-demand. /// @@ -157,7 +157,7 @@ pub type ResultsIterator = PaginatedIterator; /// /// # Returns /// An iterator that yields `Result` items -pub fn iter_results( +fn iter_results( config: &apis::configuration::Configuration, workflow_id: i64, params: ResultListParams, @@ -177,7 +177,7 @@ pub fn iter_results( /// # Returns /// `Result, Error>` containing all results or an error #[allow(clippy::result_large_err)] -pub fn paginate_results( +pub(crate) fn paginate_results( config: &apis::configuration::Configuration, workflow_id: i64, params: ResultListParams, diff --git a/src/client/commands/pagination/ro_crate_entities.rs b/src/client/commands/pagination/ro_crate_entities.rs index 82459b255..fe441f269 100644 --- a/src/client/commands/pagination/ro_crate_entities.rs +++ b/src/client/commands/pagination/ro_crate_entities.rs @@ -13,24 +13,24 @@ use crate::models::RoCrateEntityModel; #[derive(Debug, Clone, Default)] pub struct RoCrateEntityListParams { /// Workflow ID to list RO-Crate entities from - pub workflow_id: i64, + workflow_id: i64, /// Pagination offset - pub offset: i64, + offset: i64, /// Maximum number of entities to return - pub limit: Option, + limit: Option, } impl RoCrateEntityListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } @@ -86,7 +86,7 @@ impl Paginatable for RoCrateEntityModel { } /// Type alias for the RO-Crate entities iterator -pub type RoCrateEntitiesIterator = PaginatedIterator; +type RoCrateEntitiesIterator = PaginatedIterator; /// Create a lazy iterator for RO-Crate entities that fetches pages on-demand. /// @@ -99,7 +99,7 @@ pub type RoCrateEntitiesIterator = PaginatedIterator; /// /// # Returns /// An iterator that yields `Result` items -pub fn iter_ro_crate_entities( +fn iter_ro_crate_entities( config: &apis::configuration::Configuration, workflow_id: i64, params: RoCrateEntityListParams, @@ -119,7 +119,7 @@ pub fn iter_ro_crate_entities( /// # Returns /// `Result, Error>` containing all entities or an error #[allow(clippy::result_large_err)] -pub fn paginate_ro_crate_entities( +pub(crate) fn paginate_ro_crate_entities( config: &apis::configuration::Configuration, workflow_id: i64, params: RoCrateEntityListParams, diff --git a/src/client/commands/pagination/scheduled_compute_nodes.rs b/src/client/commands/pagination/scheduled_compute_nodes.rs index 654f25ca1..94afa8935 100644 --- a/src/client/commands/pagination/scheduled_compute_nodes.rs +++ b/src/client/commands/pagination/scheduled_compute_nodes.rs @@ -13,59 +13,59 @@ use crate::models::ScheduledComputeNodesModel; #[derive(Debug, Clone, Default)] pub struct ScheduledComputeNodeListParams { /// Workflow ID to list scheduled compute nodes from - pub workflow_id: i64, + workflow_id: i64, /// Pagination offset - pub offset: i64, + offset: i64, /// Maximum number of records to return - pub limit: Option, + limit: Option, /// Field to sort by - pub sort_by: Option, + sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + reverse_sort: Option, /// Filter by scheduler ID - pub scheduler_id: Option, + scheduler_id: Option, /// Filter by scheduler config ID - pub scheduler_config_id: Option, + scheduler_config_id: Option, /// Filter by status - pub status: Option, + status: Option, } impl ScheduledComputeNodeListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_offset(mut self, offset: i64) -> Self { + fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_scheduler_id(mut self, scheduler_id: String) -> Self { + fn with_scheduler_id(mut self, scheduler_id: String) -> Self { self.scheduler_id = Some(scheduler_id); self } - pub fn with_scheduler_config_id(mut self, scheduler_config_id: String) -> Self { + fn with_scheduler_config_id(mut self, scheduler_config_id: String) -> Self { self.scheduler_config_id = Some(scheduler_config_id); self } - pub fn with_status(mut self, status: String) -> Self { + pub(crate) fn with_status(mut self, status: String) -> Self { self.status = Some(status); self } @@ -122,7 +122,7 @@ impl Paginatable for ScheduledComputeNodesModel { } /// Type alias for the scheduled compute nodes iterator -pub type ScheduledComputeNodesIterator = PaginatedIterator; +type ScheduledComputeNodesIterator = PaginatedIterator; /// Create a lazy iterator for scheduled compute nodes that fetches pages on-demand. /// @@ -133,7 +133,7 @@ pub type ScheduledComputeNodesIterator = PaginatedIterator` items -pub fn iter_scheduled_compute_nodes( +fn iter_scheduled_compute_nodes( config: &apis::configuration::Configuration, workflow_id: i64, params: ScheduledComputeNodeListParams, @@ -153,7 +153,7 @@ pub fn iter_scheduled_compute_nodes( /// # Returns /// `Result, Error>` containing all scheduled compute nodes or an error #[allow(clippy::result_large_err)] -pub fn paginate_scheduled_compute_nodes( +pub(crate) fn paginate_scheduled_compute_nodes( config: &apis::configuration::Configuration, workflow_id: i64, params: ScheduledComputeNodeListParams, diff --git a/src/client/commands/pagination/slurm_schedulers.rs b/src/client/commands/pagination/slurm_schedulers.rs index d73f797d6..08ca9af44 100644 --- a/src/client/commands/pagination/slurm_schedulers.rs +++ b/src/client/commands/pagination/slurm_schedulers.rs @@ -13,71 +13,71 @@ use crate::models::SlurmSchedulerModel; #[derive(Debug, Clone, Default)] pub struct SlurmSchedulersListParams { /// Workflow ID to list slurm schedulers from - pub workflow_id: i64, + pub(crate) workflow_id: i64, /// Pagination offset - pub offset: i64, + pub(crate) offset: i64, /// Maximum number of records to return - pub limit: Option, + pub(crate) limit: Option, /// Field to sort by - pub sort_by: Option, + pub(crate) sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + pub(crate) reverse_sort: Option, /// Filter by name - pub name: Option, + pub(crate) name: Option, /// Filter by account - pub account: Option, + pub(crate) account: Option, /// Filter by gres - pub gres: Option, + pub(crate) gres: Option, /// Filter by mem - pub mem: Option, + pub(crate) mem: Option, /// Filter by nodes - pub nodes: Option, + pub(crate) nodes: Option, /// Filter by partition - pub partition: Option, + pub(crate) partition: Option, /// Filter by qos - pub qos: Option, + pub(crate) qos: Option, /// Filter by tmp - pub tmp: Option, + pub(crate) tmp: Option, /// Filter by walltime - pub walltime: Option, + pub(crate) walltime: Option, } impl SlurmSchedulersListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_name(mut self, name: String) -> Self { + fn with_name(mut self, name: String) -> Self { self.name = Some(name); self } - pub fn with_account(mut self, account: String) -> Self { + fn with_account(mut self, account: String) -> Self { self.account = Some(account); self } - pub fn with_partition(mut self, partition: String) -> Self { + fn with_partition(mut self, partition: String) -> Self { self.partition = Some(partition); self } @@ -131,7 +131,7 @@ impl Paginatable for SlurmSchedulerModel { } /// Type alias for the slurm schedulers iterator -pub type SlurmSchedulersIterator = PaginatedIterator; +type SlurmSchedulersIterator = PaginatedIterator; /// Create a lazy iterator for slurm schedulers that fetches pages on-demand. /// @@ -142,7 +142,7 @@ pub type SlurmSchedulersIterator = PaginatedIterator; /// /// # Returns /// An iterator that yields `Result` items -pub fn iter_slurm_schedulers( +fn iter_slurm_schedulers( config: &apis::configuration::Configuration, workflow_id: i64, params: SlurmSchedulersListParams, @@ -162,7 +162,7 @@ pub fn iter_slurm_schedulers( /// # Returns /// `Result, Error>` containing all slurm schedulers or an error #[allow(clippy::result_large_err)] -pub fn paginate_slurm_schedulers( +pub(crate) fn paginate_slurm_schedulers( config: &apis::configuration::Configuration, workflow_id: i64, params: SlurmSchedulersListParams, diff --git a/src/client/commands/pagination/user_data.rs b/src/client/commands/pagination/user_data.rs index 1efa934ed..883ff3f1e 100644 --- a/src/client/commands/pagination/user_data.rs +++ b/src/client/commands/pagination/user_data.rs @@ -13,66 +13,66 @@ use crate::models::UserDataModel; #[derive(Debug, Clone, Default)] pub struct UserDataListParams { /// Workflow ID to list user data from - pub workflow_id: i64, + pub(crate) workflow_id: i64, /// Filter by consumer job ID - pub consumer_job_id: Option, + pub(crate) consumer_job_id: Option, /// Filter by producer job ID - pub producer_job_id: Option, + pub(crate) producer_job_id: Option, /// Pagination offset - pub offset: i64, + pub(crate) offset: i64, /// Maximum number of records to return - pub limit: Option, + pub(crate) limit: Option, /// Field to sort by - pub sort_by: Option, + pub(crate) sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + pub(crate) reverse_sort: Option, /// Filter by name - pub name: Option, + pub(crate) name: Option, /// Filter by ephemeral status - pub is_ephemeral: Option, + pub(crate) is_ephemeral: Option, } impl UserDataListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_consumer_job_id(mut self, job_id: i64) -> Self { + pub(crate) fn with_consumer_job_id(mut self, job_id: i64) -> Self { self.consumer_job_id = Some(job_id); self } - pub fn with_producer_job_id(mut self, job_id: i64) -> Self { + pub(crate) fn with_producer_job_id(mut self, job_id: i64) -> Self { self.producer_job_id = Some(job_id); self } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + pub(crate) fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + pub(crate) fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_name(mut self, name: String) -> Self { + pub(crate) fn with_name(mut self, name: String) -> Self { self.name = Some(name); self } - pub fn with_is_ephemeral(mut self, is_ephemeral: bool) -> Self { + pub(crate) fn with_is_ephemeral(mut self, is_ephemeral: bool) -> Self { self.is_ephemeral = Some(is_ephemeral); self } @@ -130,7 +130,7 @@ impl Paginatable for UserDataModel { } /// Type alias for the user data iterator -pub type UserDataIterator = PaginatedIterator; +type UserDataIterator = PaginatedIterator; /// Create a lazy iterator for user data that fetches pages on-demand. /// @@ -141,7 +141,7 @@ pub type UserDataIterator = PaginatedIterator; /// /// # Returns /// An iterator that yields `Result` items -pub fn iter_user_data( +fn iter_user_data( config: &apis::configuration::Configuration, workflow_id: i64, params: UserDataListParams, @@ -161,7 +161,7 @@ pub fn iter_user_data( /// # Returns /// `Result, Error>` containing all user data or an error #[allow(clippy::result_large_err)] -pub fn paginate_user_data( +pub(crate) fn paginate_user_data( config: &apis::configuration::Configuration, workflow_id: i64, params: UserDataListParams, diff --git a/src/client/commands/pagination/workflows.rs b/src/client/commands/pagination/workflows.rs index 2b05fef6c..201188212 100644 --- a/src/client/commands/pagination/workflows.rs +++ b/src/client/commands/pagination/workflows.rs @@ -13,71 +13,71 @@ use crate::models::WorkflowModel; #[derive(Debug, Clone, Default)] pub struct WorkflowListParams { /// Pagination offset - pub offset: i64, + offset: i64, /// Maximum number of records to return - pub limit: Option, + limit: Option, /// Field to sort by - pub sort_by: Option, + sort_by: Option, /// Reverse sort order - pub reverse_sort: Option, + reverse_sort: Option, /// Filter by name - pub name: Option, + name: Option, /// Filter by user - pub user: Option, + user: Option, /// Filter by description - pub description: Option, + description: Option, /// Filter by archived status - pub is_archived: Option, + is_archived: Option, /// Filter to workflows shared with this access group (by group name) - pub access_group: Option, + access_group: Option, } impl WorkflowListParams { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } - pub fn with_offset(mut self, offset: i64) -> Self { + pub(crate) fn with_offset(mut self, offset: i64) -> Self { self.offset = offset; self } - pub fn with_limit(mut self, limit: i64) -> Self { + pub(crate) fn with_limit(mut self, limit: i64) -> Self { self.limit = Some(limit); self } - pub fn with_sort_by(mut self, sort_by: String) -> Self { + pub(crate) fn with_sort_by(mut self, sort_by: String) -> Self { self.sort_by = Some(sort_by); self } - pub fn with_reverse_sort(mut self, reverse: bool) -> Self { + pub(crate) fn with_reverse_sort(mut self, reverse: bool) -> Self { self.reverse_sort = Some(reverse); self } - pub fn with_name(mut self, name: String) -> Self { + fn with_name(mut self, name: String) -> Self { self.name = Some(name); self } - pub fn with_user(mut self, user: String) -> Self { + pub(crate) fn with_user(mut self, user: String) -> Self { self.user = Some(user); self } - pub fn with_description(mut self, description: String) -> Self { + fn with_description(mut self, description: String) -> Self { self.description = Some(description); self } - pub fn with_is_archived(mut self, is_archived: bool) -> Self { + pub(crate) fn with_is_archived(mut self, is_archived: bool) -> Self { self.is_archived = Some(is_archived); self } - pub fn with_access_group(mut self, access_group: String) -> Self { + pub(crate) fn with_access_group(mut self, access_group: String) -> Self { self.access_group = Some(access_group); self } @@ -135,7 +135,7 @@ impl Paginatable for WorkflowModel { } /// Type alias for the workflows iterator -pub type WorkflowsIterator = PaginatedIterator; +type WorkflowsIterator = PaginatedIterator; /// Create a lazy iterator for workflows that fetches pages on-demand. /// @@ -145,7 +145,7 @@ pub type WorkflowsIterator = PaginatedIterator; /// /// # Returns /// An iterator that yields `Result` items -pub fn iter_workflows( +fn iter_workflows( config: &apis::configuration::Configuration, params: WorkflowListParams, ) -> WorkflowsIterator { @@ -160,7 +160,7 @@ pub fn iter_workflows( /// /// # Returns /// `Result, Error>` containing all workflows or an error -pub fn paginate_workflows( +pub(crate) fn paginate_workflows( config: &apis::configuration::Configuration, params: WorkflowListParams, ) -> Result, apis::Error> { diff --git a/src/client/commands/recover.rs b/src/client/commands/recover.rs index 993b5de2f..0caa50288 100644 --- a/src/client/commands/recover.rs +++ b/src/client/commands/recover.rs @@ -104,11 +104,11 @@ pub struct RecoveryResult { pub jobs_to_retry: Vec, /// Detailed resource adjustments (for JSON output) #[serde(skip_serializing_if = "Vec::is_empty")] - pub adjustments: Vec, + adjustments: Vec, /// Slurm scheduler dry-run result (only in dry-run mode) /// Memory values are updated to reflect the adjusted values from recovery heuristics. #[serde(skip_serializing_if = "Option::is_none")] - pub slurm_dry_run: Option, + slurm_dry_run: Option, } /// Full recovery report for JSON output @@ -127,9 +127,9 @@ pub struct RecoveryReport { /// Information about Slurm logs for a job #[derive(Debug)] pub struct SlurmLogInfo { - pub slurm_job_id: Option, - pub slurm_stdout: Option, - pub slurm_stderr: Option, + slurm_job_id: Option, + slurm_stdout: Option, + slurm_stderr: Option, } /// Whether unknown-cause failures should be retried. @@ -140,7 +140,7 @@ pub struct SlurmLogInfo { /// effects) and recovery would then abort with "no auto-recoverable jobs" because the /// unknown jobs were never added to the retry set. Both `torc recover` and /// `torc watch --recover` flow through here, so the rule is applied identically. -pub(crate) fn effective_retry_unknown(retry_unknown: bool, recovery_hook: Option<&str>) -> bool { +fn effective_retry_unknown(retry_unknown: bool, recovery_hook: Option<&str>) -> bool { retry_unknown || recovery_hook.is_some() } @@ -647,10 +647,7 @@ pub fn recover_workflow( /// /// Returns `Err(message)` with a neutral description of what is wrong. Callers can /// prepend their own action-specific prefix when surfacing the error to the user. -pub(crate) fn check_workflow_quiesced( - config: &Configuration, - workflow_id: i64, -) -> Result<(), String> { +fn check_workflow_quiesced(config: &Configuration, workflow_id: i64) -> Result<(), String> { // Check if workflow is complete let is_complete = apis::workflows_api::is_workflow_complete(config, workflow_id) .map_err(|e| format!("Failed to check workflow completion status: {}", e))?; @@ -1286,7 +1283,7 @@ pub fn reinitialize_workflow(config: &Configuration, workflow_id: i64) -> Result } /// Run the user's custom recovery hook command -pub fn run_recovery_hook( +fn run_recovery_hook( config: &Configuration, workflow_id: i64, hook_command: &str, @@ -1369,7 +1366,7 @@ pub fn run_recovery_hook( } /// Regenerate Slurm schedulers and submit allocations -pub fn regenerate_and_submit( +pub(crate) fn regenerate_and_submit( config: &Configuration, workflow_id: i64, output_dir: &Path, diff --git a/src/client/commands/reports.rs b/src/client/commands/reports.rs index 9379236e3..1cfa0de8f 100644 --- a/src/client/commands/reports.rs +++ b/src/client/commands/reports.rs @@ -59,7 +59,7 @@ struct ResourceUtilizationRow { over_utilization: String, } -pub fn check_resource_utilization( +pub(crate) fn check_resource_utilization( config: &Configuration, workflow_id: Option, run_id: Option, @@ -201,7 +201,7 @@ pub fn build_resource_utilization_report( ) } -pub fn build_resource_utilization_report_with_all( +fn build_resource_utilization_report_with_all( config: &Configuration, workflow_id: Option, run_id: Option, @@ -585,7 +585,7 @@ fn check_log_file_exists(path: &str, log_type: &str, job_id: i64) { } /// Generate comprehensive JSON report of job results including log file paths -pub fn generate_results_report( +pub(crate) fn generate_results_report( config: &Configuration, workflow_id: Option, output_dir: &Path, @@ -617,7 +617,7 @@ pub fn generate_results_report( print_json(&report, "results report"); } -pub fn build_results_report( +pub(crate) fn build_results_report( config: &Configuration, workflow_id: Option, output_dir: &Path, @@ -969,7 +969,7 @@ pub fn generate_summary(config: &Configuration, workflow_id: Option, format /// This function reshapes the typed response into the JSON value consumed by /// `generate_summary` and the MCP tools, adding the human-readable /// `*_formatted` presentation fields. -pub fn build_workflow_summary_report( +pub(crate) fn build_workflow_summary_report( config: &Configuration, workflow_id: Option, ) -> Result { diff --git a/src/client/commands/self_update.rs b/src/client/commands/self_update.rs index 1967597d3..e0bec2f0d 100644 --- a/src/client/commands/self_update.rs +++ b/src/client/commands/self_update.rs @@ -28,11 +28,11 @@ EXAMPLES: pub struct SelfUpdateArgs { /// Release tag or version to install (defaults to the latest stable release) #[arg(value_name = "TARGET_VERSION")] - pub target_version: Option, + target_version: Option, /// GitHub token for release API requests; also read from GITHUB_TOKEN #[arg(long, env = "GITHUB_TOKEN", hide_env_values = true)] - pub token: Option, + token: Option, } #[derive(Debug)] diff --git a/src/client/commands/slurm.rs b/src/client/commands/slurm.rs index 160d99345..8b4734818 100644 --- a/src/client/commands/slurm.rs +++ b/src/client/commands/slurm.rs @@ -994,7 +994,7 @@ pub fn generate_schedulers_for_workflow( pub struct GenerateResult { pub scheduler_count: usize, pub action_count: usize, - pub warnings: Vec, + warnings: Vec, } /// Parse memory string like "100g", "512m", "1024" (MB) into MB @@ -2173,7 +2173,7 @@ fn serialized_slurm_job_name(workflow_id: i64, scheduler_id: i64) -> String { /// Result indicating success or failure #[allow(clippy::too_many_arguments)] -pub fn schedule_slurm_nodes( +pub(crate) fn schedule_slurm_nodes( config: &Configuration, workflow_id: i64, scheduler_config_id: i64, @@ -2545,30 +2545,30 @@ pub fn create_compute_node( /// Known Slurm error patterns and their descriptions #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SlurmErrorPattern { - pub pattern: String, - pub description: String, - pub severity: String, // "error", "warning", "info" + pattern: String, + description: String, + severity: String, // "error", "warning", "info" } /// Information about a Torc job affected by a Slurm error #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AffectedJob { - pub job_id: i64, - pub job_name: String, + job_id: i64, + job_name: String, } /// A detected error in a Slurm log file #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SlurmLogError { - pub file: String, - pub slurm_job_id: String, - pub line_number: usize, - pub line: String, - pub pattern_description: String, - pub severity: String, - pub node: Option, + file: String, + slurm_job_id: String, + line_number: usize, + line: String, + pattern_description: String, + severity: String, + node: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub affected_jobs: Option>, + affected_jobs: Option>, } /// Get known Slurm error patterns to search for @@ -2937,7 +2937,7 @@ fn scan_file_for_slurm_errors( } /// Parse Slurm log files for known error messages -pub fn parse_slurm_logs( +fn parse_slurm_logs( config: &Configuration, workflow_id: i64, output_dir: &PathBuf, @@ -3210,21 +3210,21 @@ pub fn parse_slurm_logs( #[derive(Tabled, Serialize, Deserialize, Clone)] pub struct SacctSummaryRow { #[tabled(rename = "Slurm Job")] - pub slurm_job_id: String, + slurm_job_id: String, #[tabled(rename = "Job Step")] - pub job_step: String, + job_step: String, #[tabled(rename = "State")] - pub state: String, + state: String, #[tabled(rename = "Exit Code")] - pub exit_code: String, + exit_code: String, #[tabled(rename = "Elapsed")] - pub elapsed: String, + elapsed: String, #[tabled(rename = "Max RSS")] - pub max_rss: String, + max_rss: String, #[tabled(rename = "CPU Time")] - pub cpu_time: String, + cpu_time: String, #[tabled(rename = "Nodes")] - pub nodes: String, + nodes: String, } /// Extract state string from various sacct JSON formats @@ -3652,7 +3652,7 @@ fn fetch_sacct_for_workflow( } /// Run sacct for all scheduled compute nodes of type slurm and display summary -pub fn run_sacct_for_workflow( +fn run_sacct_for_workflow( config: &Configuration, workflow_id: i64, output_dir: &PathBuf, @@ -3842,56 +3842,56 @@ struct SbatchEstimate { #[derive(Debug, Clone, Serialize)] pub struct PlanAllocationsResult { /// Workflow analysis metrics - pub workflow_analysis: WorkflowAnalysisInfo, + workflow_analysis: WorkflowAnalysisInfo, /// Cluster state per partition - pub cluster_state: Vec, + cluster_state: Vec, /// Allocation recommendations per resource group - pub recommendations: Vec, + recommendations: Vec, /// Warnings from the scheduler plan generation - pub warnings: Vec, + warnings: Vec, /// Scheduler plan details per resource group - pub resource_groups: Vec, + resource_groups: Vec, /// Profile name used for the analysis - pub profile_name: String, + profile_name: String, /// Profile display name - pub profile_display_name: String, + profile_display_name: String, /// Resolved account name - pub account: String, + account: String, } /// Workflow analysis metrics #[derive(Debug, Clone, Serialize)] pub struct WorkflowAnalysisInfo { - pub total_jobs: usize, - pub total_instances: usize, - pub dependency_depth: usize, - pub max_parallelism: usize, - pub max_parallelism_level: usize, - pub resource_groups: usize, + total_jobs: usize, + total_instances: usize, + dependency_depth: usize, + max_parallelism: usize, + max_parallelism_level: usize, + resource_groups: usize, } /// Cluster state for a single partition #[derive(Debug, Clone, Serialize)] pub struct ClusterStateInfo { - pub partition: String, - pub idle: u32, - pub mixed: u32, - pub allocated: u32, - pub down: u32, - pub total: u32, - pub pending_jobs: u32, - pub pending_nodes: u32, - pub running_jobs: u32, + partition: String, + idle: u32, + mixed: u32, + allocated: u32, + down: u32, + total: u32, + pending_jobs: u32, + pending_nodes: u32, + running_jobs: u32, } /// Resource group information from the scheduler plan #[derive(Debug, Clone, Serialize)] pub struct ResourceGroupInfo { - pub name: String, - pub partition: Option, - pub job_count: usize, - pub walltime: String, - pub ideal_nodes: i64, + name: String, + partition: Option, + job_count: usize, + walltime: String, + ideal_nodes: i64, } /// Allocation strategy recommendation @@ -4224,7 +4224,7 @@ fn compute_recommendations( /// This is the core logic for `plan-allocations`, separated from CLI I/O so it can /// be used by both the CLI and MCP tool. #[allow(clippy::too_many_arguments)] -pub fn analyze_plan_allocations( +pub(crate) fn analyze_plan_allocations( spec: &mut WorkflowSpec, account: &str, partition: Option<&str>, @@ -4949,62 +4949,62 @@ fn pretty_print_yaml(spec: &WorkflowSpec) -> String { /// Result of regenerating schedulers for an existing workflow #[derive(Debug, Serialize, Deserialize)] pub struct RegenerateResult { - pub workflow_id: i64, - pub pending_jobs: usize, - pub schedulers_created: Vec, - pub total_allocations: i64, + workflow_id: i64, + pending_jobs: usize, + schedulers_created: Vec, + total_allocations: i64, /// Number of allocations actually submitted immediately - pub allocations_submitted: i64, + allocations_submitted: i64, /// Number of allocations deferred (will be submitted via on_jobs_ready action) - pub allocations_deferred: i64, - pub warnings: Vec, - pub submitted: bool, + allocations_deferred: i64, + warnings: Vec, + submitted: bool, } /// Information about a planned scheduler (for dry run output) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PlannedSchedulerInfo { - pub name: String, - pub account: String, - pub partition: Option, - pub walltime: String, - pub mem: Option, - pub nodes: i64, - pub num_allocations: i64, - pub job_count: usize, - pub job_names: Vec, - pub has_dependencies: bool, + pub(crate) name: String, + pub(crate) account: String, + pub(crate) partition: Option, + pub(crate) walltime: String, + pub(crate) mem: Option, + pub(crate) nodes: i64, + pub(crate) num_allocations: i64, + pub(crate) job_count: usize, + pub(crate) job_names: Vec, + pub(crate) has_dependencies: bool, } /// Dry run result for regenerate command #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RegenerateDryRunResult { - pub dry_run: bool, - pub workflow_id: i64, - pub pending_jobs: usize, - pub profile_name: String, - pub profile_display_name: String, - pub planned_schedulers: Vec, - pub total_allocations: i64, - pub would_submit: bool, - pub warnings: Vec, + dry_run: bool, + workflow_id: i64, + pending_jobs: usize, + profile_name: String, + profile_display_name: String, + pub(crate) planned_schedulers: Vec, + pub(crate) total_allocations: i64, + pub(crate) would_submit: bool, + warnings: Vec, } /// Information about a created scheduler #[derive(Debug, Serialize, Deserialize)] pub struct SchedulerInfo { - pub id: i64, - pub name: String, - pub account: String, - pub partition: Option, - pub walltime: String, - pub nodes: i64, - pub num_allocations: i64, - pub job_count: usize, + id: i64, + name: String, + account: String, + partition: Option, + walltime: String, + nodes: i64, + num_allocations: i64, + job_count: usize, /// Whether the jobs using this scheduler have dependencies on other pending jobs. /// If true, allocations should not be submitted immediately - they will be /// submitted when the on_jobs_ready action fires. - pub has_dependencies: bool, + has_dependencies: bool, } /// Fire every pending `schedule_nodes` action for the workflow via `WorkflowManager::start` -- diff --git a/src/client/commands/table_format.rs b/src/client/commands/table_format.rs index 44315c123..0d423998a 100644 --- a/src/client/commands/table_format.rs +++ b/src/client/commands/table_format.rs @@ -13,7 +13,7 @@ use tabled::{Table, Tabled}; /// Records are streamed straight to a locked stdout handle (no full /// materialization). A broken pipe (e.g. piping into `head`) exits silently /// with code 0; any other write error is reported to stderr and exits 1. -pub fn display_csv(items: &[T]) { +pub(crate) fn display_csv(items: &[T]) { display_csv_excluding(items, &[]); } @@ -22,7 +22,7 @@ pub fn display_csv(items: &[T]) { /// reported as warnings on stderr, matching `display_table_excluding`. /// /// Shares the streaming/error behavior documented on [`display_csv`]. -pub fn display_csv_excluding(items: &[T], exclude_columns: &[String]) { +pub(crate) fn display_csv_excluding(items: &[T], exclude_columns: &[String]) { warn_unknown_columns::(exclude_columns); let keep = kept_columns::(exclude_columns); @@ -96,7 +96,7 @@ fn handle_csv_write_error(e: csv::Error) -> ! { /// /// Returns `true` if `format` is `"csv"` (CSV was printed and the caller should /// skip its human-readable preamble / empty-state messages), otherwise `false`. -pub fn display_csv_if_csv(format: &str, items: &[T]) -> bool { +pub(crate) fn display_csv_if_csv(format: &str, items: &[T]) -> bool { if format == "csv" { display_csv(items); true @@ -106,7 +106,7 @@ pub fn display_csv_if_csv(format: &str, items: &[T]) -> bool { } /// Display a collection of items as a formatted table -pub fn display_table(items: &[T]) { +pub(crate) fn display_table(items: &[T]) { if items.is_empty() { return; } @@ -130,7 +130,7 @@ pub fn display_table_with_title(items: &[T], title: &str) { } /// Display a collection of items as a formatted table with a total count -pub fn display_table_with_count(items: &[T], item_type: &str) { +pub(crate) fn display_table_with_count(items: &[T], item_type: &str) { if items.is_empty() { return; } @@ -143,7 +143,7 @@ pub fn display_table_with_count(items: &[T], item_type: &str) { /// Build a table string with specified columns excluded (case-insensitive match). /// Returns the table string and a list of any column names that were not found. -pub fn build_table_excluding( +fn build_table_excluding( items: &[T], exclude_columns: &[String], ) -> (String, Vec) { @@ -166,7 +166,7 @@ pub fn build_table_excluding( } /// Display a table with specified columns excluded (case-insensitive match). -pub fn display_table_excluding( +pub(crate) fn display_table_excluding( items: &[T], exclude_columns: &[String], item_type: &str, @@ -196,7 +196,7 @@ pub fn display_table_excluding( /// result set of an arbitrary SQL `SELECT`), so the table is assembled from a /// [`Builder`]. Cell values are already stringified. Prints a short notice when /// there are no columns. -pub fn display_dynamic_table(columns: &[String], rows: &[Vec]) { +pub(crate) fn display_dynamic_table(columns: &[String], rows: &[Vec]) { if columns.is_empty() { println!("(no columns)"); return; @@ -215,7 +215,7 @@ pub fn display_dynamic_table(columns: &[String], rows: &[Vec]) { /// /// Always emits the header row, even with no data rows. Shares the streaming and /// broken-pipe behavior documented on [`display_csv`]. -pub fn display_dynamic_csv(columns: &[String], rows: &[Vec]) { +pub(crate) fn display_dynamic_csv(columns: &[String], rows: &[Vec]) { let stdout = std::io::stdout(); let mut wtr = csv::Writer::from_writer(stdout.lock()); let result = (|| -> csv::Result<()> { diff --git a/src/client/commands/watch.rs b/src/client/commands/watch.rs index 403ba5740..9b0b983f1 100644 --- a/src/client/commands/watch.rs +++ b/src/client/commands/watch.rs @@ -19,8 +19,6 @@ use super::recover::{RecoverArgs, recover_workflow, regenerate_and_submit}; // Use shared orphan detection logic use super::orphan_detection::cleanup_orphaned_jobs; -// Re-export for backwards compatibility -pub use super::orphan_detection::ORPHANED_JOB_RETURN_CODE; /// Default wait time for database connectivity issues (in minutes) const WAIT_FOR_HEALTHY_DATABASE_MINUTES: u64 = 20; diff --git a/src/client/commands/workflow_export.rs b/src/client/commands/workflow_export.rs index a42ca2ac3..a3a0d461c 100644 --- a/src/client/commands/workflow_export.rs +++ b/src/client/commands/workflow_export.rs @@ -28,65 +28,65 @@ use crate::models::{ }; /// Current version of the export format -pub const EXPORT_VERSION: &str = "1.0"; +pub(crate) const EXPORT_VERSION: &str = "1.0"; /// Complete workflow export document #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WorkflowExport { /// Version of the export format - pub export_version: String, + pub(crate) export_version: String, /// Timestamp when the export was created (ISO 8601) - pub exported_at: String, + exported_at: String, /// The workflow metadata - pub workflow: WorkflowModel, + pub(crate) workflow: WorkflowModel, /// All files in the workflow - pub files: Vec, + pub(crate) files: Vec, /// All user data in the workflow - pub user_data: Vec, + pub(crate) user_data: Vec, /// All resource requirements in the workflow - pub resource_requirements: Vec, + pub(crate) resource_requirements: Vec, /// Slurm schedulers in the workflow - pub slurm_schedulers: Vec, + pub(crate) slurm_schedulers: Vec, /// Local schedulers in the workflow - pub local_schedulers: Vec, + pub(crate) local_schedulers: Vec, /// Failure handlers in the workflow #[serde(default)] - pub failure_handlers: Vec, + pub(crate) failure_handlers: Vec, /// RO-Crate entities in the workflow #[serde(default)] - pub ro_crate_entities: Vec, + pub(crate) ro_crate_entities: Vec, /// All jobs in the workflow (includes relationship IDs) - pub jobs: Vec, + pub(crate) jobs: Vec, /// Workflow actions (triggers like on_workflow_start) - pub workflow_actions: Vec, + pub(crate) workflow_actions: Vec, /// Compute nodes (included when results are included, since results reference them) #[serde(default, skip_serializing_if = "Option::is_none")] - pub compute_nodes: Option>, + pub(crate) compute_nodes: Option>, /// Job results (optional, only included with --include-results) #[serde(skip_serializing_if = "Option::is_none")] - pub results: Option>, + pub(crate) results: Option>, /// Workflow events (optional, only included with --include-events) #[serde(skip_serializing_if = "Option::is_none")] - pub events: Option>, + pub(crate) events: Option>, } impl WorkflowExport { /// Create a new empty export with the current version - pub fn new(workflow: WorkflowModel) -> Self { + pub(crate) fn new(workflow: WorkflowModel) -> Self { Self { export_version: EXPORT_VERSION.to_string(), exported_at: chrono::Utc::now().to_rfc3339(), @@ -110,22 +110,22 @@ impl WorkflowExport { /// Statistics about an export or import operation #[derive(Debug, Clone, Default)] pub struct ExportImportStats { - pub jobs: usize, - pub files: usize, - pub user_data: usize, - pub resource_requirements: usize, - pub slurm_schedulers: usize, - pub local_schedulers: usize, - pub failure_handlers: usize, - pub ro_crate_entities: usize, - pub workflow_actions: usize, - pub compute_nodes: usize, - pub results: usize, - pub events: usize, + pub(crate) jobs: usize, + pub(crate) files: usize, + pub(crate) user_data: usize, + resource_requirements: usize, + slurm_schedulers: usize, + local_schedulers: usize, + failure_handlers: usize, + ro_crate_entities: usize, + workflow_actions: usize, + compute_nodes: usize, + pub(crate) results: usize, + pub(crate) events: usize, } impl ExportImportStats { - pub fn from_export(export: &WorkflowExport) -> Self { + pub(crate) fn from_export(export: &WorkflowExport) -> Self { Self { jobs: export.jobs.len(), files: export.files.len(), @@ -148,38 +148,38 @@ use std::collections::HashMap; /// ID mapping tables used during import #[derive(Debug, Default)] pub struct IdMappings { - pub files: HashMap, - pub user_data: HashMap, - pub resource_requirements: HashMap, - pub slurm_schedulers: HashMap, - pub local_schedulers: HashMap, - pub failure_handlers: HashMap, - pub jobs: HashMap, - pub compute_nodes: HashMap, + pub(crate) files: HashMap, + pub(crate) user_data: HashMap, + pub(crate) resource_requirements: HashMap, + pub(crate) slurm_schedulers: HashMap, + pub(crate) local_schedulers: HashMap, + pub(crate) failure_handlers: HashMap, + pub(crate) jobs: HashMap, + pub(crate) compute_nodes: HashMap, } impl IdMappings { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } /// Remap a file ID using the mapping table - pub fn remap_file_id(&self, old_id: i64) -> Option { + pub(crate) fn remap_file_id(&self, old_id: i64) -> Option { self.files.get(&old_id).copied() } /// Remap a user_data ID using the mapping table - pub fn remap_user_data_id(&self, old_id: i64) -> Option { + fn remap_user_data_id(&self, old_id: i64) -> Option { self.user_data.get(&old_id).copied() } /// Remap a resource_requirements ID using the mapping table - pub fn remap_resource_requirements_id(&self, old_id: i64) -> Option { + pub(crate) fn remap_resource_requirements_id(&self, old_id: i64) -> Option { self.resource_requirements.get(&old_id).copied() } /// Remap a scheduler ID (tries both slurm and local) - pub fn remap_scheduler_id(&self, old_id: i64) -> Option { + pub(crate) fn remap_scheduler_id(&self, old_id: i64) -> Option { self.slurm_schedulers .get(&old_id) .or_else(|| self.local_schedulers.get(&old_id)) @@ -187,22 +187,22 @@ impl IdMappings { } /// Remap a failure_handler ID using the mapping table - pub fn remap_failure_handler_id(&self, old_id: i64) -> Option { + pub(crate) fn remap_failure_handler_id(&self, old_id: i64) -> Option { self.failure_handlers.get(&old_id).copied() } /// Remap a job ID using the mapping table - pub fn remap_job_id(&self, old_id: i64) -> Option { + pub(crate) fn remap_job_id(&self, old_id: i64) -> Option { self.jobs.get(&old_id).copied() } /// Remap a compute_node ID using the mapping table - pub fn remap_compute_node_id(&self, old_id: i64) -> Option { + pub(crate) fn remap_compute_node_id(&self, old_id: i64) -> Option { self.compute_nodes.get(&old_id).copied() } /// Remap a vector of file IDs - pub fn remap_file_ids(&self, old_ids: &[i64]) -> Vec { + pub(crate) fn remap_file_ids(&self, old_ids: &[i64]) -> Vec { old_ids .iter() .filter_map(|id| self.remap_file_id(*id)) @@ -210,7 +210,7 @@ impl IdMappings { } /// Remap a vector of user_data IDs - pub fn remap_user_data_ids(&self, old_ids: &[i64]) -> Vec { + pub(crate) fn remap_user_data_ids(&self, old_ids: &[i64]) -> Vec { old_ids .iter() .filter_map(|id| self.remap_user_data_id(*id)) @@ -218,7 +218,7 @@ impl IdMappings { } /// Remap a vector of job IDs - pub fn remap_job_ids(&self, old_ids: &[i64]) -> Vec { + pub(crate) fn remap_job_ids(&self, old_ids: &[i64]) -> Vec { old_ids .iter() .filter_map(|id| self.remap_job_id(*id)) @@ -232,7 +232,7 @@ impl IdMappings { /// - metadata JSON containing `prov:wasGeneratedBy: {"@id": "#job-{old_id}-attempt-{n}"}` /// /// Returns the updated (entity_id, metadata) tuple. - pub fn remap_ro_crate_job_ids( + pub(crate) fn remap_ro_crate_job_ids( &self, entity_id: &str, metadata: &std::collections::HashMap, diff --git a/src/client/commands/workflows.rs b/src/client/commands/workflows.rs index b4ed1ba9d..cd8f2877d 100644 --- a/src/client/commands/workflows.rs +++ b/src/client/commands/workflows.rs @@ -2189,7 +2189,7 @@ fn wait_for_workflow_task_or_exit( } } -pub fn handle_reinitialize( +fn handle_reinitialize( config: &Configuration, workflow_id: &Option, force: bool, @@ -2328,7 +2328,7 @@ pub fn handle_reinitialize( } #[allow(clippy::too_many_arguments)] -pub fn handle_initialize( +fn handle_initialize( config: &Configuration, workflow_id: &Option, force: bool, diff --git a/src/client/execution_plan.rs b/src/client/execution_plan.rs index 667e3b02c..5976f85c9 100644 --- a/src/client/execution_plan.rs +++ b/src/client/execution_plan.rs @@ -21,10 +21,10 @@ use std::collections::{HashMap, HashSet}; /// Represents a scheduler allocation in the execution plan #[derive(Debug, Clone, Serialize)] pub struct SchedulerAllocation { - pub scheduler: String, - pub scheduler_type: String, - pub num_allocations: i64, - pub jobs: Vec, + pub(crate) scheduler: String, + pub(crate) scheduler_type: String, + pub(crate) num_allocations: i64, + pub(crate) jobs: Vec, } /// What triggers an execution event @@ -41,19 +41,19 @@ pub enum EventTrigger { #[derive(Debug, Clone, Serialize)] pub struct ExecutionEvent { /// Unique identifier for this event - pub id: String, + pub(crate) id: String, /// What triggers this event - pub trigger: EventTrigger, + pub(crate) trigger: EventTrigger, /// Human-readable description of the trigger pub trigger_description: String, /// Scheduler allocations triggered by this event - pub scheduler_allocations: Vec, + pub(crate) scheduler_allocations: Vec, /// Jobs that become ready when this event fires pub jobs_becoming_ready: Vec, /// Event IDs that must complete before this event can fire - pub depends_on_events: Vec, + pub(crate) depends_on_events: Vec, /// Event IDs that depend on this event - pub unlocks_events: Vec, + pub(crate) unlocks_events: Vec, } /// Represents the complete execution plan for a workflow as a DAG @@ -67,17 +67,17 @@ pub struct ExecutionPlan { pub leaf_events: Vec, /// The underlying workflow graph (if built from spec) #[serde(skip)] - pub graph: Option, + graph: Option, } // Legacy stage-based interface for backwards compatibility /// Represents a stage in the workflow execution plan (legacy format) #[derive(Debug, Clone, Serialize)] pub struct ExecutionStage { - pub stage_number: usize, - pub trigger_description: String, - pub scheduler_allocations: Vec, - pub jobs_becoming_ready: Vec, + stage_number: usize, + trigger_description: String, + scheduler_allocations: Vec, + jobs_becoming_ready: Vec, } impl ExecutionPlan { @@ -486,7 +486,7 @@ impl ExecutionPlan { } /// Display the execution plan in a human-readable format - pub fn display(&self) { + pub(crate) fn display(&self) { println!("\n{}", "=".repeat(80)); println!("Workflow Execution Plan (DAG)"); println!("{}", "=".repeat(80)); @@ -606,14 +606,14 @@ impl ExecutionPlan { } /// Get the underlying workflow graph (if available) - pub fn workflow_graph(&self) -> Option<&WorkflowGraph> { + fn workflow_graph(&self) -> Option<&WorkflowGraph> { self.graph.as_ref() } /// Convert to legacy stage format for backwards compatibility /// Note: This flattens the DAG into a linear sequence, which may not /// accurately represent parallel subgraphs - pub fn to_stages(&self) -> Vec { + fn to_stages(&self) -> Vec { let mut stages = Vec::new(); let mut displayed = HashSet::new(); let mut queue: Vec = self.root_events.clone(); diff --git a/src/client/hpc.rs b/src/client/hpc.rs index 3e6355b17..8fd07a9ca 100644 --- a/src/client/hpc.rs +++ b/src/client/hpc.rs @@ -8,24 +8,24 @@ //! that include partition configurations, resource limits, and auto-detection. pub mod common; -pub mod dane; +mod dane; pub mod hpc_interface; -pub mod hpc_manager; +pub(crate) mod hpc_manager; pub mod kestrel; pub mod profiles; pub mod slurm; pub mod slurm_interface; -pub use common::{HpcJobInfo, HpcJobStats, HpcJobStatus, HpcType}; +pub(crate) use common::HpcType; +pub use common::{HpcJobInfo, HpcJobStats, HpcJobStatus}; pub use hpc_interface::HpcInterface; -pub use hpc_manager::HpcManager; pub use profiles::{HpcDetection, HpcPartition, HpcProfile, HpcProfileRegistry}; pub use slurm_interface::SlurmInterface; use anyhow::Result; /// Factory function to create an HPC interface based on the type -pub fn create_hpc_interface(hpc_type: HpcType) -> Result> { +fn create_hpc_interface(hpc_type: HpcType) -> Result> { match hpc_type { HpcType::Slurm => Ok(Box::new(SlurmInterface::new()?)), HpcType::Pbs => Err(anyhow::anyhow!("PBS support not yet implemented")), diff --git a/src/client/hpc/common.rs b/src/client/hpc/common.rs index 12661996a..6c797ca55 100644 --- a/src/client/hpc/common.rs +++ b/src/client/hpc/common.rs @@ -43,7 +43,7 @@ pub struct HpcJobInfo { } impl HpcJobInfo { - pub fn new(job_id: String, name: String, status: HpcJobStatus) -> Self { + pub(crate) fn new(job_id: String, name: String, status: HpcJobStatus) -> Self { Self { job_id, name, @@ -52,7 +52,7 @@ impl HpcJobInfo { } /// Create an empty job info for when no job is found - pub fn none() -> Self { + pub(crate) fn none() -> Self { Self { job_id: String::new(), name: String::new(), @@ -65,21 +65,21 @@ impl HpcJobInfo { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct HpcJobStats { /// HPC job ID - pub hpc_job_id: String, + pub(crate) hpc_job_id: String, /// Job name - pub name: String, + pub(crate) name: String, /// Job start time - pub start: DateTime, + pub(crate) start: DateTime, /// Job end time (if finished) - pub end: Option>, + pub(crate) end: Option>, /// Job state as a string - pub state: String, + pub(crate) state: String, /// Account used for the job - pub account: String, + pub(crate) account: String, /// Partition/queue name - pub partition: String, + pub(crate) partition: String, /// Quality of Service - pub qos: String, + pub(crate) qos: String, } /// HPC types supported diff --git a/src/client/hpc/dane.rs b/src/client/hpc/dane.rs index b37bb8b6f..58a7be961 100644 --- a/src/client/hpc/dane.rs +++ b/src/client/hpc/dane.rs @@ -9,7 +9,7 @@ use super::profiles::{HpcDetection, HpcPartition, HpcProfile}; /// Create the Dane HPC profile -pub fn dane_profile() -> HpcProfile { +pub(crate) fn dane_profile() -> HpcProfile { HpcProfile { name: "dane".to_string(), display_name: "LLNL Dane".to_string(), diff --git a/src/client/hpc/hpc_manager.rs b/src/client/hpc/hpc_manager.rs index f4a0e8703..0fb509bf1 100644 --- a/src/client/hpc/hpc_manager.rs +++ b/src/client/hpc/hpc_manager.rs @@ -9,7 +9,7 @@ use super::common::{HpcJobStats, HpcJobStatus, HpcType}; use super::hpc_interface::HpcInterface; /// Manages HPC job submission and monitoring -pub struct HpcManager { +pub(crate) struct HpcManager { output: String, config: HashMap, hpc_type: HpcType, @@ -23,7 +23,7 @@ impl HpcManager { /// * `config` - Configuration parameters for the HPC scheduler /// * `hpc_type` - Type of HPC scheduler (Slurm, PBS, etc.) /// * `output` - Directory path for job output files - pub fn new(config: HashMap, hpc_type: HpcType, output: String) -> Result { + fn new(config: HashMap, hpc_type: HpcType, output: String) -> Result { let interface = super::create_hpc_interface(hpc_type)?; trace!("Constructed HpcManager with output={}", output); @@ -43,7 +43,7 @@ impl HpcManager { /// /// # Returns /// The return code from the cancellation command (0 = success) - pub fn cancel_job(&self, job_id: &str) -> Result { + fn cancel_job(&self, job_id: &str) -> Result { let ret = self.interface.cancel_job(job_id)?; if ret == 0 { @@ -62,7 +62,7 @@ impl HpcManager { /// /// # Returns /// The current status of the job - pub fn get_status(&self, job_id: &str) -> Result { + fn get_status(&self, job_id: &str) -> Result { let info = self.interface.get_status(job_id)?; trace!("hpc_job_id={} status={:?}", job_id, info.status); Ok(info.status) @@ -72,7 +72,7 @@ impl HpcManager { /// /// # Returns /// HashMap mapping job_id to HpcJobStatus - pub fn get_statuses(&self) -> Result> { + fn get_statuses(&self) -> Result> { self.interface.get_statuses() } @@ -83,7 +83,7 @@ impl HpcManager { /// /// # Returns /// HpcJobStats with detailed job information - pub fn get_job_stats(&self, job_id: &str) -> Result { + fn get_job_stats(&self, job_id: &str) -> Result { self.interface.get_job_stats(job_id) } @@ -91,12 +91,12 @@ impl HpcManager { /// /// # Returns /// Path to local storage space - pub fn get_local_scratch(&self) -> Result { + fn get_local_scratch(&self) -> Result { self.interface.get_local_scratch() } /// Return the type of HPC management system - pub fn hpc_type(&self) -> HpcType { + fn hpc_type(&self) -> HpcType { self.hpc_type } @@ -107,7 +107,7 @@ impl HpcManager { /// /// # Returns /// Vector of node hostnames in deterministic order - pub fn list_active_nodes(&self, job_id: &str) -> Result> { + fn list_active_nodes(&self, job_id: &str) -> Result> { self.interface.list_active_nodes(job_id) } @@ -125,7 +125,7 @@ impl HpcManager { /// # Returns /// The HPC job ID #[allow(clippy::too_many_arguments)] - pub fn submit( + fn submit( &self, directory: &Path, name: &str, diff --git a/src/client/hpc/profiles.rs b/src/client/hpc/profiles.rs index 9ea3e9d1c..5c4e278b0 100644 --- a/src/client/hpc/profiles.rs +++ b/src/client/hpc/profiles.rs @@ -33,7 +33,7 @@ pub enum HpcDetection { impl HpcDetection { /// Check if this detection method matches the current environment - pub fn matches(&self) -> bool { + fn matches(&self) -> bool { match self { HpcDetection::EnvVar { name, value } => { env::var(name).map(|v| v == *value).unwrap_or(false) @@ -118,7 +118,7 @@ pub struct HpcPartition { impl HpcPartition { /// Get the maximum wall time as a Duration - pub fn max_walltime(&self) -> Duration { + fn max_walltime(&self) -> Duration { Duration::from_secs(self.max_walltime_secs) } @@ -139,7 +139,7 @@ impl HpcPartition { } /// Get memory in GB - pub fn memory_gb(&self) -> f64 { + pub(crate) fn memory_gb(&self) -> f64 { self.memory_mb as f64 / 1024.0 } @@ -349,7 +349,7 @@ impl HpcProfile { } /// Get all GPU partitions - pub fn gpu_partitions(&self) -> Vec<&HpcPartition> { + fn gpu_partitions(&self) -> Vec<&HpcPartition> { self.partitions .iter() .filter(|p| p.gpus_per_node.is_some()) @@ -357,7 +357,7 @@ impl HpcProfile { } /// Get all CPU-only partitions - pub fn cpu_partitions(&self) -> Vec<&HpcPartition> { + fn cpu_partitions(&self) -> Vec<&HpcPartition> { self.partitions .iter() .filter(|p| p.gpus_per_node.is_none()) @@ -380,7 +380,7 @@ impl HpcProfileRegistry { } /// Create a registry with all built-in profiles - pub fn with_builtin_profiles() -> Self { + pub(crate) fn with_builtin_profiles() -> Self { let mut registry = Self::new(); registry.register(super::dane::dane_profile()); registry.register(super::kestrel::kestrel_profile()); @@ -395,7 +395,7 @@ impl HpcProfileRegistry { } /// Get all registered profiles - pub fn profiles(&self) -> &[HpcProfile] { + pub(crate) fn profiles(&self) -> &[HpcProfile] { &self.profiles } @@ -420,7 +420,7 @@ impl HpcProfileRegistry { } /// Get profile names - pub fn names(&self) -> Vec<&str> { + fn names(&self) -> Vec<&str> { self.profiles.iter().map(|p| p.name.as_str()).collect() } } diff --git a/src/client/hpc/slurm.rs b/src/client/hpc/slurm.rs index 99315adc3..024f2934f 100644 --- a/src/client/hpc/slurm.rs +++ b/src/client/hpc/slurm.rs @@ -65,7 +65,7 @@ pub fn detect_slurm_profile() -> Option { } /// Generate an HPC profile from the current Slurm cluster -pub fn generate_dynamic_slurm_profile( +pub(crate) fn generate_dynamic_slurm_profile( name: Option, display_name: Option, skip_stdby: bool, @@ -274,7 +274,7 @@ pub fn parse_sinfo_string(input: &str) -> Result, String> { } /// Parse timelimit string from Slurm format to seconds -pub fn parse_slurm_timelimit(s: &str) -> u64 { +pub(crate) fn parse_slurm_timelimit(s: &str) -> u64 { let s = s.trim(); if s == "infinite" || s == "UNLIMITED" { @@ -421,27 +421,27 @@ pub(crate) fn parse_gres(gres: &Option) -> (Option, Option) /// Node availability counts for a partition #[derive(Debug, Clone)] pub struct PartitionAvailability { - pub partition: String, - pub idle: u32, - pub mixed: u32, - pub allocated: u32, - pub down: u32, - pub total: u32, + pub(crate) partition: String, + pub(crate) idle: u32, + pub(crate) mixed: u32, + pub(crate) allocated: u32, + pub(crate) down: u32, + pub(crate) total: u32, } /// Queue depth information for a partition #[derive(Debug, Clone)] pub struct QueueDepthInfo { - pub partition: String, - pub pending_jobs: u32, - pub pending_nodes: u32, - pub running_jobs: u32, + pub(crate) partition: String, + pub(crate) pending_jobs: u32, + pub(crate) pending_nodes: u32, + pub(crate) running_jobs: u32, } /// Query sinfo for node availability per partition. /// /// If `partition` is Some, only queries that partition. Otherwise queries all. -pub fn query_partition_availability( +pub(crate) fn query_partition_availability( partition: Option<&str>, ) -> Result, String> { let mut args = vec!["-e", "-o", "%P|%T|%D", "--noheader"]; @@ -470,7 +470,7 @@ pub fn query_partition_availability( /// Parse sinfo output for node availability. /// Format: "%P|%T|%D" (partition|state|node_count) -pub fn parse_partition_availability(input: &str) -> Result, String> { +fn parse_partition_availability(input: &str) -> Result, String> { let mut map: HashMap = HashMap::new(); for line in input.lines() { @@ -519,7 +519,7 @@ pub fn parse_partition_availability(input: &str) -> Result) -> Result, String> { +pub(crate) fn query_queue_depth(partition: Option<&str>) -> Result, String> { let squeue_exec = if cfg!(any(test, debug_assertions)) { std::env::var("TORC_FAKE_SQUEUE").unwrap_or_else(|_| "squeue".to_string()) } else { @@ -552,7 +552,7 @@ pub fn query_queue_depth(partition: Option<&str>) -> Result, /// Parse squeue output for queue depth. /// Format: "%P|%T|%D" (partition|state|nodes) -pub fn parse_queue_depth(input: &str) -> Result, String> { +fn parse_queue_depth(input: &str) -> Result, String> { let mut map: HashMap = HashMap::new(); for line in input.lines() { @@ -595,13 +595,13 @@ pub fn parse_queue_depth(input: &str) -> Result, String> { #[derive(Debug, Clone)] pub struct SbatchTestResult { /// Estimated start time from Slurm scheduler - pub estimated_start: Option, + pub(crate) estimated_start: Option, /// Whether the probe succeeded - pub success: bool, + success: bool, /// Error message if the probe failed - pub error_message: Option, + pub(crate) error_message: Option, /// Raw output from sbatch (for debugging) - pub raw_output: String, + raw_output: String, } /// Get the sbatch executable path (allows for testing with fake binary in dev/test builds) @@ -617,7 +617,7 @@ fn get_sbatch_exec() -> String { /// /// This does NOT submit a job. It asks the scheduler when a job with the given /// parameters would start, without actually queuing it. -pub fn run_sbatch_test_only( +pub(crate) fn run_sbatch_test_only( account: &str, partition: Option<&str>, nodes: u32, @@ -684,7 +684,7 @@ pub fn run_sbatch_test_only( /// /// Some Slurm versions use slightly different formats: /// `sbatch: Job 12345 to start at 2026-03-17T14:30:00 on nodes ...` -pub fn parse_sbatch_test_only(output: &str) -> SbatchTestResult { +fn parse_sbatch_test_only(output: &str) -> SbatchTestResult { // Look for the estimated start time pattern // Various Slurm versions may use slightly different formats for line in output.lines() { diff --git a/src/client/job_runner.rs b/src/client/job_runner.rs index 33493d9a4..23727601a 100644 --- a/src/client/job_runner.rs +++ b/src/client/job_runner.rs @@ -76,7 +76,7 @@ pub struct Wakeup { } impl Wakeup { - pub fn new() -> Arc { + fn new() -> Arc { Arc::new(Self { pending: Mutex::new(false), cv: Condvar::new(), @@ -93,7 +93,7 @@ impl Wakeup { /// Wait until notified or `timeout` elapses. Returns `true` if a /// notification was consumed, `false` on timeout. - pub fn wait_with_timeout(&self, timeout: Duration) -> bool { + fn wait_with_timeout(&self, timeout: Duration) -> bool { let mut pending = self.pending.lock().unwrap(); if *pending { *pending = false; @@ -309,13 +309,13 @@ fn next_poll_interval( #[derive(Debug, Clone, serde::Deserialize)] pub struct FailureHandlerRule { #[serde(default)] - pub exit_codes: Vec, + exit_codes: Vec, /// If true, this rule matches any non-zero exit code #[serde(default)] - pub match_all_exit_codes: bool, - pub recovery_script: Option, + match_all_exit_codes: bool, + recovery_script: Option, #[serde(default = "default_max_retries")] - pub max_retries: i32, + max_retries: i32, } fn default_max_retries() -> i32 { @@ -992,7 +992,7 @@ impl JobRunner { /// /// Returns `true` if the termination flag has been set, indicating that the /// JobRunner should stop accepting new jobs and gracefully terminate running ones. - pub fn is_termination_requested(&self) -> bool { + fn is_termination_requested(&self) -> bool { self.termination_requested.load(Ordering::SeqCst) } @@ -1004,7 +1004,7 @@ impl JobRunner { /// /// Typically, termination is triggered by a signal handler, but this method /// allows programmatic termination for testing or other use cases. - pub fn request_termination(&self) { + fn request_termination(&self) { self.termination_requested.store(true, Ordering::SeqCst); } diff --git a/src/client/log_paths.rs b/src/client/log_paths.rs index 8f4bd09bf..23b744f38 100644 --- a/src/client/log_paths.rs +++ b/src/client/log_paths.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; /// Return the name of the job runner log file for the local runner. -pub fn get_job_runner_log_file( +pub(crate) fn get_job_runner_log_file( output_dir: PathBuf, hostname: &str, workflow_id: i64, @@ -35,7 +35,7 @@ pub fn get_slurm_job_runner_log_file( } /// Get the path to a job's stdout log file -pub fn get_job_stdout_path( +pub(crate) fn get_job_stdout_path( output_dir: &Path, workflow_id: i64, job_id: i64, @@ -53,7 +53,7 @@ pub fn get_job_stdout_path( } /// Get the path to a job's stderr log file -pub fn get_job_stderr_path( +pub(crate) fn get_job_stderr_path( output_dir: &Path, workflow_id: i64, job_id: i64, @@ -71,7 +71,7 @@ pub fn get_job_stderr_path( } /// Get the path to a job's combined stdout+stderr log file -pub fn get_job_combined_path( +pub(crate) fn get_job_combined_path( output_dir: &Path, workflow_id: i64, job_id: i64, @@ -89,7 +89,11 @@ pub fn get_job_combined_path( } /// Get the path to Slurm's stdout log file -pub fn get_slurm_stdout_path(output_dir: &Path, workflow_id: i64, slurm_job_id: &str) -> String { +pub(crate) fn get_slurm_stdout_path( + output_dir: &Path, + workflow_id: i64, + slurm_job_id: &str, +) -> String { format!( "{}/slurm_output_wf{}_sl{}.o", output_dir.display(), @@ -99,7 +103,11 @@ pub fn get_slurm_stdout_path(output_dir: &Path, workflow_id: i64, slurm_job_id: } /// Get the path to Slurm's stderr log file -pub fn get_slurm_stderr_path(output_dir: &Path, workflow_id: i64, slurm_job_id: &str) -> String { +pub(crate) fn get_slurm_stderr_path( + output_dir: &Path, + workflow_id: i64, + slurm_job_id: &str, +) -> String { format!( "{}/slurm_output_wf{}_sl{}.e", output_dir.display(), @@ -147,7 +155,7 @@ pub fn get_slurm_env_log_file( } /// Return the name of the watch log file. -pub fn get_watch_log_file(output_dir: PathBuf, hostname: &str, workflow_id: i64) -> String { +pub(crate) fn get_watch_log_file(output_dir: PathBuf, hostname: &str, workflow_id: i64) -> String { format!( "{}/watch_{}_wf{}.log", output_dir.display(), diff --git a/src/client/offline_journal.rs b/src/client/offline_journal.rs index 09826db46..c5dc338f8 100644 --- a/src/client/offline_journal.rs +++ b/src/client/offline_journal.rs @@ -28,7 +28,7 @@ const FILENAME_PREFIX: &str = "offline_results"; /// Maximum completions to send in a single `batch_complete_jobs` request when /// replaying a journal. Bounds request body size and lets replay make partial /// progress. Shared by the runner's resume flush and `torc workflows reconcile`. -pub const FLUSH_BATCH_SIZE: usize = 500; +pub(crate) const FLUSH_BATCH_SIZE: usize = 500; /// Build the journal file name for a runner. The `workflow_id` / `run_id` prefix /// is what [`OfflineJournal::discover`] globs on; `unique_label` makes the name @@ -44,12 +44,7 @@ fn journal_filename_prefix(workflow_id: i64, run_id: i64) -> String { /// Returns the path of the journal file that would be created for the given /// runner identity, mirroring the layout used by [`OfflineJournal::open_or_create`]. -pub fn journal_path( - output_dir: &Path, - workflow_id: i64, - run_id: i64, - unique_label: &str, -) -> PathBuf { +fn journal_path(output_dir: &Path, workflow_id: i64, run_id: i64, unique_label: &str) -> PathBuf { output_dir .join(JOURNAL_SUBDIR) .join(journal_filename(workflow_id, run_id, unique_label)) @@ -99,7 +94,7 @@ impl OfflineJournal { } /// Path to the underlying SQLite file. - pub fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Path { &self.path } @@ -131,13 +126,13 @@ impl OfflineJournal { } /// Read every journaled completion from this file. - pub fn read_all(&self) -> Result, String> { + pub(crate) fn read_all(&self) -> Result, String> { Self::read_all_from_conn(&self.conn) } /// Delete all journaled completions. Called after a successful flush to the /// server so a subsequent outage does not re-submit already-recorded jobs. - pub fn clear(&self) -> Result<(), String> { + pub(crate) fn clear(&self) -> Result<(), String> { self.conn .execute("DELETE FROM journaled_completions", []) .map_err(|e| format!("Failed to clear journal: {e}"))?; @@ -164,7 +159,7 @@ impl OfflineJournal { /// Read all completions from a journal file at `path` without holding a /// long-lived handle. Used by `torc workflows reconcile`. - pub fn read_file(path: &Path) -> Result, String> { + pub(crate) fn read_file(path: &Path) -> Result, String> { let conn = Connection::open(path) .map_err(|e| format!("Failed to open journal {}: {e}", path.display()))?; Self::read_all_from_conn(&conn) @@ -173,7 +168,7 @@ impl OfflineJournal { /// Count the journaled completions in the file at `path` without /// deserializing each payload. Cheaper than [`read_file`] when only the /// number of pending completions is needed (e.g. an advisory check). - pub fn count_file(path: &Path) -> Result { + pub(crate) fn count_file(path: &Path) -> Result { let conn = Connection::open(path) .map_err(|e| format!("Failed to open journal {}: {e}", path.display()))?; let count: i64 = conn @@ -187,7 +182,7 @@ impl OfflineJournal { /// Find every journal file for a `(workflow_id, run_id)` by recursively /// walking `base_dir`. Matching is by file name prefix, so journals written /// to per-node `output_dir`s nested anywhere under `base_dir` are all found. - pub fn discover(base_dir: &Path, workflow_id: i64, run_id: i64) -> Vec { + pub(crate) fn discover(base_dir: &Path, workflow_id: i64, run_id: i64) -> Vec { let prefix = journal_filename_prefix(workflow_id, run_id); let mut found = Vec::new(); walk(base_dir, &prefix, &mut found); diff --git a/src/client/parameter_expansion.rs b/src/client/parameter_expansion.rs index 42b980a4b..3416f0298 100644 --- a/src/client/parameter_expansion.rs +++ b/src/client/parameter_expansion.rs @@ -21,7 +21,7 @@ impl std::fmt::Display for ParameterValue { impl ParameterValue { /// Format the parameter value with optional format specifier /// Supports printf-style format specifiers like {:03d} for integers - pub fn format(&self, format_spec: Option<&str>) -> String { + pub(crate) fn format(&self, format_spec: Option<&str>) -> String { match (self, format_spec) { (ParameterValue::Integer(i), Some(spec)) => { // Parse format spec like "03d" to mean zero-padded 3 digits @@ -57,7 +57,7 @@ impl ParameterValue { /// /// Also tolerates curly braces around values (e.g., "{1:100}" is treated as "1:100") /// since users sometimes confuse parameter value syntax with template substitution syntax. -pub fn parse_parameter_value(value: &str) -> Result, String> { +pub(crate) fn parse_parameter_value(value: &str) -> Result, String> { let trimmed = value.trim(); // File-backed list: `@path/to/file.txt` (one value per line) @@ -191,7 +191,9 @@ fn parse_file_list(path: &str) -> Result, String> { /// /// Returns an error if the file is missing, has an unsupported extension, is /// malformed, or contains zero rows. -pub fn load_parameter_table(path: &str) -> Result>, String> { +pub(crate) fn load_parameter_table( + path: &str, +) -> Result>, String> { let trimmed = path.trim(); if trimmed.is_empty() { return Err("Empty parameters_file path".to_string()); @@ -460,7 +462,7 @@ fn parse_range(value: &str) -> Result, String> { /// Generate the Cartesian product of parameter values /// Given a map of parameter names to value lists, returns a vector of all possible combinations -pub fn cartesian_product( +pub(crate) fn cartesian_product( params: &HashMap>, ) -> Vec> { if params.is_empty() { @@ -491,7 +493,7 @@ pub fn cartesian_product( /// All parameter lists must have the same length /// Given a map of parameter names to value lists, returns a vector where /// the i-th element contains the i-th value from each parameter -pub fn zip_parameters( +pub(crate) fn zip_parameters( params: &HashMap>, ) -> Result>, String> { if params.is_empty() { @@ -534,7 +536,10 @@ pub fn zip_parameters( /// Substitute parameter values into a template string /// Supports both {param_name} and {param_name:format} syntax -pub fn substitute_parameters(template: &str, params: &HashMap) -> String { +pub(crate) fn substitute_parameters( + template: &str, + params: &HashMap, +) -> String { let mut result = template.to_string(); for (param_name, param_value) in params { @@ -569,10 +574,7 @@ pub fn substitute_parameters(template: &str, params: &HashMap, -) -> String { +fn substitute_parameters_regex(template: &str, params: &HashMap) -> String { let mut result = template.to_string(); for (param_name, param_value) in params { diff --git a/src/client/remote.rs b/src/client/remote.rs index dede123b6..181cf13be 100644 --- a/src/client/remote.rs +++ b/src/client/remote.rs @@ -29,10 +29,12 @@ pub mod types; pub mod worker_file; pub use shell::{RemoteShell, detect_remote_shell}; -pub use ssh::{ - check_all_connectivity, check_ssh_connectivity, get_remote_torc_version, parallel_execute, - scp_download, ssh_execute, ssh_execute_capture, ssh_execute_checked, verify_all_versions, - verify_version, +pub(crate) use ssh::{ + check_all_connectivity, check_ssh_connectivity, parallel_execute, scp_download, + ssh_execute_checked, verify_all_versions, }; -pub use types::{RemoteOperationResult, RemoteWorkerState, WorkerEntry}; -pub use worker_file::{parse_worker_content, parse_worker_file}; +pub use ssh::{ssh_execute, ssh_execute_capture}; +pub use types::WorkerEntry; +pub(crate) use types::{RemoteOperationResult, RemoteWorkerState}; +pub use worker_file::parse_worker_content; +pub(crate) use worker_file::parse_worker_file; diff --git a/src/client/remote/shell.rs b/src/client/remote/shell.rs index a53a6f349..f8a4abba1 100644 --- a/src/client/remote/shell.rs +++ b/src/client/remote/shell.rs @@ -256,7 +256,7 @@ impl RemoteShell { /// Command that prints `started` if `log_file` contains the worker startup /// line, else `waiting`. A missing log file prints `waiting`. - pub fn log_shows_startup(&self, log_file: &str) -> String { + pub(crate) fn log_shows_startup(&self, log_file: &str) -> String { match self { RemoteShell::Posix => format!( "grep -q 'Starting torc job runner' {} 2>/dev/null && echo started || echo waiting", @@ -273,7 +273,7 @@ impl RemoteShell { /// Command that prints `running` if a `torc ... run ` process /// exists, else `stopped`. Used to confirm startup after the log line. - pub fn torc_process_running(&self, workflow_id: i64) -> String { + pub(crate) fn torc_process_running(&self, workflow_id: i64) -> String { match self { RemoteShell::Posix => format!( "pgrep -f 'torc .* run {}( |$)' >/dev/null 2>&1 && echo running || echo stopped", @@ -288,7 +288,7 @@ impl RemoteShell { /// Command that prints the PID of a `torc ... run ` process if /// one exists, else nothing. Used as a fallback when the PID file is absent. - pub fn torc_process_pid(&self, workflow_id: i64) -> String { + pub(crate) fn torc_process_pid(&self, workflow_id: i64) -> String { match self { RemoteShell::Posix => { format!( diff --git a/src/client/remote/ssh.rs b/src/client/remote/ssh.rs index 62e6a87d7..01d7fd92f 100644 --- a/src/client/remote/ssh.rs +++ b/src/client/remote/ssh.rs @@ -58,7 +58,7 @@ pub fn ssh_execute( /// (an unreadable PID file, a missing archive) with the original stderr lost. /// This wrapper checks `status.success()` and surfaces the remote stderr at the /// call site instead. -pub fn ssh_execute_checked( +pub(crate) fn ssh_execute_checked( worker: &WorkerEntry, command: &str, timeout_secs: Option, @@ -105,7 +105,7 @@ pub fn ssh_execute_capture(worker: &WorkerEntry, command: &str) -> Result Result<(), String> { +pub(crate) fn check_ssh_connectivity(worker: &WorkerEntry) -> Result<(), String> { debug!("Checking SSH connectivity to {}", worker.display_name()); super::shell::detect_remote_shell(worker).map(|shell| { @@ -120,7 +120,7 @@ pub fn check_ssh_connectivity(worker: &WorkerEntry) -> Result<(), String> { /// Get the torc version on a remote host. /// /// Returns the version string (e.g., "torc 0.7.0"). -pub fn get_remote_torc_version(worker: &WorkerEntry) -> Result { +pub(crate) fn get_remote_torc_version(worker: &WorkerEntry) -> Result { debug!("Getting torc version from {}", worker.display_name()); let output = ssh_execute_capture(worker, "torc --version")?; @@ -130,13 +130,13 @@ pub fn get_remote_torc_version(worker: &WorkerEntry) -> Result { /// Parse the version from a torc version string. /// /// Handles formats like "torc 0.7.0" or just "0.7.0". -pub fn parse_torc_version(version_str: &str) -> String { +fn parse_torc_version(version_str: &str) -> String { let trimmed = version_str.trim(); trimmed.strip_prefix("torc ").unwrap_or(trimmed).to_string() } /// Verify that a remote worker has the same torc version as local. -pub fn verify_version(worker: &WorkerEntry, local_version: &str) -> Result<(), String> { +pub(crate) fn verify_version(worker: &WorkerEntry, local_version: &str) -> Result<(), String> { let remote_version_str = get_remote_torc_version(worker)?; let remote_version = parse_torc_version(&remote_version_str); @@ -160,7 +160,7 @@ pub fn verify_version(worker: &WorkerEntry, local_version: &str) -> Result<(), S /// Execute a command on a remote host via SCP. /// /// Returns the raw Output from the SCP command. -pub fn scp_download( +pub(crate) fn scp_download( worker: &WorkerEntry, remote_path: &str, local_path: &str, @@ -201,7 +201,11 @@ pub fn scp_download( /// Execute operations in parallel across multiple workers. /// /// Returns results in the same order as the input workers. -pub fn parallel_execute(workers: &[WorkerEntry], operation: F, max_parallel: usize) -> Vec +pub(crate) fn parallel_execute( + workers: &[WorkerEntry], + operation: F, + max_parallel: usize, +) -> Vec where F: Fn(&WorkerEntry) -> R + Send + Sync + Clone + 'static, R: Send + 'static, @@ -276,7 +280,7 @@ where /// Verify that all workers have matching torc versions. /// /// Returns Ok if all versions match, or an error with details about mismatches. -pub fn verify_all_versions( +pub(crate) fn verify_all_versions( workers: &[WorkerEntry], local_version: &str, max_parallel: usize, @@ -307,7 +311,10 @@ pub fn verify_all_versions( /// Check SSH connectivity to all workers. /// /// Returns Ok if all workers are reachable, or an error with details. -pub fn check_all_connectivity(workers: &[WorkerEntry], max_parallel: usize) -> Result<(), String> { +pub(crate) fn check_all_connectivity( + workers: &[WorkerEntry], + max_parallel: usize, +) -> Result<(), String> { info!( "Checking SSH connectivity to {} worker(s)...", workers.len() diff --git a/src/client/remote/types.rs b/src/client/remote/types.rs index 5e7addafa..84325fbf7 100644 --- a/src/client/remote/types.rs +++ b/src/client/remote/types.rs @@ -6,7 +6,7 @@ use std::fmt; #[derive(Debug, Clone, PartialEq, Eq)] pub struct WorkerEntry { /// Original line from file (for error messages) - pub original: String, + pub(crate) original: String, /// Username (optional, defaults to current user if not specified) pub user: Option, /// Hostname or IP address @@ -28,19 +28,19 @@ impl WorkerEntry { } /// Set the user for this entry. - pub fn with_user(mut self, user: impl Into) -> Self { + fn with_user(mut self, user: impl Into) -> Self { self.user = Some(user.into()); self } /// Set the port for this entry. - pub fn with_port(mut self, port: u16) -> Self { + fn with_port(mut self, port: u16) -> Self { self.port = Some(port); self } /// Returns the SSH target string: [user@]host - pub fn ssh_target(&self) -> String { + pub(crate) fn ssh_target(&self) -> String { match &self.user { Some(user) => format!("{}@{}", user, self.host), None => self.host.clone(), @@ -48,7 +48,7 @@ impl WorkerEntry { } /// Returns the display name for this worker (used in output). - pub fn display_name(&self) -> &str { + pub(crate) fn display_name(&self) -> &str { &self.host } } @@ -95,16 +95,16 @@ impl fmt::Display for RemoteWorkerState { #[derive(Debug)] pub struct RemoteOperationResult { /// The worker this result is for - pub worker: WorkerEntry, + pub(crate) worker: WorkerEntry, /// Whether the operation succeeded - pub success: bool, + pub(crate) success: bool, /// Human-readable message about the result - pub message: String, + pub(crate) message: String, } impl RemoteOperationResult { /// Create a successful result. - pub fn success(worker: WorkerEntry, message: impl Into) -> Self { + pub(crate) fn success(worker: WorkerEntry, message: impl Into) -> Self { Self { worker, success: true, @@ -113,7 +113,7 @@ impl RemoteOperationResult { } /// Create a failed result. - pub fn failure(worker: WorkerEntry, message: impl Into) -> Self { + pub(crate) fn failure(worker: WorkerEntry, message: impl Into) -> Self { Self { worker, success: false, diff --git a/src/client/remote/worker_file.rs b/src/client/remote/worker_file.rs index ec7e59fb3..1c7c644c5 100644 --- a/src/client/remote/worker_file.rs +++ b/src/client/remote/worker_file.rs @@ -21,7 +21,7 @@ use super::types::WorkerEntry; /// user@192.168.1.10 /// admin@server.local:2222 /// ``` -pub fn parse_worker_file(path: &Path) -> Result, String> { +pub(crate) fn parse_worker_file(path: &Path) -> Result, String> { let content = fs::read_to_string(path) .map_err(|e| format!("Failed to read worker file '{}': {}", path.display(), e))?; diff --git a/src/client/report_models.rs b/src/client/report_models.rs index c34478e32..a1ae07e83 100644 --- a/src/client/report_models.rs +++ b/src/client/report_models.rs @@ -33,12 +33,12 @@ fn is_zero(n: &usize) -> bool { /// A resource utilization violation (job exceeded its specified resources) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResourceViolation { - pub job_id: i64, - pub job_name: String, - pub resource_type: String, - pub specified: String, - pub peak_used: String, - pub over_utilization: String, + pub(crate) job_id: i64, + pub(crate) job_name: String, + pub(crate) resource_type: String, + pub(crate) specified: String, + pub(crate) peak_used: String, + pub(crate) over_utilization: String, } /// Information about a job that exceeded resource allocation. @@ -48,57 +48,57 @@ pub struct ResourceViolation { /// Used for proactive resource optimization and recovery diagnostics. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResourceViolationInfo { - pub job_id: i64, - pub job_name: String, - pub return_code: i64, - pub exec_time_minutes: f64, - pub configured_memory: String, - pub configured_runtime: String, - pub configured_cpus: i64, + pub(crate) job_id: i64, + pub(crate) job_name: String, + pub(crate) return_code: i64, + pub(crate) exec_time_minutes: f64, + pub(crate) configured_memory: String, + pub(crate) configured_runtime: String, + pub(crate) configured_cpus: i64, /// Peak memory usage in bytes (if available from resource monitoring) #[serde(default, skip_serializing_if = "Option::is_none")] - pub peak_memory_bytes: Option, + pub(crate) peak_memory_bytes: Option, /// Human-readable peak memory (e.g., "1.5 GB") #[serde(default, skip_serializing_if = "Option::is_none")] - pub peak_memory_formatted: Option, + pub(crate) peak_memory_formatted: Option, /// Whether this job violated memory limits #[serde(default, skip_serializing_if = "is_false")] - pub memory_violation: bool, + pub(crate) memory_violation: bool, /// Reason for OOM detection (e.g., "memory_exceeded", "sigkill_137") #[serde(default, skip_serializing_if = "Option::is_none")] - pub oom_reason: Option, + pub(crate) oom_reason: Option, /// How much memory was over-utilized (e.g., "+25.3%") #[serde(default, skip_serializing_if = "Option::is_none")] - pub memory_over_utilization: Option, + pub(crate) memory_over_utilization: Option, /// Whether this job likely failed due to timeout #[serde(default, skip_serializing_if = "is_false")] - pub likely_timeout: bool, + pub(crate) likely_timeout: bool, /// Reason for timeout detection (e.g., "sigxcpu_152") #[serde(default, skip_serializing_if = "Option::is_none")] - pub timeout_reason: Option, + pub(crate) timeout_reason: Option, /// Runtime utilization percentage (e.g., "95.2%") #[serde(default, skip_serializing_if = "Option::is_none")] - pub runtime_utilization: Option, + pub(crate) runtime_utilization: Option, /// Whether this job exceeded its CPU allocation #[serde(default, skip_serializing_if = "is_false")] - pub likely_cpu_violation: bool, + pub(crate) likely_cpu_violation: bool, /// Peak CPU percentage used (e.g., 501.4%) #[serde(default, skip_serializing_if = "Option::is_none")] - pub peak_cpu_percent: Option, + pub(crate) peak_cpu_percent: Option, /// Whether this job exceeded its runtime allocation #[serde(default, skip_serializing_if = "is_false")] - pub likely_runtime_violation: bool, + pub(crate) likely_runtime_violation: bool, } fn is_false(b: &bool) -> bool { @@ -108,58 +108,58 @@ fn is_false(b: &bool) -> bool { /// Output of `torc reports results` #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResultsReport { - pub workflow_id: i64, - pub workflow_name: String, - pub workflow_user: String, - pub all_runs: bool, - pub total_results: usize, + pub(crate) workflow_id: i64, + pub(crate) workflow_name: String, + pub(crate) workflow_user: String, + pub(crate) all_runs: bool, + pub(crate) total_results: usize, /// Job result records. /// /// Serialized as `items` for consistency with the other list commands and /// the REST API's paginated responses (which all wrap records in `items`). #[serde(rename = "items")] - pub results: Vec, + pub(crate) results: Vec, } /// A single job result record with log file paths #[derive(Debug, Clone, Serialize, Deserialize)] pub struct JobResultRecord { - pub job_id: i64, - pub job_name: String, - pub status: String, - pub run_id: i64, - pub return_code: i64, - pub completion_time: String, - pub exec_time_minutes: f64, - pub compute_node_id: i64, + pub(crate) job_id: i64, + pub(crate) job_name: String, + pub(crate) status: String, + pub(crate) run_id: i64, + pub(crate) return_code: i64, + pub(crate) completion_time: String, + pub(crate) exec_time_minutes: f64, + pub(crate) compute_node_id: i64, /// Path to job stdout log #[serde(default, skip_serializing_if = "Option::is_none")] - pub job_stdout: Option, + pub(crate) job_stdout: Option, /// Path to job stderr log #[serde(default, skip_serializing_if = "Option::is_none")] - pub job_stderr: Option, + pub(crate) job_stderr: Option, /// Type of compute node ("local" or "slurm") #[serde(default, skip_serializing_if = "Option::is_none")] - pub compute_node_type: Option, + pub(crate) compute_node_type: Option, /// Path to job runner log file #[serde(default, skip_serializing_if = "Option::is_none")] - pub job_runner_log: Option, + pub(crate) job_runner_log: Option, /// Slurm job ID (only for slurm jobs) #[serde(default, skip_serializing_if = "Option::is_none")] - pub slurm_job_id: Option, + pub(crate) slurm_job_id: Option, /// Path to Slurm stdout log (only for slurm jobs) #[serde(default, skip_serializing_if = "Option::is_none")] - pub slurm_stdout: Option, + pub(crate) slurm_stdout: Option, /// Path to Slurm stderr log (only for slurm jobs) #[serde(default, skip_serializing_if = "Option::is_none")] - pub slurm_stderr: Option, + pub(crate) slurm_stderr: Option, } #[cfg(test)] diff --git a/src/client/resource_correction.rs b/src/client/resource_correction.rs index 1d65407ae..5a8c638fa 100644 --- a/src/client/resource_correction.rs +++ b/src/client/resource_correction.rs @@ -43,10 +43,10 @@ pub struct ResourceCorrectionOptions { #[derive(Debug, Clone, Serialize, Default)] pub struct ResourceCorrectionResult { pub resource_requirements_updated: usize, - pub jobs_analyzed: usize, - pub memory_corrections: usize, - pub runtime_corrections: usize, - pub cpu_corrections: usize, + pub(crate) jobs_analyzed: usize, + pub(crate) memory_corrections: usize, + pub(crate) runtime_corrections: usize, + pub(crate) cpu_corrections: usize, pub downsize_memory_corrections: usize, pub downsize_runtime_corrections: usize, pub downsize_cpu_corrections: usize, @@ -176,44 +176,44 @@ struct DownsizeCandidate { #[derive(Debug, Clone, Serialize)] pub struct ResourceAdjustmentReport { /// The resource_requirements_id being adjusted - pub resource_requirements_id: i64, + pub(crate) resource_requirements_id: i64, /// Direction of adjustment: "upscale" or "downscale" pub direction: String, /// Job IDs that share this resource requirement - pub job_ids: Vec, + pub(crate) job_ids: Vec, /// Job names for reference - pub job_names: Vec, + pub(crate) job_names: Vec, /// Whether memory was adjusted pub memory_adjusted: bool, /// Original memory setting #[serde(skip_serializing_if = "Option::is_none")] - pub original_memory: Option, + pub(crate) original_memory: Option, /// New memory setting #[serde(skip_serializing_if = "Option::is_none")] - pub new_memory: Option, + pub(crate) new_memory: Option, /// Maximum peak memory observed (bytes) #[serde(skip_serializing_if = "Option::is_none")] - pub max_peak_memory_bytes: Option, + max_peak_memory_bytes: Option, /// Whether runtime was adjusted pub runtime_adjusted: bool, /// Original runtime setting #[serde(skip_serializing_if = "Option::is_none")] - pub original_runtime: Option, + pub(crate) original_runtime: Option, /// New runtime setting #[serde(skip_serializing_if = "Option::is_none")] - pub new_runtime: Option, + pub(crate) new_runtime: Option, /// Whether CPU was adjusted #[serde(default, skip_serializing_if = "is_false")] pub cpu_adjusted: bool, /// Original CPU count #[serde(skip_serializing_if = "Option::is_none")] - pub original_cpus: Option, + pub(crate) original_cpus: Option, /// New CPU count #[serde(skip_serializing_if = "Option::is_none")] - pub new_cpus: Option, + pub(crate) new_cpus: Option, /// Maximum peak CPU percentage observed #[serde(skip_serializing_if = "Option::is_none")] - pub max_peak_cpu_percent: Option, + max_peak_cpu_percent: Option, } fn is_false(b: &bool) -> bool { @@ -255,7 +255,7 @@ struct ResourceAdjustment { /// Format bytes to memory string (e.g., "12g", "512m") /// Uses ceiling division to ensure sufficient memory allocation -pub fn format_memory_bytes_short(bytes: u64) -> String { +pub(crate) fn format_memory_bytes_short(bytes: u64) -> String { const GB: u64 = 1024 * 1024 * 1024; const MB: u64 = 1024 * 1024; const KB: u64 = 1024; @@ -272,7 +272,7 @@ pub fn format_memory_bytes_short(bytes: u64) -> String { } /// Format seconds to ISO8601 duration (e.g., "PT2H30M") -pub fn format_duration_iso8601(secs: u64) -> String { +pub(crate) fn format_duration_iso8601(secs: u64) -> String { let hours = secs / 3600; let mins = (secs % 3600) / 60; if hours > 0 && mins > 0 { diff --git a/src/client/resource_monitor.rs b/src/client/resource_monitor.rs index c3e983065..dbec012a5 100644 --- a/src/client/resource_monitor.rs +++ b/src/client/resource_monitor.rs @@ -19,13 +19,13 @@ const DB_FILENAME_PREFIX: &str = "resource_metrics"; #[derive(Debug, Clone)] pub struct OomViolation { /// PID of the job process (used to identify the job in running_jobs map). - pub pid: u32, + pub(crate) pid: u32, /// Torc job ID. - pub job_id: i64, + pub(crate) job_id: i64, /// Current memory usage in bytes. - pub memory_bytes: u64, + pub(crate) memory_bytes: u64, /// Configured memory limit in bytes. - pub limit_bytes: u64, + pub(crate) limit_bytes: u64, } // `ResourceMonitorConfig` (struct + impl) and its sub-types now live in @@ -38,7 +38,7 @@ pub use crate::models::{ /// Returns the path of the time-series metrics database that would be produced for the /// given `output_dir` / `unique_label`. This mirrors the layout created by /// `init_timeseries_db` so callers (e.g. post-run plot generation) can locate the file. -pub fn timeseries_db_path(output_dir: &Path, unique_label: &str) -> PathBuf { +fn timeseries_db_path(output_dir: &Path, unique_label: &str) -> PathBuf { output_dir .join("resource_utilization") .join(format!("{}_{}.db", DB_FILENAME_PREFIX, unique_label)) @@ -47,10 +47,10 @@ pub fn timeseries_db_path(output_dir: &Path, unique_label: &str) -> PathBuf { /// Metrics collected for a single job #[derive(Debug, Clone)] pub struct JobMetrics { - pub peak_memory_bytes: u64, - pub avg_memory_bytes: u64, - pub peak_cpu_percent: f64, - pub avg_cpu_percent: f64, + pub(crate) peak_memory_bytes: u64, + pub(crate) avg_memory_bytes: u64, + pub(crate) peak_cpu_percent: f64, + pub(crate) avg_cpu_percent: f64, sample_count: usize, total_memory_bytes: u64, total_cpu_percent: f64, @@ -103,11 +103,11 @@ impl JobMetrics { #[derive(Debug, Clone)] pub struct SystemMetricsSummary { - pub sample_count: i64, - pub peak_cpu_percent: f64, - pub avg_cpu_percent: f64, - pub peak_memory_bytes: u64, - pub avg_memory_bytes: u64, + pub(crate) sample_count: i64, + pub(crate) peak_cpu_percent: f64, + pub(crate) avg_cpu_percent: f64, + pub(crate) peak_memory_bytes: u64, + pub(crate) avg_memory_bytes: u64, } /// Metrics collected for the whole system while this runner is active. @@ -257,7 +257,7 @@ pub struct ResourceMonitor { impl ResourceMonitor { /// Create a new resource monitor - pub fn new( + pub(crate) fn new( config: ResourceMonitorConfig, output_dir: PathBuf, unique_label: String, @@ -304,17 +304,17 @@ impl ResourceMonitor { } /// Path to the time-series metrics DB, or `None` if no time-series scope is enabled. - pub fn timeseries_db_path(&self) -> Option<&Path> { + pub(crate) fn timeseries_db_path(&self) -> Option<&Path> { self.db_path.as_deref() } /// Whether the workflow requested post-run plot generation. - pub fn generate_plots(&self) -> bool { + pub(crate) fn generate_plots(&self) -> bool { self.config.generate_plots } /// Returns `true` when the monitor is configured for `TimeSeries` granularity. - pub fn is_time_series(&self) -> bool { + pub(crate) fn is_time_series(&self) -> bool { matches!( self.config.jobs_config().granularity, MonitorGranularity::TimeSeries @@ -322,7 +322,7 @@ impl ResourceMonitor { } /// Returns `true` when per-job monitoring is enabled. - pub fn jobs_enabled(&self) -> bool { + pub(crate) fn jobs_enabled(&self) -> bool { self.config.jobs_config().enabled } @@ -330,7 +330,7 @@ impl ResourceMonitor { /// /// If `memory_limit_bytes` is set and the job exceeds this limit, an OOM violation /// will be sent via [`recv_oom_violations()`]. - pub fn start_monitoring( + pub(crate) fn start_monitoring( &self, pid: u32, job_id: i64, @@ -354,7 +354,7 @@ impl ResourceMonitor { /// /// Returns a vector of jobs that have exceeded their memory limits. /// The job runner should kill these jobs and mark them as OOM-killed. - pub fn recv_oom_violations(&self) -> Vec { + pub(crate) fn recv_oom_violations(&self) -> Vec { let mut violations = Vec::new(); loop { match self.oom_rx.try_recv() { @@ -380,7 +380,7 @@ impl ResourceMonitor { /// /// `pid` must be the srun process PID so that the existing `stop_monitoring(pid)` API /// continues to work without changes. - pub fn start_monitoring_slurm( + pub(crate) fn start_monitoring_slurm( &self, pid: u32, slurm_job_id: String, @@ -408,7 +408,7 @@ impl ResourceMonitor { /// /// Sends a stop command to the monitoring thread and waits for it to return /// the collected metrics via a response channel, with a 5-second timeout. - pub fn stop_monitoring(&self, pid: u32) -> Option { + pub(crate) fn stop_monitoring(&self, pid: u32) -> Option { let (response_tx, response_rx) = channel(); if let Err(e) = self .tx @@ -431,7 +431,7 @@ impl ResourceMonitor { } /// Shutdown the monitoring thread and return compute-node summary metrics, if collected. - pub fn shutdown(self) -> Option { + pub(crate) fn shutdown(self) -> Option { let (response_tx, response_rx) = channel(); if let Err(e) = self.tx.send(MonitorCommand::Shutdown { response_tx }) { error!("Failed to send shutdown command: {}", e); @@ -1079,7 +1079,7 @@ fn collect_process_tree_stats(root_pid: u32, sys: &System) -> (f64, u64, usize) /// Discover the numeric step ID for a named step, retrying a few times for Slurm registration. /// /// Called at srun launch time. Returns `None` if the step doesn't appear within ~1 second. -pub fn discover_step_id_with_retries(slurm_job_id: &str, step_name: &str) -> Option { +pub(crate) fn discover_step_id_with_retries(slurm_job_id: &str, step_name: &str) -> Option { for attempt in 0..5 { let map = discover_step_ids(slurm_job_id); if let Some(id) = map.get(step_name) { diff --git a/src/client/ro_crate_utils.rs b/src/client/ro_crate_utils.rs index 2903e7971..8a90b5059 100644 --- a/src/client/ro_crate_utils.rs +++ b/src/client/ro_crate_utils.rs @@ -32,14 +32,14 @@ fn metadata_value_to_map(value: &serde_json::Value) -> HashMap bool { +pub(crate) fn is_reserved_entity_id(id: &str) -> bool { RESERVED_ENTITY_IDS.contains(&id) || RESERVED_ENTITY_ID_PREFIXES .iter() @@ -64,7 +64,7 @@ fn refs_value(ids: &[String]) -> Option { /// /// Returns the hash as a lowercase hexadecimal string, or None if the file /// cannot be read. -pub fn compute_file_sha256(path: &str) -> Option { +fn compute_file_sha256(path: &str) -> Option { let file = match File::open(path) { Ok(f) => f, Err(e) => { @@ -107,7 +107,7 @@ pub fn compute_file_sha256(path: &str) -> Option { /// `identifier_override` carries a user-supplied stable identifier (DOI, PURL, URN, /// ...) for input files; see [`FileSpec::identifier`]. When None, the @id falls back /// to the file path to preserve the original behaviour. -pub fn build_file_entity( +fn build_file_entity( workflow_id: i64, file: &FileModel, content_size: Option, @@ -174,7 +174,7 @@ pub fn build_file_entity( /// /// For output files, includes `prov:wasGeneratedBy` linking to the job's CreateAction entity. #[allow(clippy::too_many_arguments)] -pub fn build_file_entity_with_provenance( +fn build_file_entity_with_provenance( workflow_id: i64, run_id: i64, file: &FileModel, @@ -248,7 +248,7 @@ pub fn build_file_entity_with_provenance( /// - `prov:hadPlan`: reference to the workflow plan entity /// - `instrument`: reference to the run-specific software agent /// - `result`: references to output file entities -pub fn build_create_action_entity( +fn build_create_action_entity( workflow_id: i64, run_id: i64, job: &JobModel, @@ -368,7 +368,7 @@ fn parse_entity_datetime(entity: &RoCrateEntityModel, field: &str) -> Option bool { /// Returns `Some("#job-{id}-attempt-{attempt}")` on success so the caller can /// wire the dataset's `prov:wasGeneratedBy`. This is a non-blocking operation: /// warnings are logged and `None` is returned on failure. -pub fn link_dataset_to_job_create_action( +pub(crate) fn link_dataset_to_job_create_action( config: &Configuration, workflow_id: i64, run_id: i64, @@ -882,7 +882,7 @@ fn append_dataset_result( /// /// Returns an error if the entity cannot be created -- callers should roll back /// the workflow on failure, the same way they do for other creation steps. -pub fn create_input_file_entity_with_identifier( +pub(crate) fn create_input_file_entity_with_identifier( config: &Configuration, workflow_id: i64, file: &FileModel, @@ -916,7 +916,7 @@ pub fn create_input_file_entity_with_identifier( /// /// Called during workflow initialization when `enable_ro_crate` is true. /// Input files are identified as files with `st_mtime` set (they exist before the workflow runs). -pub fn create_entities_for_input_files( +pub(crate) fn create_entities_for_input_files( config: &Configuration, workflow_id: i64, files: &[FileModel], @@ -998,7 +998,7 @@ fn build_software_entity( /// /// This is called during workflow initialization regardless of `enable_ro_crate`. /// The `run_id` is included in each entity to distinguish software records across runs. -pub fn create_software_entities(config: &Configuration, workflow_id: i64, run_id: i64) { +pub(crate) fn create_software_entities(config: &Configuration, workflow_id: i64, run_id: i64) { let mut binary_names: Vec<&str> = vec!["torc"]; // torc-slurm-job-runner is only available on Linux diff --git a/src/client/scheduler_plan.rs b/src/client/scheduler_plan.rs index 46e69f68f..43cda4bba 100644 --- a/src/client/scheduler_plan.rs +++ b/src/client/scheduler_plan.rs @@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize}; /// /// When both groups exist, each needs at least 1 allocation, so the minimum total is 2. /// A threshold of 2 means we merge when exactly 2 allocations are needed (the minimum case). -pub const MERGE_THRESHOLD: i64 = 2; +const MERGE_THRESHOLD: i64 = 2; use crate::client::hpc::HpcProfile; use crate::client::workflow_graph::{SchedulerGroup, WorkflowGraph}; @@ -157,52 +157,52 @@ fn calculate_walltime( #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PlannedScheduler { /// Scheduler name (includes suffix like "_deferred" for jobs with dependencies) - pub name: String, + pub(crate) name: String, /// Slurm account - pub account: String, + pub(crate) account: String, /// Partition (if explicit request required for sbatch --partition flag) pub partition: Option, /// Resolved partition name (always set, used for cluster state queries and display) - pub resolved_partition: String, + pub(crate) resolved_partition: String, /// Memory request - pub mem: Option, + pub(crate) mem: Option, /// Walltime in HH:MM:SS format pub walltime: String, /// Nodes per allocation - pub nodes: i64, + pub(crate) nodes: i64, /// GPU gres string (e.g., "gpu:2") - pub gres: Option, + pub(crate) gres: Option, /// QOS - pub qos: Option, + pub(crate) qos: Option, /// Resource requirements name this scheduler is for - pub resource_requirements: String, + pub(crate) resource_requirements: String, /// Whether this scheduler is for jobs with dependencies - pub has_dependencies: bool, + pub(crate) has_dependencies: bool, /// Number of jobs this scheduler will handle - pub job_count: usize, + pub(crate) job_count: usize, /// Job names that will use this scheduler - pub job_names: Vec, + pub(crate) job_names: Vec, /// Job name patterns for action matching - pub job_name_patterns: Vec, + job_name_patterns: Vec, /// Number of allocations to create - pub num_allocations: i64, + pub(crate) num_allocations: i64, } /// A planned workflow action for scheduling nodes. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PlannedAction { /// Trigger type: "on_workflow_start" or "on_jobs_ready" - pub trigger_type: String, + trigger_type: String, /// Scheduler name this action references - pub scheduler_name: String, + pub(crate) scheduler_name: String, /// Exact job names for this action (preferred over patterns for expanded jobs) - pub job_names: Option>, + pub(crate) job_names: Option>, /// Job name regex patterns (for on_jobs_ready triggers with unexpanded parameterized jobs) - pub job_name_patterns: Option>, + pub(crate) job_name_patterns: Option>, /// Number of allocations to submit - pub num_allocations: i64, + pub(crate) num_allocations: i64, /// Whether this is a recovery action (ephemeral, deleted on reinitialize) - pub is_recovery: bool, + pub(crate) is_recovery: bool, } /// A complete scheduler plan for a workflow. @@ -214,16 +214,16 @@ pub struct SchedulerPlan { /// Schedulers to create pub schedulers: Vec, /// Actions to create - pub actions: Vec, + pub(crate) actions: Vec, /// Map of job name -> scheduler name - pub job_assignments: HashMap, + pub(crate) job_assignments: HashMap, /// Warnings generated during planning pub warnings: Vec, } impl SchedulerPlan { /// Create an empty plan - pub fn new() -> Self { + fn new() -> Self { Self { schedulers: Vec::new(), actions: Vec::new(), @@ -1021,7 +1021,7 @@ use crate::client::workflow_spec::{SlurmSchedulerSpec, WorkflowActionSpec, Workf /// /// This adds the planned schedulers and actions to the spec, and updates /// job scheduler assignments. -pub fn apply_plan_to_spec(plan: &SchedulerPlan, spec: &mut WorkflowSpec) { +pub(crate) fn apply_plan_to_spec(plan: &SchedulerPlan, spec: &mut WorkflowSpec) { // Convert planned schedulers to SlurmSchedulerSpec let schedulers: Vec = plan .schedulers diff --git a/src/client/sse_client.rs b/src/client/sse_client.rs index c689dcbb0..98e03696f 100644 --- a/src/client/sse_client.rs +++ b/src/client/sse_client.rs @@ -15,13 +15,13 @@ use std::time::Duration; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SseEvent { /// The workflow ID this event belongs to. - pub workflow_id: i64, + pub(crate) workflow_id: i64, /// Timestamp in milliseconds since Unix epoch. - pub timestamp: i64, + pub(crate) timestamp: i64, /// The type of event (e.g., "job_started", "job_completed", "job_failed"). pub event_type: String, /// The severity level of the event. - pub severity: EventSeverity, + pub(crate) severity: EventSeverity, /// Event-specific data as JSON. pub data: serde_json::Value, } @@ -211,7 +211,7 @@ impl SseConnection { /// /// This is a convenience function that handles the connection loop and /// calls the provided callback for each received event. -pub fn stream_events( +fn stream_events( config: &Configuration, workflow_id: i64, level: Option, diff --git a/src/client/utils.rs b/src/client/utils.rs index 70cc61fae..066d71c56 100644 --- a/src/client/utils.rs +++ b/src/client/utils.rs @@ -53,7 +53,7 @@ const PING_INTERVAL_SECONDS: u64 = 30; /// .arg("echo hello") /// .output()?; /// ``` -pub fn shell_command() -> Command { +pub(crate) fn shell_command() -> Command { if cfg!(target_os = "windows") { let mut cmd = Command::new("cmd"); cmd.arg("/C"); @@ -77,7 +77,7 @@ pub fn shell_command() -> Command { /// /// Silent when `submission_directory` is unset (older workflows) or matches /// the current directory. -pub fn warn_if_cwd_differs_from_submission_directory( +pub(crate) fn warn_if_cwd_differs_from_submission_directory( submission_directory: Option<&str>, command_name: &str, ) { @@ -110,7 +110,7 @@ pub fn warn_if_cwd_differs_from_submission_directory( /// `TORC_WORKFLOW_SUBMISSION_DIR`. Returns `None` (with a logged warning) if /// the CWD cannot be read or contains non-UTF-8 characters; the workflow /// still gets created in that case. -pub fn capture_submission_directory() -> Option { +pub(crate) fn capture_submission_directory() -> Option { match std::env::current_dir() { Ok(path) => match path.to_str() { Some(s) => Some(s.to_string()), @@ -369,7 +369,7 @@ where /// * `Ok(true)` - Successfully claimed the action /// * `Ok(false)` - Action was already claimed by another compute node /// * `Err(_)` - An error occurred during the claim attempt -pub fn claim_action( +pub(crate) fn claim_action( config: &Configuration, workflow_id: i64, action_id: i64, @@ -414,7 +414,7 @@ pub fn claim_action( /// let num_gpus = detect_nvidia_gpus(); /// println!("Detected {} NVIDIA GPU(s)", num_gpus); /// ``` -pub fn detect_nvidia_gpus() -> i64 { +pub(crate) fn detect_nvidia_gpus() -> i64 { match nvml_wrapper::Nvml::init() { Ok(nvml) => match nvml.device_count() { Ok(count) => { @@ -598,7 +598,7 @@ fn parse_dmesg_timestamp(line: &str) -> Option> { /// Display format used everywhere humans see timestamps in the CLI/TUI/dash: /// `YYYY-MM-DD HH:MM:SS ±HHMM` in the client's local timezone. -pub const HUMAN_TIMESTAMP_FORMAT: &str = "%Y-%m-%d %H:%M:%S %z"; +const HUMAN_TIMESTAMP_FORMAT: &str = "%Y-%m-%d %H:%M:%S %z"; /// Render an RFC3339 timestamp (as returned by the server) as a local-time /// string with an explicit ±HHMM offset. Returns the input verbatim if it @@ -606,7 +606,7 @@ pub const HUMAN_TIMESTAMP_FORMAT: &str = "%Y-%m-%d %H:%M:%S %z"; /// /// JSON output should keep the raw RFC3339 UTC value — only call this when /// rendering for human consumption (tables, detail views). -pub fn format_local_timestamp(rfc3339_utc: &str) -> String { +pub(crate) fn format_local_timestamp(rfc3339_utc: &str) -> String { match DateTime::parse_from_rfc3339(rfc3339_utc) { Ok(dt) => dt .with_timezone(&Local) @@ -636,7 +636,7 @@ pub fn parse_finite_non_negative_secs(s: &str) -> Result { /// Same as [`format_local_timestamp`] but for unix-epoch seconds (e.g. /// `file.st_mtime`). -pub fn format_local_timestamp_epoch(epoch_secs: f64) -> String { +pub(crate) fn format_local_timestamp_epoch(epoch_secs: f64) -> String { // Converting (secs: i64, nsecs: u32) via `from_timestamp` is fragile here: // float rounding can push `nsecs` to 1_000_000_000, and pre-epoch values // give negative fractional nsecs that underflow the u32 cast. Both make diff --git a/src/client/version_check.rs b/src/client/version_check.rs index b8884eee8..dd6b67ab7 100644 --- a/src/client/version_check.rs +++ b/src/client/version_check.rs @@ -12,7 +12,7 @@ use crate::client::apis; use crate::client::apis::configuration::Configuration; /// The current version of this binary, set at compile time. -pub const CLIENT_VERSION: &str = env!("CARGO_PKG_VERSION"); +const CLIENT_VERSION: &str = env!("CARGO_PKG_VERSION"); /// The API version that this client expects from the server. /// @@ -23,15 +23,15 @@ pub const CLIENT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const CLIENT_API_VERSION: &str = crate::api_version::HTTP_API_VERSION; /// The git commit hash of this binary, set at compile time via build.rs. -pub const GIT_HASH: &str = env!("GIT_HASH"); +pub(crate) const GIT_HASH: &str = env!("GIT_HASH"); /// Returns the full version string including git hash (e.g., "0.8.0 (abc1234)") -pub fn full_version() -> String { +pub(crate) fn full_version() -> String { format!("{} ({})", CLIENT_VERSION, GIT_HASH) } /// Returns just the version with git hash suffix (e.g., "0.8.0-abc1234") -pub fn version_with_hash() -> String { +fn version_with_hash() -> String { format!("{}-{}", CLIENT_VERSION, GIT_HASH) } @@ -73,7 +73,7 @@ pub struct ServerInfo { #[derive(Debug, Clone)] pub struct VersionCheckResult { /// The client binary version. - pub client_version: String, + client_version: String, /// The server binary version (if successfully retrieved). pub server_version: Option, /// The client API version. @@ -88,7 +88,7 @@ pub struct VersionCheckResult { impl VersionCheckResult { /// Creates a new result for when the server couldn't be reached. - pub fn server_unreachable() -> Self { + fn server_unreachable() -> Self { Self { client_version: CLIENT_VERSION.to_string(), server_version: None, @@ -279,7 +279,7 @@ fn format_legacy_version_message( } /// Fetches server information from the /version endpoint. -pub fn get_server_info(config: &Configuration) -> Option { +fn get_server_info(config: &Configuration) -> Option { match apis::system_api::get_version(config) { Ok(value) => Some(ServerInfo { version: value.version, @@ -291,7 +291,7 @@ pub fn get_server_info(config: &Configuration) -> Option { /// Fetches the server version string from the API. /// Returns the binary version for display purposes (e.g., in log messages). -pub fn get_server_version(config: &Configuration) -> Option { +fn get_server_version(config: &Configuration) -> Option { get_server_info(config).map(|info| info.version) } @@ -320,7 +320,7 @@ pub fn print_version_warning(result: &VersionCheckResult) -> VersionMismatchSeve /// Checks the server version and prints appropriate warnings. /// Returns true if the version check passed (no major incompatibility). -pub fn check_and_warn(config: &Configuration) -> bool { +pub(crate) fn check_and_warn(config: &Configuration) -> bool { let result = check_version(config); let severity = print_version_warning(&result); !severity.is_blocking() diff --git a/src/client/workflow_graph.rs b/src/client/workflow_graph.rs index 5738b4925..b7338ede2 100644 --- a/src/client/workflow_graph.rs +++ b/src/client/workflow_graph.rs @@ -21,43 +21,43 @@ use crate::models::{JobModel, ResourceRequirementsModel}; #[derive(Debug, Clone)] pub struct JobNode { /// Job name (may contain parameter placeholders like `{index}`) - pub name: String, + name: String, /// Resource requirements name - pub resource_requirements: Option, + resource_requirements: Option, /// Number of job instances (1 for non-parameterized, N for parameterized) - pub instance_count: usize, + pub(crate) instance_count: usize, /// Regex pattern matching all instances of this job - pub name_pattern: String, + name_pattern: String, /// Assigned scheduler name - pub scheduler: Option, + scheduler: Option, /// Original job spec reference data - pub command: String, + command: String, } /// Represents a group of jobs that share scheduling characteristics #[derive(Debug, Clone)] pub struct SchedulerGroup { /// Resource requirements name - pub resource_requirements: String, + pub(crate) resource_requirements: String, /// Whether jobs in this group have dependencies - pub has_dependencies: bool, + pub(crate) has_dependencies: bool, /// Total job count across all jobs in this group - pub job_count: usize, + pub(crate) job_count: usize, /// Job name patterns for matching (regex patterns) - pub job_name_patterns: Vec, + pub(crate) job_name_patterns: Vec, /// Job names in this group - pub job_names: Vec, + pub(crate) job_names: Vec, } /// A connected component (independent sub-workflow) within the graph #[derive(Debug, Clone)] pub struct WorkflowComponent { /// Job names in this component - pub jobs: HashSet, + jobs: HashSet, /// Root jobs (no dependencies within the component) - pub roots: Vec, + roots: Vec, /// Leaf jobs (nothing depends on them within the component) - pub leaves: Vec, + leaves: Vec, } /// The main workflow graph structure @@ -77,7 +77,7 @@ pub struct WorkflowGraph { impl WorkflowGraph { /// Create a new empty graph - pub fn new() -> Self { + fn new() -> Self { Self { nodes: HashMap::new(), depends_on: HashMap::new(), @@ -186,7 +186,7 @@ impl WorkflowGraph { /// /// This is used for recovery scenarios and execution plan visualization /// when we don't have access to the original workflow specification. - pub fn from_jobs( + pub(crate) fn from_jobs( jobs: &[JobModel], resource_requirements: &[ResourceRequirementsModel], ) -> Result> { @@ -297,27 +297,27 @@ impl WorkflowGraph { } /// Get all job names in the graph - pub fn job_names(&self) -> impl Iterator { + pub(crate) fn job_names(&self) -> impl Iterator { self.nodes.keys() } /// Get a job node by name - pub fn get_job(&self, name: &str) -> Option<&JobNode> { + pub(crate) fn get_job(&self, name: &str) -> Option<&JobNode> { self.nodes.get(name) } /// Get the number of jobs in the graph - pub fn job_count(&self) -> usize { + pub(crate) fn job_count(&self) -> usize { self.nodes.len() } /// Get total instance count (accounting for parameterized jobs) - pub fn total_instance_count(&self) -> usize { + pub(crate) fn total_instance_count(&self) -> usize { self.nodes.values().map(|n| n.instance_count).sum() } /// Check if a job has any dependencies - pub fn has_dependencies(&self, job: &str) -> bool { + fn has_dependencies(&self, job: &str) -> bool { self.depends_on .get(job) .map(|deps| !deps.is_empty()) @@ -325,17 +325,17 @@ impl WorkflowGraph { } /// Get the jobs that a job depends on (its blockers) - pub fn dependencies_of(&self, job: &str) -> Option<&HashSet> { + pub(crate) fn dependencies_of(&self, job: &str) -> Option<&HashSet> { self.depends_on.get(job) } /// Get the jobs that depend on a job (its dependents) - pub fn dependents_of(&self, job: &str) -> Option<&HashSet> { + fn dependents_of(&self, job: &str) -> Option<&HashSet> { self.depended_by.get(job) } /// Get root jobs (jobs with no dependencies) - pub fn roots(&self) -> Vec<&str> { + pub(crate) fn roots(&self) -> Vec<&str> { self.nodes .keys() .filter(|name| { @@ -349,7 +349,7 @@ impl WorkflowGraph { } /// Get leaf jobs (jobs that nothing depends on) - pub fn leaves(&self) -> Vec<&str> { + fn leaves(&self) -> Vec<&str> { self.nodes .keys() .filter(|name| { @@ -366,7 +366,9 @@ impl WorkflowGraph { /// /// Level 0 contains jobs with no dependencies. /// Level N contains jobs whose dependencies are all in levels < N. - pub fn topological_levels(&mut self) -> Result<&Vec>, Box> { + pub(crate) fn topological_levels( + &mut self, + ) -> Result<&Vec>, Box> { if let Some(ref levels) = self.levels { return Ok(levels); } @@ -406,7 +408,7 @@ impl WorkflowGraph { /// Find connected components (independent sub-workflows) /// /// Each component can be scheduled independently of others. - pub fn connected_components(&mut self) -> &Vec { + pub(crate) fn connected_components(&mut self) -> &Vec { if let Some(ref components) = self.components { return components; } @@ -485,7 +487,7 @@ impl WorkflowGraph { } /// Extract a sub-graph containing only the specified jobs - pub fn subgraph(&self, job_names: &HashSet) -> Self { + fn subgraph(&self, job_names: &HashSet) -> Self { let mut subgraph = Self::new(); // Copy relevant nodes @@ -524,7 +526,7 @@ impl WorkflowGraph { /// /// Jobs are grouped by their resource requirements and dependency status. /// This is used for scheduler generation. - pub fn scheduler_groups(&self) -> Vec { + pub(crate) fn scheduler_groups(&self) -> Vec { // Group by (resource_requirements, has_dependencies) let mut groups: HashMap<(String, bool), SchedulerGroup> = HashMap::new(); @@ -556,9 +558,7 @@ impl WorkflowGraph { /// Generate scheduler groups per connected component /// /// Returns a map of component index to scheduler groups for that component. - pub fn scheduler_groups_by_component( - &mut self, - ) -> Vec<(WorkflowComponent, Vec)> { + fn scheduler_groups_by_component(&mut self) -> Vec<(WorkflowComponent, Vec)> { let components = self.connected_components().clone(); let mut result = Vec::new(); @@ -574,7 +574,7 @@ impl WorkflowGraph { /// Find the critical path (longest path through the graph) /// /// Returns job names along the critical path and the total instance count. - pub fn critical_path(&mut self) -> Result<(Vec, usize), Box> { + fn critical_path(&mut self) -> Result<(Vec, usize), Box> { // Use dynamic programming on topological order let levels = self.topological_levels()?.clone(); @@ -632,7 +632,7 @@ impl WorkflowGraph { } /// Get jobs that become ready when a set of jobs complete - pub fn jobs_unblocked_by(&self, completed_jobs: &HashSet) -> Vec { + pub(crate) fn jobs_unblocked_by(&self, completed_jobs: &HashSet) -> Vec { let mut unblocked = Vec::new(); for (name, deps) in &self.depends_on { @@ -650,7 +650,7 @@ impl WorkflowGraph { } /// Find actions that should trigger when specific jobs become ready - pub fn matching_actions<'a>( + pub(crate) fn matching_actions<'a>( &self, jobs_becoming_ready: &[String], actions: &'a [WorkflowActionSpec], diff --git a/src/client/workflow_manager.rs b/src/client/workflow_manager.rs index 2adfb75c2..574036711 100644 --- a/src/client/workflow_manager.rs +++ b/src/client/workflow_manager.rs @@ -12,9 +12,9 @@ use std::time::UNIX_EPOCH; #[derive(Debug, serde::Serialize)] pub struct InitializationCheck { - pub safe: bool, - pub missing_input_files: Vec, - pub existing_output_files: Vec, + pub(crate) safe: bool, + pub(crate) missing_input_files: Vec, + pub(crate) existing_output_files: Vec, } /// Summary of what `WorkflowManager::start` actually did, so the caller can distinguish a real @@ -65,7 +65,7 @@ impl WorkflowManager { /// Check if initialization is safe to run without executing. /// Returns information about missing input files and existing output files. - pub fn check_initialization(&self) -> Result { + pub(crate) fn check_initialization(&self) -> Result { // Check for missing required input files let missing_input_files = self.get_missing_required_files()?; @@ -570,7 +570,7 @@ impl WorkflowManager { /// Check for existing output files and optionally delete them. /// If delete_files is true, deletes the files. If false, logs warnings only. - pub fn cleanup_output_files(&self, delete_files: bool) -> Result<(), TorcError> { + fn cleanup_output_files(&self, delete_files: bool) -> Result<(), TorcError> { info!( "Checking for existing output files for workflow {}", self.workflow_id @@ -696,7 +696,7 @@ impl WorkflowManager { Ok(()) } - pub fn check_user_data(&self) -> Result<(), TorcError> { + fn check_user_data(&self) -> Result<(), TorcError> { match apis::workflows_api::list_missing_user_data(&self.config, self.workflow_id) { Ok(response) => { if !response.user_data.is_empty() { @@ -984,7 +984,7 @@ impl WorkflowManager { /// Calls the server's process_changed_job_inputs endpoint which computes /// input hashes and resets jobs to Uninitialized if inputs have changed. /// If dry_run is true, log required changes but do not apply them. - pub fn process_changed_user_data(&self, dry_run: bool) -> Result<(), TorcError> { + fn process_changed_user_data(&self, dry_run: bool) -> Result<(), TorcError> { debug!( "Processing changed user_data for workflow {}", self.workflow_id @@ -1253,7 +1253,7 @@ impl WorkflowManager { /// Check that all required existing files for the workflow exist on the filesystem. /// If force is true, log missing files as warnings but don't return an error. /// If force is false, return an error if any required files are missing. - pub fn check_workflow_files(&self, force: bool) -> Result<(), TorcError> { + fn check_workflow_files(&self, force: bool) -> Result<(), TorcError> { // Get list of required existing file IDs let response = match apis::workflows_api::list_required_existing_files(&self.config, self.workflow_id) diff --git a/src/client/workflow_spec.rs b/src/client/workflow_spec.rs index 7a8635953..be05ad852 100644 --- a/src/client/workflow_spec.rs +++ b/src/client/workflow_spec.rs @@ -194,7 +194,7 @@ pub struct ValidationSummary { /// Number of files that would be created pub file_count: usize, /// Number of files before parameter expansion - pub file_count_before_expansion: usize, + pub(crate) file_count_before_expansion: usize, /// Number of user data records that would be created pub user_data_count: usize, /// Number of resource requirements that would be created @@ -220,9 +220,9 @@ use kdl::{KdlDocument, KdlNode}; #[serde(deny_unknown_fields)] pub struct FileSpec { /// Name of the file - pub name: String, + name: String, /// Path to the file - pub path: String, + path: String, /// Optional stable RO-Crate identifier for this file (e.g. a DOI, PURL, or URN). /// When provided, this string is used as the `@id` of the file's RO-Crate entity /// instead of the file path. The path is still recorded as `sameAs` so the @@ -230,36 +230,36 @@ pub struct FileSpec { /// after parameter expansion. Parameter tokens (`{name}` / `{name:fmt}`) are /// substituted into the identifier just like `name` and `path`. #[serde(skip_serializing_if = "Option::is_none")] - pub identifier: Option, + identifier: Option, /// File modification time as Unix timestamp (seconds since epoch). /// If not specified, torc automatically checks if the file exists on disk /// during workflow creation and uses its actual modification time. /// This distinguishes input files (exist before workflow) from output files /// (created by jobs). Used by RO-Crate for automatic entity generation. #[serde(skip_serializing_if = "Option::is_none")] - pub st_mtime: Option, + st_mtime: Option, /// Optional parameters for generating multiple files /// Supports range notation (e.g., "1:100" or "1:100:5") and lists (e.g., "[1,5,10]") #[serde(skip_serializing_if = "Option::is_none")] #[serde(default, deserialize_with = "deserialize_parameter_map")] - pub parameters: Option>, + parameters: Option>, /// How to combine multiple parameters: "product" (default, Cartesian product) or "zip" /// With "zip", parameters are combined element-wise (all must have the same length) #[serde(skip_serializing_if = "Option::is_none")] - pub parameter_mode: Option, + parameter_mode: Option, /// Names of workflow-level parameters to use for this file /// If set, only these parameters from the workflow will be used #[serde(skip_serializing_if = "Option::is_none")] - pub use_parameters: Option>, + use_parameters: Option>, /// Path to a CSV or JSON file supplying parameter combinations as a table. /// Each CSV row / JSON array object becomes one generated file. Mutually /// exclusive with `parameters`, `parameter_mode`, and `use_parameters`. #[serde(skip_serializing_if = "Option::is_none")] - pub parameters_file: Option, + parameters_file: Option, /// Expand this file over the workflow-level `parameters_file` table when set to /// true. Mutually exclusive with the per-file parameter sources above. #[serde(skip_serializing_if = "Option::is_none")] - pub use_parameters_file: Option, + use_parameters_file: Option, } impl FileSpec { @@ -281,7 +281,7 @@ impl FileSpec { /// Expand this FileSpec into multiple FileSpecs based on its parameters /// Returns a single-element vec if no parameters are present - pub fn expand(&self) -> Result, String> { + fn expand(&self) -> Result, String> { let combinations = match build_parameter_combinations( &self.parameters, &self.parameter_mode, @@ -363,7 +363,7 @@ impl UserDataSpec { /// arrays). Non-string JSON values (numbers, bools, null) are not modified, even /// though they could in principle be rewritten -- substitution is string-only, /// matching how FileSpec handles `name` and `path`. - pub fn expand(&self) -> Result, String> { + fn expand(&self) -> Result, String> { let combinations = match build_parameter_combinations( &self.parameters, &self.parameter_mode, @@ -436,31 +436,31 @@ pub struct WorkflowActionSpec { pub action_type: String, /// For on_jobs_ready/on_jobs_complete: exact job names to match #[serde(skip_serializing_if = "Option::is_none")] - pub jobs: Option>, + pub(crate) jobs: Option>, /// For on_jobs_ready/on_jobs_complete: regex patterns to match job names #[serde(skip_serializing_if = "Option::is_none")] - pub job_name_regexes: Option>, + pub(crate) job_name_regexes: Option>, /// For run_commands action: array of commands to execute #[serde(skip_serializing_if = "Option::is_none")] - pub commands: Option>, + pub(crate) commands: Option>, /// For schedule_nodes action: scheduler name (will be translated to scheduler_id) #[serde(skip_serializing_if = "Option::is_none")] pub scheduler: Option, /// For schedule_nodes action: scheduler type (e.g., "slurm", "local") #[serde(skip_serializing_if = "Option::is_none")] - pub scheduler_type: Option, + pub(crate) scheduler_type: Option, /// For schedule_nodes action: number of node allocations to request #[serde(skip_serializing_if = "Option::is_none")] pub num_allocations: Option, /// For schedule_nodes action: whether to start one worker per node #[serde(skip_serializing_if = "Option::is_none")] - pub start_one_worker_per_node: Option, + pub(crate) start_one_worker_per_node: Option, /// For schedule_nodes action: maximum parallel jobs #[serde(skip_serializing_if = "Option::is_none")] - pub max_parallel_jobs: Option, + pub(crate) max_parallel_jobs: Option, /// Whether the action persists and can be claimed by multiple workers (default: false) #[serde(skip_serializing_if = "Option::is_none")] - pub persistent: Option, + pub(crate) persistent: Option, } /// Resource requirements specification for JSON serialization (without workflow_id and id) @@ -500,17 +500,17 @@ impl ResourceRequirementsSpec { pub struct FailureHandlerRuleSpec { /// Exit codes that trigger this rule. Can be omitted if match_all_exit_codes is true. #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub exit_codes: Vec, + exit_codes: Vec, /// If true, this rule matches any non-zero exit code. /// Use this for simple retry-on-any-failure behavior. #[serde(default, skip_serializing_if = "std::ops::Not::not")] - pub match_all_exit_codes: bool, + match_all_exit_codes: bool, /// Optional recovery script to run before retrying #[serde(skip_serializing_if = "Option::is_none")] - pub recovery_script: Option, + recovery_script: Option, /// Maximum number of retry attempts (defaults to 3) #[serde(default = "FailureHandlerRuleSpec::default_max_retries")] - pub max_retries: i32, + max_retries: i32, } impl FailureHandlerRuleSpec { @@ -524,9 +524,9 @@ impl FailureHandlerRuleSpec { #[serde(deny_unknown_fields)] pub struct FailureHandlerSpec { /// Name of the failure handler - pub name: String, + name: String, /// Rules for handling different exit codes - pub rules: Vec, + rules: Vec, } /// Slurm scheduler specification for JSON serialization (without workflow_id and id) @@ -587,7 +587,7 @@ impl SlurmSchedulerSpec { /// Parameters that are managed by torc and cannot be set in slurm_defaults /// Note: "account" is allowed in slurm_defaults as a workflow-level default -pub const SLURM_EXCLUDED_PARAMS: &[&str] = &[ +const SLURM_EXCLUDED_PARAMS: &[&str] = &[ "partition", "nodes", "walltime", @@ -2131,7 +2131,7 @@ impl WorkflowSpec { ]; /// Validate workflow actions - pub fn validate_actions(&self) -> Result<(), Box> { + pub(crate) fn validate_actions(&self) -> Result<(), Box> { if let Some(ref actions) = self.actions { for action in actions { // Reject unknown trigger types (e.g. a typo'd `on_ready_jobs`), which would @@ -2218,7 +2218,7 @@ impl WorkflowSpec { /// /// The validation rejects the case where jobs request a different multi-node count /// than the scheduler provides (e.g., scheduler allocates 4 nodes but jobs request 2). - pub fn validate_scheduler_node_requirements(&self) -> Result<(), Box> { + fn validate_scheduler_node_requirements(&self) -> Result<(), Box> { // Build lookup maps for resource requirements and schedulers let resource_req_map: HashMap<&str, &ResourceRequirementsSpec> = self .resource_requirements @@ -2342,7 +2342,7 @@ impl WorkflowSpec { /// /// Scheduler fields that are not set (e.g., `mem: None`, `gres: None`) are skipped /// for that dimension. - pub fn validate_scheduler_resources(&self) -> Vec { + fn validate_scheduler_resources(&self) -> Vec { let resource_req_map: HashMap<&str, &ResourceRequirementsSpec> = self .resource_requirements .as_ref() @@ -3195,7 +3195,7 @@ impl WorkflowSpec { /// Create a workflow from a pre-parsed and validated spec. /// Use this after `validate_for_creation` to avoid re-reading the file. - pub fn create_from_validated_spec( + pub(crate) fn create_from_validated_spec( config: &Configuration, mut spec: WorkflowSpec, user: &str, @@ -6327,7 +6327,7 @@ impl WorkflowSpec { /// the same order as `from_spec_file`'s extension-less fallback. Returns a /// canonical file extension ("json", "json5", "yaml", or "kdl"), or `None` /// when the content cannot be parsed by any supported format. - pub fn detect_spec_format(content: &str) -> Option<&'static str> { + fn detect_spec_format(content: &str) -> Option<&'static str> { // A workflow spec is always a mapping/object. Requiring an object (rather // than just "parses successfully") avoids false positives -- notably YAML, // which happily parses arbitrary text such as KDL as a bare scalar string. @@ -6433,7 +6433,7 @@ impl WorkflowSpec { /// - ${files.output.NAME} - output file (automatically adds to output_files) /// - ${user_data.input.NAME} - input user data (automatically adds to input_user_data) /// - ${user_data.output.NAME} - output user data (automatically adds to output_user_data) - pub fn substitute_variables(&mut self) -> Result<(), Box> { + pub(crate) fn substitute_variables(&mut self) -> Result<(), Box> { // Build file name to path mapping let mut file_name_to_path = HashMap::new(); if let Some(files) = &self.files { diff --git a/src/config/client.rs b/src/config/client.rs index d71db9fac..323a1f095 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -21,16 +21,16 @@ pub struct ClientConfig { pub run: ClientRunConfig, /// Offline-drain configuration for job runners - pub offline: ClientOfflineConfig, + pub(crate) offline: ClientOfflineConfig, /// Slurm scheduler configuration pub slurm: ClientSlurmConfig, /// HPC profile configuration - pub hpc: ClientHpcConfig, + pub(crate) hpc: ClientHpcConfig, /// Watch command configuration - pub watch: ClientWatchConfig, + watch: ClientWatchConfig, /// TLS configuration pub tls: ClientTlsConfig, @@ -148,12 +148,12 @@ pub struct ClientSlurmConfig { pub poll_interval: i32, /// Keep submission scripts after job submission (useful for debugging) - pub keep_submission_scripts: bool, + pub(crate) keep_submission_scripts: bool, /// If true, only claim jobs that match the scheduler_id of the worker. /// If false (default), jobs with a scheduler_id mismatch will be claimed /// if no matching jobs are available. - pub strict_scheduler_match: bool, + pub(crate) strict_scheduler_match: bool, } impl Default for ClientSlurmConfig { @@ -215,14 +215,14 @@ impl Default for ClientWatchConfig { #[serde(default)] pub struct ClientHpcConfig { /// Default account to use for HPC jobs - pub default_account: Option, + default_account: Option, /// Profile overrides - allows customizing built-in profiles /// Key is the profile name (e.g., "kestrel") - pub profile_overrides: HashMap, + profile_overrides: HashMap, /// Custom profiles defined by the user - pub custom_profiles: HashMap, + pub(crate) custom_profiles: HashMap, } /// Override settings for a built-in HPC profile @@ -230,42 +230,42 @@ pub struct ClientHpcConfig { #[serde(default)] pub struct HpcProfileOverride { /// Override the default account for this profile - pub default_account: Option, + default_account: Option, } /// Configuration for a custom HPC profile #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HpcProfileConfig { /// Display name for the profile - pub display_name: String, + pub(crate) display_name: String, /// Description of the HPC system #[serde(default)] - pub description: String, + pub(crate) description: String, /// Detection via environment variable (name=value) #[serde(default)] - pub detect_env_var: Option, + pub(crate) detect_env_var: Option, /// Detection via hostname pattern (regex) #[serde(default)] - pub detect_hostname: Option, + pub(crate) detect_hostname: Option, /// Default account for this profile #[serde(default)] - pub default_account: Option, + pub(crate) default_account: Option, /// Charge factor for CPU jobs #[serde(default = "default_charge_factor")] - pub charge_factor_cpu: f64, + pub(crate) charge_factor_cpu: f64, /// Charge factor for GPU jobs #[serde(default = "default_charge_factor_gpu")] - pub charge_factor_gpu: f64, + pub(crate) charge_factor_gpu: f64, /// Partition configurations #[serde(default)] - pub partitions: Vec, + pub(crate) partitions: Vec, } fn default_charge_factor() -> f64 { @@ -280,40 +280,40 @@ fn default_charge_factor_gpu() -> f64 { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HpcPartitionConfig { /// Partition name - pub name: String, + pub(crate) name: String, /// Description #[serde(default)] - pub description: String, + pub(crate) description: String, /// CPUs per node - pub cpus_per_node: u32, + pub(crate) cpus_per_node: u32, /// Memory per node in MB - pub memory_mb: u64, + pub(crate) memory_mb: u64, /// Maximum wall time in seconds - pub max_walltime_secs: u64, + pub(crate) max_walltime_secs: u64, /// GPUs per node (if any) #[serde(default)] - pub gpus_per_node: Option, + pub(crate) gpus_per_node: Option, /// GPU type (e.g., "h100", "a100") #[serde(default)] - pub gpu_type: Option, + pub(crate) gpu_type: Option, /// GPU memory in GB #[serde(default)] - pub gpu_memory_gb: Option, + pub(crate) gpu_memory_gb: Option, /// Whether the partition supports shared access #[serde(default)] - pub shared: bool, + pub(crate) shared: bool, /// Whether partition must be explicitly requested #[serde(default)] - pub requires_explicit_request: bool, + pub(crate) requires_explicit_request: bool, } #[cfg(test)] diff --git a/src/config/loader.rs b/src/config/loader.rs index 1f4b5356c..c8c3bc6d0 100644 --- a/src/config/loader.rs +++ b/src/config/loader.rs @@ -360,7 +360,7 @@ completion_check_interval_secs = 5 } /// Get the configuration paths - pub fn paths() -> ConfigPaths { + fn paths() -> ConfigPaths { ConfigPaths::new() } diff --git a/src/lib.rs b/src/lib.rs index d0de7d603..0c812585b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,25 +21,30 @@ pub fn get_username() -> String { // Shared modules (always available) pub mod api_version; pub mod memory_utils; +#[allow(dead_code)] pub mod models; pub mod network_utils; -pub mod ro_crate_json_ld; +pub(crate) mod ro_crate_json_ld; pub mod time_utils; // Configuration module (requires config feature, enabled by client) #[cfg(feature = "config")] +#[allow(dead_code)] pub mod config; // Server modules (behind feature flag) #[cfg(feature = "server")] +#[allow(dead_code)] pub mod server; // Client modules (behind feature flag) #[cfg(feature = "client")] +#[allow(dead_code)] pub mod client; // TUI module (behind feature flag) #[cfg(feature = "tui")] +#[allow(dead_code)] pub mod tui; // Binary command modules (behind feature flags) - re-exported for standalone binaries @@ -57,6 +62,7 @@ pub mod plot_resources_cmd; // MCP server modules (behind feature flag) #[cfg(feature = "mcp-server")] +#[allow(dead_code)] pub mod mcp_server; // Rust-owned OpenAPI emission diff --git a/src/mcp_server/server.rs b/src/mcp_server/server.rs index 9b45716db..7d1613493 100644 --- a/src/mcp_server/server.rs +++ b/src/mcp_server/server.rs @@ -28,7 +28,7 @@ pub struct TorcMcpServer { impl TorcMcpServer { /// Create a new TorcMcpServer with the given API URL and output directory. - pub fn new(api_url: String, output_dir: PathBuf) -> Self { + fn new(api_url: String, output_dir: PathBuf) -> Self { Self::new_with_tls(api_url, output_dir, TlsConfig::default()) } @@ -50,7 +50,7 @@ impl TorcMcpServer { } /// Create a new TorcMcpServer with authentication. - pub fn with_auth( + fn with_auth( api_url: String, output_dir: PathBuf, username: Option, @@ -111,34 +111,34 @@ impl TorcMcpServer { #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct WorkflowIdParam { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct JobIdParam { #[schemars(description = "The job ID")] - pub job_id: i64, + job_id: i64, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct GetJobLogsParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, #[schemars(description = "The job ID")] - pub job_id: i64, + job_id: i64, #[schemars(description = "The run ID (1 for first run, increments on restart)")] - pub run_id: i64, + run_id: i64, #[schemars( description = "The attempt ID (1 for first attempt, increments on retry). Defaults to 1." )] #[serde(default = "default_attempt_id")] - pub attempt_id: i64, + attempt_id: i64, #[schemars(description = "Log type: 'stdout' or 'stderr'")] - pub log_type: String, + log_type: String, #[schemars( description = "Number of lines to return from the end (optional, returns all if not specified)" )] - pub tail_lines: Option, + tail_lines: Option, } fn default_attempt_id() -> i64 { @@ -148,25 +148,25 @@ fn default_attempt_id() -> i64 { #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct ListJobsByStatusParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, #[schemars( description = "Job status to filter by: 'uninitialized', 'blocked', 'ready', 'pending', 'running', 'completed', 'failed', 'canceled', 'terminated', 'disabled'" )] - pub status: String, + status: String, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct UpdateJobResourcesParams { #[schemars(description = "The job ID")] - pub job_id: i64, + job_id: i64, #[schemars(description = "Number of CPUs (optional)")] - pub num_cpus: Option, + num_cpus: Option, #[schemars(description = "Memory requirement, e.g., '4g', '512m' (optional)")] - pub memory: Option, + memory: Option, #[schemars( description = "Runtime in ISO8601 duration format, e.g., 'PT30M', 'PT2H' (optional)" )] - pub runtime: Option, + runtime: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] @@ -174,35 +174,35 @@ pub struct CreateWorkflowParams { #[schemars( description = "Workflow specification as a JSON object (not a string). For Slurm workflows, must include a 'resource_requirements' section and each job must reference one." )] - pub spec_json: serde_json::Value, + spec_json: serde_json::Value, #[schemars(description = "User that owns the workflow (optional, defaults to current user)")] - pub user: Option, + user: Option, #[schemars( description = "Action to perform: 'create_workflow' to create in the database, 'save_spec_file' to save to filesystem only, 'validate' to validate without creating" )] - pub action: String, + action: String, #[schemars(description = "Workflow type: 'local' for local execution, 'slurm' for Slurm HPC")] - pub workflow_type: String, + workflow_type: String, #[schemars(description = "Slurm account (required for slurm workflow_type)")] - pub account: Option, + account: Option, #[schemars( description = "HPC profile to use (optional, auto-detected if not specified). Required for slurm if auto-detection fails." )] - pub hpc_profile: Option, + hpc_profile: Option, #[schemars( description = "Output file path for save_spec_file action (required for save_spec_file, use .json extension)" )] - pub output_path: Option, + output_path: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct CheckResourceUtilizationParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, #[schemars( description = "Include failed jobs in the analysis (recommended for recovery diagnostics)" )] - pub include_failed: Option, + include_failed: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] @@ -210,139 +210,139 @@ pub struct GetExecutionPlanParams { #[schemars( description = "Either a workflow ID (integer) to get plan for existing workflow, or a JSON workflow specification string to preview execution plan before creating" )] - pub spec_or_id: String, + spec_or_id: String, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct AnalyzeWorkflowLogsParams { #[schemars(description = "Workflow ID to analyze logs for")] - pub workflow_id: i64, + workflow_id: i64, #[schemars( description = "Output directory where logs are stored (the same directory passed to `torc run`). Defaults to 'torc_output'." )] - pub output_dir: Option, + output_dir: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct GetWorkflowSummaryParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct CheckOfflineJournalsParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, #[schemars( description = "Run ID to match journals against (optional). Defaults to the workflow's current run_id, so only journals for the run the server still considers active are reported." )] - pub run_id: Option, + run_id: Option, #[schemars( description = "Base directory to search recursively for journal files (optional). Defaults to the server's output directory. For Slurm runs this is typically the shared output directory passed to `torc run`." )] - pub base_dir: Option, + base_dir: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct ListResultsParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, #[schemars(description = "Filter by job ID")] - pub job_id: Option, + job_id: Option, #[schemars(description = "Filter by run ID")] - pub run_id: Option, + run_id: Option, #[schemars(description = "Filter by return code (e.g., 0 for success, 1 for failure)")] - pub return_code: Option, + return_code: Option, #[schemars(description = "Show only failed jobs (non-zero return code)")] - pub failed_only: Option, + failed_only: Option, #[schemars( description = "Filter by job status: completed, failed, terminated, canceled, etc." )] - pub status: Option, + status: Option, #[schemars(description = "Maximum number of results to return (default: 100)")] - pub limit: Option, + limit: Option, #[schemars( description = "Field to sort by: exec_time_minutes, peak_memory_bytes, peak_cpu_percent, return_code" )] - pub sort_by: Option, + sort_by: Option, #[schemars(description = "Reverse the sort order (descending instead of ascending)")] - pub reverse_sort: Option, + reverse_sort: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct GetSlurmSacctParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct RecoverWorkflowParams { #[schemars(description = "The workflow ID to recover")] - pub workflow_id: i64, + workflow_id: i64, #[schemars( description = "If true, shows what would be done without making any changes. \ ALWAYS use dry_run=true first to preview recovery actions, then confirm with user before running with dry_run=false." )] - pub dry_run: bool, + dry_run: bool, #[schemars( description = "Memory multiplier for OOM failures (default: 1.5 = 50% increase). \ Jobs that failed due to OOM will have their memory increased by this factor." )] - pub memory_multiplier: Option, + memory_multiplier: Option, #[schemars( description = "Runtime multiplier for timeout failures (default: 1.4 = 40% increase). \ Jobs that timed out will have their runtime increased by this factor." )] - pub runtime_multiplier: Option, + runtime_multiplier: Option, #[schemars( description = "If true, also retry jobs with unknown failure causes (not OOM or timeout). \ Default is false - only retry jobs with diagnosable resource issues." )] - pub retry_unknown: Option, + retry_unknown: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct ListPendingFailedJobsParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, } #[derive(Debug, Clone, Deserialize, schemars::JsonSchema)] pub struct FailureClassificationParam { #[schemars(description = "The job ID to classify")] - pub job_id: i64, + job_id: i64, #[schemars(description = "The classification action: 'retry' or 'fail'")] - pub action: String, + action: String, #[schemars(description = "Optional new memory requirement (e.g., '8g')")] - pub memory: Option, + memory: Option, #[schemars(description = "Optional new runtime (ISO8601 duration, e.g., 'PT2H')")] - pub runtime: Option, + runtime: Option, #[schemars(description = "Reason for the classification (for logging)")] - pub reason: Option, + reason: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct ClassifyAndResolveFailuresParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, #[schemars(description = "List of classifications for pending_failed jobs")] - pub classifications: Vec, + classifications: Vec, #[schemars( description = "If true, shows what would be done without making any changes. \ ALWAYS use dry_run=true first to preview classifications, then confirm with user before running with dry_run=false." )] - pub dry_run: bool, + dry_run: bool, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct AnalyzeResourceUsageParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, #[schemars( description = "If true, only include jobs with return_code=0 (successful). \ If false (default), include all jobs with results." )] - pub completed_only: Option, + completed_only: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] @@ -350,11 +350,11 @@ pub struct GetExampleParams { #[schemars( description = "Name of the example to retrieve (e.g., 'diamond_workflow', 'hyperparameter_sweep')" )] - pub name: String, + name: String, #[schemars( description = "Preferred format: 'yaml' (default), 'json', or 'kdl'. Falls back to available format." )] - pub format: Option, + format: Option, } #[derive(Debug, Deserialize, schemars::JsonSchema)] @@ -371,7 +371,7 @@ pub struct GetDocsParams { 'workflow-formats' (YAML/JSON/KDL formats), \ 'allocation-strategies' (single-large vs many-small Slurm allocations), \ 'tutorials' (list of available tutorials)")] - pub topic: String, + topic: String, } #[derive(Debug, Deserialize, schemars::JsonSchema)] @@ -379,52 +379,52 @@ pub struct PlanAllocationsParams { #[schemars( description = "Workflow specification as a JSON object (not a string). Must include 'resource_requirements' section with CPU, memory, and runtime for each job type." )] - pub spec_json: serde_json::Value, + spec_json: serde_json::Value, #[schemars(description = "Slurm account to use for allocation estimates")] - pub account: String, + account: String, #[schemars(description = "Partition to target (optional, auto-selected if not specified)")] - pub partition: Option, + partition: Option, #[schemars( description = "HPC profile to use (optional, auto-detected if not specified). Use when auto-detection fails." )] - pub hpc_profile: Option, + hpc_profile: Option, #[schemars( description = "Skip sbatch --test-only probes (faster, uses heuristics only). Default: false" )] #[serde(default)] - pub skip_test_only: bool, + skip_test_only: bool, } #[derive(Debug, Clone, Deserialize, schemars::JsonSchema)] pub struct ResourceGroupParam { #[schemars(description = "Memory requirement, e.g., '10g', '512m'")] - pub memory: String, + memory: String, #[schemars(description = "Number of CPUs")] - pub num_cpus: i64, + num_cpus: i64, #[schemars(description = "Runtime in ISO8601 duration format, e.g., 'PT2H', 'PT30M'")] - pub runtime: String, + runtime: String, #[schemars(description = "Number of GPUs (defaults to the job's current RR value, or 0)")] - pub num_gpus: Option, + num_gpus: Option, #[schemars(description = "Number of nodes (defaults to the job's current RR value, or 1)")] - pub num_nodes: Option, + num_nodes: Option, #[schemars(description = "Name for this resource group (auto-generated if not provided)")] - pub name: Option, + name: Option, #[schemars(description = "Job IDs to assign to this resource group")] - pub job_ids: Vec, + job_ids: Vec, } #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct RegroupJobResourcesParams { #[schemars(description = "The workflow ID")] - pub workflow_id: i64, + workflow_id: i64, #[schemars(description = "List of new resource groups with job assignments. \ Each group defines resource requirements and which jobs belong to it.")] - pub groups: Vec, + groups: Vec, #[schemars( description = "If true, shows what would be done without making any changes. \ ALWAYS use dry_run=true first to preview the regrouping, then confirm with user before running with dry_run=false." )] - pub dry_run: bool, + dry_run: bool, } // Tool implementations using rmcp tool routing. diff --git a/src/mcp_server/tools.rs b/src/mcp_server/tools.rs index 09517ea20..02bccaf90 100644 --- a/src/mcp_server/tools.rs +++ b/src/mcp_server/tools.rs @@ -33,7 +33,7 @@ fn invalid_params(msg: &str) -> McpError { } /// Get workflow status with job counts. -pub fn get_workflow_status( +pub(crate) fn get_workflow_status( config: &Configuration, workflow_id: i64, ) -> Result { @@ -69,7 +69,10 @@ pub fn get_workflow_status( } /// Get detailed job information. -pub fn get_job_details(config: &Configuration, job_id: i64) -> Result { +pub(crate) fn get_job_details( + config: &Configuration, + job_id: i64, +) -> Result { let job = apis::jobs_api::get_job(config, job_id) .map_err(|e| internal_error(format!("Failed to get job: {}", e)))?; @@ -122,7 +125,7 @@ pub fn get_job_details(config: &Configuration, job_id: i64) -> Result Result { @@ -192,7 +195,7 @@ pub fn list_failed_jobs( } /// List jobs by status. -pub fn list_jobs_by_status( +pub(crate) fn list_jobs_by_status( config: &Configuration, workflow_id: i64, status: &str, @@ -233,7 +236,7 @@ pub fn list_jobs_by_status( } /// Check resource utilization for a workflow. -pub fn check_resource_utilization( +pub(crate) fn check_resource_utilization( config: &Configuration, workflow_id: i64, include_failed: bool, @@ -299,7 +302,7 @@ pub fn check_resource_utilization( } /// Update job resource requirements. -pub fn update_job_resources( +pub(crate) fn update_job_resources( config: &Configuration, job_id: i64, num_cpus: Option, @@ -381,7 +384,7 @@ pub fn update_job_resources( /// - action: "validate" (validate only), "create_workflow" (create in database) or "save_spec_file" (save to filesystem) /// - workflow_type: "local" or "slurm" #[allow(clippy::too_many_arguments)] -pub fn create_workflow( +pub(crate) fn create_workflow( config: &Configuration, spec_json: &str, user: &str, @@ -695,7 +698,7 @@ pub fn create_workflow( /// Accepts either: /// - A workflow ID (integer as string) for existing workflows /// - A JSON workflow specification string for previewing before creation -pub fn get_execution_plan( +pub(crate) fn get_execution_plan( config: &Configuration, spec_or_id: &str, ) -> Result { @@ -861,7 +864,7 @@ pub fn get_execution_plan( /// Scans all log files for a workflow and detects common error patterns like: /// OOM, timeout, segfaults, permission denied, disk full, connection errors, /// Python exceptions, Rust panics, and Slurm errors. -pub fn analyze_workflow_logs( +pub(crate) fn analyze_workflow_logs( output_dir: &Path, workflow_id: i64, ) -> Result { @@ -992,7 +995,7 @@ pub fn analyze_workflow_logs( /// are not reflected on the server until replayed, so a workflow can look stalled /// or partially failed when it actually finished offline. This tool only inspects /// the filesystem; it never replays anything. -pub fn check_offline_journals( +pub(crate) fn check_offline_journals( config: &Configuration, base_dir: &Path, workflow_id: i64, @@ -1157,7 +1160,7 @@ fn shell_quote(s: &str) -> String { } /// Get workflow summary. -pub fn get_workflow_summary( +pub(crate) fn get_workflow_summary( config: &Configuration, workflow_id: i64, ) -> Result { @@ -1172,7 +1175,7 @@ pub fn get_workflow_summary( /// List job results with filtering options. #[allow(clippy::too_many_arguments)] -pub fn list_results( +pub(crate) fn list_results( workflow_id: i64, job_id: Option, run_id: Option, @@ -1228,7 +1231,7 @@ pub fn list_results( } /// Get Slurm sacct accounting data for a workflow with walltime summary. -pub fn get_slurm_sacct(workflow_id: i64) -> Result { +pub(crate) fn get_slurm_sacct(workflow_id: i64) -> Result { let output = Command::new("torc") .args(["-f", "json", "slurm", "sacct", &workflow_id.to_string()]) .output() @@ -1304,7 +1307,7 @@ fn parse_elapsed_to_seconds(elapsed: &str) -> i64 { /// /// This function runs `torc recover` with the specified parameters. /// When dry_run is true, it shows what would be done without making changes. -pub fn recover_workflow( +pub(crate) fn recover_workflow( workflow_id: i64, output_dir: &Path, dry_run: bool, @@ -1374,7 +1377,7 @@ pub fn recover_workflow( /// /// These are jobs that failed without a matching failure handler and are awaiting /// AI-assisted classification to determine whether they should be retried or marked as failed. -pub fn list_pending_failed_jobs( +pub(crate) fn list_pending_failed_jobs( config: &Configuration, workflow_id: i64, output_dir: &Path, @@ -1465,15 +1468,15 @@ pub fn list_pending_failed_jobs( #[derive(Debug, Clone, serde::Deserialize)] pub struct FailureClassification { /// The job ID to classify - pub job_id: i64, + pub(crate) job_id: i64, /// The classification action: "retry" or "fail" - pub action: String, + pub(crate) action: String, /// Optional new memory requirement (e.g., "8g") - pub memory: Option, + pub(crate) memory: Option, /// Optional new runtime (ISO8601 duration, e.g., "PT2H") - pub runtime: Option, + pub(crate) runtime: Option, /// Reason for the classification (for logging) - pub reason: Option, + pub(crate) reason: Option, } /// Classify and resolve pending_failed jobs. @@ -1483,7 +1486,7 @@ pub struct FailureClassification { /// - Resets them to "ready" status with bumped attempt_id for retry /// /// Resource requirements can optionally be adjusted before retry. -pub fn classify_and_resolve_failures( +pub(crate) fn classify_and_resolve_failures( config: &Configuration, workflow_id: i64, classifications: Vec, @@ -1788,7 +1791,7 @@ fn compute_memory_stats(values: &[i64]) -> serde_json::Value { /// /// Returns structured JSON with per-RR summary statistics and per-job detail, /// optimized for AI cluster analysis. -pub fn analyze_resource_usage( +pub(crate) fn analyze_resource_usage( config: &Configuration, workflow_id: i64, completed_only: bool, @@ -1938,19 +1941,19 @@ pub fn analyze_resource_usage( /// A resource group definition for regrouping jobs. #[derive(Debug, Clone)] pub struct ResourceGroup { - pub memory: String, - pub num_cpus: i64, - pub runtime: String, - pub num_gpus: Option, - pub num_nodes: Option, - pub name: Option, - pub job_ids: Vec, + pub(crate) memory: String, + pub(crate) num_cpus: i64, + pub(crate) runtime: String, + pub(crate) num_gpus: Option, + pub(crate) num_nodes: Option, + pub(crate) name: Option, + pub(crate) job_ids: Vec, } /// Regroup jobs into new resource requirement groups. /// /// Creates new RR records and reassigns jobs to them. Supports dry_run for previewing. -pub fn regroup_job_resources( +pub(crate) fn regroup_job_resources( config: &Configuration, workflow_id: i64, groups: Vec, @@ -2600,7 +2603,7 @@ fn read_example_content( } /// List available example workflow specifications. -pub fn list_examples(examples_dir: Option<&Path>) -> Result { +pub(crate) fn list_examples(examples_dir: Option<&Path>) -> Result { let descriptions = example_descriptions(); let mut examples = Vec::new(); @@ -2642,7 +2645,7 @@ pub fn list_examples(examples_dir: Option<&Path>) -> Result, name: &str, format: &str, @@ -2668,7 +2671,7 @@ pub fn get_example( } /// Get documentation on a specific topic. -pub fn get_docs(docs_dir: Option<&Path>, topic: &str) -> Result { +pub(crate) fn get_docs(docs_dir: Option<&Path>, topic: &str) -> Result { let mapping = doc_topic_mapping(); // Find matching topic (case-insensitive) @@ -2744,7 +2747,7 @@ pub fn get_docs(docs_dir: Option<&Path>, topic: &str) -> Result, @@ -2827,7 +2830,10 @@ pub fn plan_allocations( /// List all available MCP resources (docs + examples). /// Resources are always listed since they can be fetched from GitHub. -pub fn list_mcp_resources(docs_dir: Option<&Path>, examples_dir: Option<&Path>) -> Vec { +pub(crate) fn list_mcp_resources( + docs_dir: Option<&Path>, + examples_dir: Option<&Path>, +) -> Vec { let mut resources = Vec::new(); // Add documentation resources (always listed — fetched from GitHub if not local) @@ -2881,7 +2887,7 @@ pub fn list_mcp_resources(docs_dir: Option<&Path>, examples_dir: Option<&Path>) } /// Read an MCP resource by URI. -pub fn read_mcp_resource( +pub(crate) fn read_mcp_resource( docs_dir: Option<&Path>, examples_dir: Option<&Path>, uri: &str, diff --git a/src/memory_utils.rs b/src/memory_utils.rs index 6983082d9..f0d0ae185 100644 --- a/src/memory_utils.rs +++ b/src/memory_utils.rs @@ -106,7 +106,7 @@ pub fn memory_string_to_mb(memory_str: &str) -> Option { /// # Panics /// /// Panics if the memory string is invalid. -pub fn memory_string_to_gb(memory_str: &str) -> f64 { +pub(crate) fn memory_string_to_gb(memory_str: &str) -> f64 { const GB: i64 = 1024 * 1024 * 1024; match memory_string_to_bytes(memory_str) { Ok(bytes) => bytes as f64 / GB as f64, diff --git a/src/models.rs b/src/models.rs index b4e1713a5..766278818 100644 --- a/src/models.rs +++ b/src/models.rs @@ -13,7 +13,7 @@ use std::sync::OnceLock; /// Returns true when `name` is a valid POSIX-style environment variable name: /// starts with a letter or underscore, followed by letters, digits, or underscores. -pub fn is_valid_env_var_name(name: &str) -> bool { +pub(crate) fn is_valid_env_var_name(name: &str) -> bool { let mut chars = name.chars(); match chars.next() { Some(first) if first == '_' || first.is_ascii_alphabetic() => {} @@ -71,35 +71,35 @@ pub enum EventSeverity { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ComputeNodeSchedule { #[serde(skip_serializing_if = "Option::is_none")] - pub max_parallel_jobs: Option, - pub num_jobs: i64, - pub scheduler_id: i64, + max_parallel_jobs: Option, + num_jobs: i64, + scheduler_id: i64, #[serde(skip_serializing_if = "Option::is_none")] - pub start_one_worker_per_node: Option, + start_one_worker_per_node: Option, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ErrorResponse { - pub error: Value, + pub(crate) error: Value, #[serde(rename = "errorNum", skip_serializing_if = "Option::is_none")] - pub error_num: Option, + error_num: Option, #[serde(rename = "errorMessage", skip_serializing_if = "Option::is_none")] - pub error_message: Option, + error_message: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub code: Option, + code: Option, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PingResponse { - pub status: String, + status: String, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct VersionResponse { - pub version: String, + pub(crate) version: String, pub api_version: String, #[serde(skip_serializing_if = "Option::is_none")] pub git_hash: Option, @@ -110,16 +110,16 @@ pub struct VersionResponse { pub struct ComputeNodeModel { #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, - pub workflow_id: i64, + pub(crate) workflow_id: i64, pub hostname: String, - pub pid: i64, - pub start_time: String, + pub(crate) pid: i64, + pub(crate) start_time: String, /// Allocation end time (RFC3339), reported by the runner at registration. /// Used to compute remaining walltime for active nodes. #[serde(skip_serializing_if = "Option::is_none")] pub end_time: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub duration_seconds: Option, + pub(crate) duration_seconds: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_active: Option, pub num_cpus: i64, @@ -127,12 +127,12 @@ pub struct ComputeNodeModel { pub num_gpus: i64, pub num_nodes: i64, #[serde(skip_serializing_if = "Option::is_none")] - pub time_limit: Option, + pub(crate) time_limit: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub scheduler_config_id: Option, - pub compute_node_type: String, + pub(crate) scheduler_config_id: Option, + pub(crate) compute_node_type: String, #[serde(skip_serializing_if = "Option::is_none")] - pub scheduler: Option, + pub(crate) scheduler: Option, #[serde(skip_serializing_if = "Option::is_none")] pub sample_count: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -149,17 +149,17 @@ pub struct ComputeNodeModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListComputeNodesResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, pub total_count: i64, - pub has_more: bool, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct DeleteCountResponse { - pub count: i64, + count: i64, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -167,8 +167,8 @@ pub struct DeleteCountResponse { pub struct EventModel { #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, - pub workflow_id: i64, - pub timestamp: i64, + pub(crate) workflow_id: i64, + pub(crate) timestamp: i64, pub data: Value, } @@ -176,11 +176,11 @@ pub struct EventModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListEventsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, pub total_count: i64, - pub has_more: bool, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -188,7 +188,7 @@ pub struct ListEventsResponse { pub struct FileModel { #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, - pub workflow_id: i64, + pub(crate) workflow_id: i64, pub name: String, pub path: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -199,11 +199,11 @@ pub struct FileModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListFilesResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, - pub total_count: i64, - pub has_more: bool, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, + pub(crate) total_count: i64, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -211,7 +211,7 @@ pub struct ListFilesResponse { pub struct UserDataModel { #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, - pub workflow_id: i64, + pub(crate) workflow_id: i64, #[serde(skip_serializing_if = "Option::is_none")] pub is_ephemeral: Option, pub name: String, @@ -223,11 +223,11 @@ pub struct UserDataModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListUserDataResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, - pub total_count: i64, - pub has_more: bool, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, + pub(crate) total_count: i64, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -248,14 +248,14 @@ pub struct JobModel { /// cleared by complete_job and the reset/retry paths. NULL when the job is /// not running (use `status` as the source of truth for "is running"). #[serde(skip_serializing_if = "Option::is_none")] - pub start_time: Option, + pub(crate) start_time: Option, /// Compute node executing the current attempt. Set by start_job and cleared /// by complete_job and the reset/retry paths. For completed attempts, the /// compute node is recorded on the result record. #[serde(skip_serializing_if = "Option::is_none")] - pub compute_node_id: Option, + pub(crate) compute_node_id: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub schedule_compute_nodes: Option, + pub(crate) schedule_compute_nodes: Option, #[serde(skip_serializing_if = "Option::is_none")] pub cancel_on_blocking_job_failure: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -275,7 +275,7 @@ pub struct JobModel { #[serde(skip_serializing_if = "Option::is_none")] pub scheduler_id: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub failure_handler_id: Option, + pub(crate) failure_handler_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub attempt_id: Option, /// Scheduling priority; higher values are submitted first. Minimum 0, default 0. @@ -296,11 +296,11 @@ pub struct JobModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListJobsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, pub total_count: i64, - pub has_more: bool, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -337,28 +337,28 @@ pub struct ResultModel { #[serde(skip_serializing_if = "Option::is_none")] pub peak_memory_bytes: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub avg_memory_bytes: Option, + pub(crate) avg_memory_bytes: Option, #[serde(skip_serializing_if = "Option::is_none")] pub peak_cpu_percent: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub avg_cpu_percent: Option, + pub(crate) avg_cpu_percent: Option, pub status: JobStatus, /// Name of the job this result belongs to. Populated by the server on read /// paths (list/get) as a convenience so clients need not re-fetch jobs; it /// is ignored on create/update input. #[serde(skip_serializing_if = "Option::is_none")] - pub job_name: Option, + pub(crate) job_name: Option, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListResultsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, pub count: i64, - pub total_count: i64, - pub has_more: bool, + pub(crate) total_count: i64, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -379,8 +379,8 @@ pub struct BatchCompleteJobsRequest { #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct JobCompletionError { - pub job_id: i64, - pub message: String, + pub(crate) job_id: i64, + pub(crate) message: String, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -468,36 +468,36 @@ pub struct ScheduledComputeNodesModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListScheduledComputeNodesResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, - pub total_count: i64, - pub has_more: bool, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, + pub(crate) total_count: i64, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct LocalSchedulerModel { #[serde(skip_serializing_if = "Option::is_none")] - pub id: Option, - pub workflow_id: i64, + pub(crate) id: Option, + pub(crate) workflow_id: i64, #[serde(skip_serializing_if = "Option::is_none")] - pub name: Option, + pub(crate) name: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub memory: Option, + pub(crate) memory: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub num_cpus: Option, + pub(crate) num_cpus: Option, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListLocalSchedulersResponse { - pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, - pub total_count: i64, - pub has_more: bool, + pub(crate) items: Vec, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, + pub(crate) total_count: i64, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -540,11 +540,11 @@ pub struct SlurmSchedulerModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListSlurmSchedulersResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, - pub total_count: i64, - pub has_more: bool, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, + pub(crate) total_count: i64, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -832,7 +832,7 @@ impl ExecutionConfig { } /// Whether staggered startup is enabled for Slurm job runners. - pub fn staggered_start(&self) -> bool { + pub(crate) fn staggered_start(&self) -> bool { self.staggered_start.unwrap_or(true) } @@ -854,7 +854,7 @@ impl ExecutionConfig { } /// Build from a WorkflowModel's execution_config field. - pub fn from_workflow_model(workflow: &WorkflowModel) -> ExecutionConfig { + pub(crate) fn from_workflow_model(workflow: &WorkflowModel) -> ExecutionConfig { workflow.execution_config.clone().unwrap_or_default() } } @@ -874,8 +874,8 @@ pub enum MonitorGranularity { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(default)] pub struct JobMonitorConfig { - pub enabled: bool, - pub granularity: MonitorGranularity, + pub(crate) enabled: bool, + pub(crate) granularity: MonitorGranularity, } impl Default for JobMonitorConfig { @@ -892,10 +892,10 @@ impl Default for JobMonitorConfig { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(default)] pub struct ComputeNodeMonitorConfig { - pub enabled: bool, - pub granularity: MonitorGranularity, - pub cpu: bool, - pub memory: bool, + pub(crate) enabled: bool, + pub(crate) granularity: MonitorGranularity, + pub(crate) cpu: bool, + pub(crate) memory: bool, } impl Default for ComputeNodeMonitorConfig { @@ -919,15 +919,15 @@ impl Default for ComputeNodeMonitorConfig { #[serde(default)] pub struct ResourceMonitorConfig { /// Deprecated compatibility field. Use `jobs.enabled` for new workflow specs. - pub enabled: bool, + pub(crate) enabled: bool, /// Deprecated compatibility field. Use `jobs.granularity` for new workflow specs. - pub granularity: MonitorGranularity, - pub sample_interval_seconds: i32, + pub(crate) granularity: MonitorGranularity, + pub(crate) sample_interval_seconds: i32, /// How often buffered time-series samples are flushed to SQLite, in seconds. - pub flush_interval_seconds: i32, - pub generate_plots: bool, - pub jobs: Option, - pub compute_node: Option, + pub(crate) flush_interval_seconds: i32, + pub(crate) generate_plots: bool, + pub(crate) jobs: Option, + pub(crate) compute_node: Option, } impl Default for ResourceMonitorConfig { @@ -945,23 +945,23 @@ impl Default for ResourceMonitorConfig { } impl ResourceMonitorConfig { - pub fn jobs_config(&self) -> JobMonitorConfig { + pub(crate) fn jobs_config(&self) -> JobMonitorConfig { self.jobs.clone().unwrap_or(JobMonitorConfig { enabled: self.enabled, granularity: self.granularity.clone(), }) } - pub fn compute_node_config(&self) -> Option { + pub(crate) fn compute_node_config(&self) -> Option { self.compute_node.clone().filter(|config| config.enabled) } - pub fn is_enabled(&self) -> bool { + pub(crate) fn is_enabled(&self) -> bool { self.jobs_config().enabled || self.compute_node_config().is_some() } /// Returns true if any enabled scope uses time-series granularity. - pub fn has_timeseries_db(&self) -> bool { + pub(crate) fn has_timeseries_db(&self) -> bool { let jobs_ts = { let jobs = self.jobs_config(); jobs.enabled && matches!(jobs.granularity, MonitorGranularity::TimeSeries) @@ -977,11 +977,11 @@ impl ResourceMonitorConfig { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListWorkflowsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, - pub total_count: i64, - pub has_more: bool, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, + pub(crate) total_count: i64, + pub(crate) has_more: bool, } /// Request body for `POST /workflows/{id}/archive`. Setting `is_archived` @@ -1013,7 +1013,7 @@ pub struct ClaimJobsBasedOnResources { #[serde(skip_serializing_if = "Option::is_none")] pub jobs: Option>, #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option, + pub(crate) reason: Option, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -1030,26 +1030,26 @@ pub struct JobDependencyModel { pub job_name: String, pub depends_on_job_id: i64, pub depends_on_job_name: String, - pub workflow_id: i64, + pub(crate) workflow_id: i64, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListJobDependenciesResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, pub total_count: i64, - pub has_more: bool, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct JobFileRelationshipModel { - pub file_id: i64, + pub(crate) file_id: i64, pub file_name: String, - pub file_path: String, + pub(crate) file_path: String, #[serde(skip_serializing_if = "Option::is_none")] pub producer_job_id: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -1065,8 +1065,8 @@ pub struct JobFileRelationshipModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListJobFileRelationshipsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, pub count: i64, pub total_count: i64, pub has_more: bool, @@ -1075,7 +1075,7 @@ pub struct ListJobFileRelationshipsResponse { #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct JobUserDataRelationshipModel { - pub user_data_id: i64, + pub(crate) user_data_id: i64, pub user_data_name: String, #[serde(skip_serializing_if = "Option::is_none")] pub producer_job_id: Option, @@ -1092,18 +1092,18 @@ pub struct JobUserDataRelationshipModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListJobUserDataRelationshipsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, pub count: i64, pub total_count: i64, - pub has_more: bool, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListJobIdsResponse { - pub job_ids: Vec, - pub count: i64, + job_ids: Vec, + count: i64, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -1115,18 +1115,18 @@ pub struct ListMissingUserDataResponse { #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ProcessChangedJobInputsResponse { - pub reinitialized_jobs: Vec, + pub(crate) reinitialized_jobs: Vec, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct GetReadyJobRequirementsResponse { - pub num_jobs: i64, - pub num_cpus: i64, - pub num_gpus: i64, - pub memory_gb: f64, - pub max_num_nodes: i64, - pub max_runtime: String, + num_jobs: i64, + num_cpus: i64, + num_gpus: i64, + memory_gb: f64, + max_num_nodes: i64, + max_runtime: String, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -1175,17 +1175,17 @@ pub struct ListAccessGroupsResponse { pub offset: i64, pub limit: i64, pub total_count: i64, - pub has_more: bool, + has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListUserGroupMembershipsResponse { pub items: Vec, - pub offset: i64, - pub limit: i64, - pub total_count: i64, - pub has_more: bool, + offset: i64, + limit: i64, + total_count: i64, + has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -1195,7 +1195,7 @@ pub struct AccessCheckResponse { pub user_name: String, pub workflow_id: i64, #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option, + pub(crate) reason: Option, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -1208,33 +1208,33 @@ pub struct JobsModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct CreateJobsResponse { #[serde(skip_serializing_if = "Option::is_none")] - pub jobs: Option>, + pub(crate) jobs: Option>, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct FilesModel { - pub files: Vec, + pub(crate) files: Vec, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct CreateFilesResponse { #[serde(skip_serializing_if = "Option::is_none")] - pub files: Option>, + pub(crate) files: Option>, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct UserDataListModel { - pub user_data: Vec, + pub(crate) user_data: Vec, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct CreateUserDataListResponse { #[serde(skip_serializing_if = "Option::is_none")] - pub user_data: Option>, + pub(crate) user_data: Option>, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -1268,10 +1268,10 @@ pub struct ResourceRequirementsModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListResourceRequirementsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, - pub total_count: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, + pub(crate) total_count: i64, pub has_more: bool, } @@ -1279,21 +1279,21 @@ pub struct ListResourceRequirementsResponse { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct FailureHandlerModel { #[serde(skip_serializing_if = "Option::is_none")] - pub id: Option, - pub workflow_id: i64, - pub name: String, - pub rules: String, + pub(crate) id: Option, + pub(crate) workflow_id: i64, + pub(crate) name: String, + pub(crate) rules: String, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListFailureHandlersResponse { - pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, - pub total_count: i64, - pub has_more: bool, + pub(crate) items: Vec, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, + pub(crate) total_count: i64, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] @@ -1325,17 +1325,17 @@ pub struct SlurmStatsModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListSlurmStatsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, pub total_count: i64, - pub has_more: bool, + pub(crate) has_more: bool, } -pub struct JobStatusMap; +struct JobStatusMap; impl JobStatusMap { - pub fn enum_to_int_map() -> &'static HashMap { + fn enum_to_int_map() -> &'static HashMap { static MAP: OnceLock> = OnceLock::new(); MAP.get_or_init(|| { let mut map = HashMap::new(); @@ -1354,7 +1354,7 @@ impl JobStatusMap { }) } - pub fn int_to_enum_map() -> &'static HashMap { + fn int_to_enum_map() -> &'static HashMap { static MAP: OnceLock> = OnceLock::new(); MAP.get_or_init(|| { let mut map = HashMap::new(); @@ -1373,15 +1373,15 @@ impl JobStatusMap { }) } - pub fn to_int(status: &JobStatus) -> i32 { + fn to_int(status: &JobStatus) -> i32 { *Self::enum_to_int_map().get(status).unwrap_or(&-1) } - pub fn from_int(value: i32) -> Option { + fn from_int(value: i32) -> Option { Self::int_to_enum_map().get(&value).copied() } - pub fn from_i64(value: i64) -> Option { + fn from_i64(value: i64) -> Option { Self::from_int(value as i32) } } @@ -1412,7 +1412,7 @@ impl std::str::FromStr for EventSeverity { } impl CreateJobsResponse { - pub fn new() -> CreateJobsResponse { + fn new() -> CreateJobsResponse { CreateJobsResponse { jobs: None } } } @@ -1457,7 +1457,7 @@ impl ComputeNodeModel { } impl ComputeNodeSchedule { - pub fn new(num_jobs: i64, scheduler_id: i64) -> ComputeNodeSchedule { + fn new(num_jobs: i64, scheduler_id: i64) -> ComputeNodeSchedule { ComputeNodeSchedule { max_parallel_jobs: None, num_jobs, @@ -1487,7 +1487,7 @@ impl ComputeNodesResources { } impl ErrorResponse { - pub fn new(error: serde_json::Value) -> ErrorResponse { + pub(crate) fn new(error: serde_json::Value) -> ErrorResponse { ErrorResponse { error, error_num: None, @@ -1507,7 +1507,7 @@ impl EventModel { } } - pub fn timestamp_as_string(&self) -> String { + fn timestamp_as_string(&self) -> String { use chrono::{DateTime, Utc}; DateTime::from_timestamp_millis(self.timestamp) .map(|dt: DateTime| dt.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string()) @@ -1528,7 +1528,7 @@ impl FileModel { } impl FailureHandlerModel { - pub fn new(workflow_id: i64, name: String, rules: String) -> FailureHandlerModel { + pub(crate) fn new(workflow_id: i64, name: String, rules: String) -> FailureHandlerModel { FailureHandlerModel { id: None, workflow_id, @@ -1539,7 +1539,7 @@ impl FailureHandlerModel { } impl ListFailureHandlersResponse { - pub fn new( + fn new( offset: i64, max_limit: i64, count: i64, @@ -1576,7 +1576,7 @@ impl RoCrateEntityModel { } impl ListRoCrateEntitiesResponse { - pub fn new( + fn new( offset: i64, max_limit: i64, count: i64, @@ -1595,7 +1595,7 @@ impl ListRoCrateEntitiesResponse { } impl GetReadyJobRequirementsResponse { - pub fn new( + fn new( num_jobs: i64, num_cpus: i64, num_gpus: i64, @@ -1615,7 +1615,7 @@ impl GetReadyJobRequirementsResponse { } impl IsCompleteResponse { - pub fn new(is_canceled: bool, is_complete: bool) -> IsCompleteResponse { + fn new(is_canceled: bool, is_complete: bool) -> IsCompleteResponse { IsCompleteResponse { is_canceled, is_complete, @@ -1693,7 +1693,7 @@ impl std::str::FromStr for JobStatus { } impl JobStatus { - pub fn is_terminal(&self) -> bool { + pub(crate) fn is_terminal(&self) -> bool { matches!( self, JobStatus::Completed @@ -1711,8 +1711,8 @@ impl JobStatus { ) } - pub fn to_int(&self) -> i32 { - match *self { + pub(crate) fn to_int(self) -> i32 { + match self { JobStatus::Uninitialized => 0, JobStatus::Blocked => 1, JobStatus::Ready => 2, @@ -1727,7 +1727,7 @@ impl JobStatus { } } - pub fn from_int(value: i32) -> std::result::Result { + pub(crate) fn from_int(value: i32) -> std::result::Result { match value { 0 => Ok(JobStatus::Uninitialized), 1 => Ok(JobStatus::Blocked), @@ -1744,37 +1744,37 @@ impl JobStatus { } } - pub fn from_i64(value: i64) -> std::result::Result { + pub(crate) fn from_i64(value: i64) -> std::result::Result { Self::from_int(value as i32) } } impl JobsModel { - pub fn new(jobs: Vec) -> JobsModel { + pub(crate) fn new(jobs: Vec) -> JobsModel { JobsModel { jobs } } } impl FilesModel { - pub fn new(files: Vec) -> FilesModel { + pub(crate) fn new(files: Vec) -> FilesModel { FilesModel { files } } } impl CreateFilesResponse { - pub fn new() -> CreateFilesResponse { + fn new() -> CreateFilesResponse { CreateFilesResponse { files: None } } } impl UserDataListModel { - pub fn new(user_data: Vec) -> UserDataListModel { + pub(crate) fn new(user_data: Vec) -> UserDataListModel { UserDataListModel { user_data } } } impl CreateUserDataListResponse { - pub fn new() -> CreateUserDataListResponse { + fn new() -> CreateUserDataListResponse { CreateUserDataListResponse { user_data: None } } } @@ -1819,7 +1819,7 @@ empty_list_response_new!(ListJobUserDataRelationshipsResponse); empty_list_response_new!(ListSlurmStatsResponse); impl ListMissingUserDataResponse { - pub fn new() -> ListMissingUserDataResponse { + fn new() -> ListMissingUserDataResponse { ListMissingUserDataResponse { user_data: Vec::new(), } @@ -1827,13 +1827,13 @@ impl ListMissingUserDataResponse { } impl ListRequiredExistingFilesResponse { - pub fn new() -> ListRequiredExistingFilesResponse { + fn new() -> ListRequiredExistingFilesResponse { ListRequiredExistingFilesResponse { files: Vec::new() } } } impl LocalSchedulerModel { - pub fn new(workflow_id: i64) -> LocalSchedulerModel { + fn new(workflow_id: i64) -> LocalSchedulerModel { LocalSchedulerModel { id: None, workflow_id, @@ -1845,7 +1845,7 @@ impl LocalSchedulerModel { } impl ClaimJobsBasedOnResources { - pub fn new() -> ClaimJobsBasedOnResources { + fn new() -> ClaimJobsBasedOnResources { ClaimJobsBasedOnResources { jobs: None, reason: None, @@ -1854,13 +1854,13 @@ impl ClaimJobsBasedOnResources { } impl ClaimNextJobsResponse { - pub fn new() -> ClaimNextJobsResponse { + fn new() -> ClaimNextJobsResponse { ClaimNextJobsResponse { jobs: None } } } impl ProcessChangedJobInputsResponse { - pub fn new() -> ProcessChangedJobInputsResponse { + fn new() -> ProcessChangedJobInputsResponse { ProcessChangedJobInputsResponse { reinitialized_jobs: vec![], } @@ -1934,12 +1934,7 @@ impl ScheduledComputeNodesModel { } impl SlurmSchedulerModel { - pub fn new( - workflow_id: i64, - account: String, - nodes: i64, - walltime: String, - ) -> SlurmSchedulerModel { + fn new(workflow_id: i64, account: String, nodes: i64, walltime: String) -> SlurmSchedulerModel { SlurmSchedulerModel { id: None, workflow_id, @@ -2003,7 +1998,7 @@ impl WorkflowModel { } impl JobDependencyModel { - pub fn new( + fn new( job_id: i64, job_name: String, depends_on_job_id: i64, @@ -2021,7 +2016,7 @@ impl JobDependencyModel { } impl JobFileRelationshipModel { - pub fn new( + fn new( file_id: i64, file_name: String, file_path: String, @@ -2041,7 +2036,7 @@ impl JobFileRelationshipModel { } impl JobUserDataRelationshipModel { - pub fn new( + fn new( user_data_id: i64, user_data_name: String, workflow_id: i64, @@ -2059,7 +2054,7 @@ impl JobUserDataRelationshipModel { } impl WorkflowActionModel { - pub fn new( + fn new( workflow_id: i64, trigger_type: String, action_type: String, @@ -2084,7 +2079,7 @@ impl WorkflowActionModel { } impl RemoteWorkerModel { - pub fn new(worker: String, workflow_id: i64) -> RemoteWorkerModel { + pub(crate) fn new(worker: String, workflow_id: i64) -> RemoteWorkerModel { RemoteWorkerModel { worker, workflow_id, @@ -2093,7 +2088,11 @@ impl RemoteWorkerModel { } impl ResetJobStatusResponse { - pub fn new(workflow_id: i64, updated_count: i64, status: String) -> ResetJobStatusResponse { + pub(crate) fn new( + workflow_id: i64, + updated_count: i64, + status: String, + ) -> ResetJobStatusResponse { ResetJobStatusResponse { workflow_id, updated_count, @@ -2102,7 +2101,7 @@ impl ResetJobStatusResponse { } } - pub fn with_reset_type(mut self, reset_type: String) -> Self { + pub(crate) fn with_reset_type(mut self, reset_type: String) -> Self { self.reset_type = Some(reset_type); self } @@ -2118,11 +2117,11 @@ impl DeleteCountResponse { } impl VersionResponse { - pub fn is_object(&self) -> bool { + fn is_object(&self) -> bool { true } - pub fn get(&self, key: &str) -> Option { + fn get(&self, key: &str) -> Option { match key { "version" => Some(Value::from(self.version.clone())), "api_version" => Some(Value::from(self.api_version.clone())), @@ -2131,13 +2130,13 @@ impl VersionResponse { } } - pub fn as_str(&self) -> Option<&str> { + fn as_str(&self) -> Option<&str> { Some(self.version.as_str()) } } impl ClaimActionResponse { - pub fn get(&self, key: &str) -> Option { + fn get(&self, key: &str) -> Option { match key { "claimed" => Some(Value::from(self.success)), "success" => Some(Value::from(self.success)), @@ -2148,7 +2147,7 @@ impl ClaimActionResponse { } impl ReloadAuthResponse { - pub fn get(&self, key: &str) -> Option { + fn get(&self, key: &str) -> Option { match key { "message" => Some(Value::from(self.message.clone())), "user_count" => Some(Value::from(self.user_count)), @@ -2165,20 +2164,20 @@ impl IsUninitializedResponse { } } - pub fn as_bool(&self) -> Option { + fn as_bool(&self) -> Option { Some(self.is_uninitialized) } } impl ListJobIdsResponse { - pub fn new(job_ids: Vec) -> ListJobIdsResponse { + pub(crate) fn new(job_ids: Vec) -> ListJobIdsResponse { let count = job_ids.len() as i64; ListJobIdsResponse { job_ids, count } } } impl AccessGroupModel { - pub fn new(name: String) -> AccessGroupModel { + pub(crate) fn new(name: String) -> AccessGroupModel { AccessGroupModel { id: None, name, @@ -2189,7 +2188,7 @@ impl AccessGroupModel { } impl UserGroupMembershipModel { - pub fn new(user_name: String, group_id: i64) -> UserGroupMembershipModel { + pub(crate) fn new(user_name: String, group_id: i64) -> UserGroupMembershipModel { UserGroupMembershipModel { id: None, user_name, @@ -2201,7 +2200,7 @@ impl UserGroupMembershipModel { } impl WorkflowAccessGroupModel { - pub fn new(workflow_id: i64, group_id: i64) -> WorkflowAccessGroupModel { + fn new(workflow_id: i64, group_id: i64) -> WorkflowAccessGroupModel { WorkflowAccessGroupModel { workflow_id, group_id, @@ -2211,7 +2210,12 @@ impl WorkflowAccessGroupModel { } impl ListAccessGroupsResponse { - pub fn new(items: Vec, offset: i64, limit: i64, total_count: i64) -> Self { + pub(crate) fn new( + items: Vec, + offset: i64, + limit: i64, + total_count: i64, + ) -> Self { let has_more = offset + (items.len() as i64) < total_count; ListAccessGroupsResponse { items, @@ -2224,7 +2228,7 @@ impl ListAccessGroupsResponse { } impl ListUserGroupMembershipsResponse { - pub fn new( + pub(crate) fn new( items: Vec, offset: i64, limit: i64, @@ -2333,8 +2337,8 @@ pub struct RoCrateEntityModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListRoCrateEntitiesResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, pub count: i64, pub total_count: i64, pub has_more: bool, @@ -2343,13 +2347,13 @@ pub struct ListRoCrateEntitiesResponse { #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct MessageResponse { - pub message: String, + message: String, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct DeleteRoCrateEntitiesResponse { - pub message: String, + message: String, pub deleted_count: i64, } @@ -2365,23 +2369,23 @@ pub struct ReloadAuthResponse { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AdminSqlRequest { /// The single SQL statement to execute. - pub sql: String, + pub(crate) sql: String, /// Opt into the write path. When false (default) the statement runs on a /// read-only connection, so any write fails at the SQLite layer. #[serde(default)] - pub write: bool, + pub(crate) write: bool, /// Permit an unqualified UPDATE/DELETE (no WHERE clause). Ignored on the /// read-only path. #[serde(default)] - pub allow_full_table: bool, + pub(crate) allow_full_table: bool, /// Write path only: run inside a transaction, report rows affected, then /// roll back instead of committing (preview). #[serde(default)] - pub dry_run: bool, + pub(crate) dry_run: bool, /// Maximum number of SELECT result rows to return. Defaults to and is capped /// at 100,000 (the server-wide list cap); values above the cap are clamped. #[serde(default, skip_serializing_if = "Option::is_none")] - pub limit: Option, + pub(crate) limit: Option, } /// Response body for the admin raw-SQL endpoint (`POST /admin/sql`). @@ -2391,14 +2395,14 @@ pub struct AdminSqlResponse { /// Column names in result order, defining how `items` is displayed (empty for /// write statements). Names the query repeats are suffixed (`id`, `id_2`, ...) /// so each item's keys stay unique. - pub columns: Vec, + pub(crate) columns: Vec, /// Result rows as objects keyed by `columns` (empty for write statements). - pub items: Vec>, + pub(crate) items: Vec>, /// Number of rows affected by a write statement, when applicable. #[serde(skip_serializing_if = "Option::is_none")] - pub rows_affected: Option, + pub(crate) rows_affected: Option, /// True when a write was committed to the database. - pub committed: bool, + pub(crate) committed: bool, } /// One row of the admin raw-SQL audit log (`admin_audit_log`), returned by @@ -2407,27 +2411,27 @@ pub struct AdminSqlResponse { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AdminAuditLogEntry { /// Auto-increment row id. - pub id: i64, + pub(crate) id: i64, /// User that executed the statement. - pub user_name: String, + pub(crate) user_name: String, /// Execution time in milliseconds since the Unix epoch. - pub timestamp: i64, + pub(crate) timestamp: i64, /// The SQL statement text. - pub sql_text: String, + pub(crate) sql_text: String, /// True for write-path statements (all audited rows are writes). - pub is_write: bool, + pub(crate) is_write: bool, /// True when the full-table guard was overridden for this statement. - pub allow_full_table: bool, + pub(crate) allow_full_table: bool, /// Rows affected by the statement, when known. #[serde(skip_serializing_if = "Option::is_none")] - pub rows_affected: Option, + pub(crate) rows_affected: Option, /// True when the write was committed to the database. - pub committed: bool, + pub(crate) committed: bool, /// True when the statement executed without error. - pub success: bool, + pub(crate) success: bool, /// Error message captured for a failed statement, when applicable. #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, + pub(crate) error: Option, } /// Paginated response for `GET /admin/audit-log` (entries newest first). @@ -2435,30 +2439,30 @@ pub struct AdminAuditLogEntry { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ListAdminAuditLogResponse { /// Audit-log entries, newest first. - pub items: Vec, + pub(crate) items: Vec, /// Offset applied to this page. - pub offset: i64, + pub(crate) offset: i64, /// Maximum page size enforced by the server. - pub max_limit: i64, + pub(crate) max_limit: i64, /// Number of entries returned in this page. - pub count: i64, + pub(crate) count: i64, /// Total number of audit-log entries. - pub total_count: i64, + pub(crate) total_count: i64, /// True when more entries exist beyond this page. - pub has_more: bool, + pub(crate) has_more: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct IsCompleteResponse { - pub is_canceled: bool, + pub(crate) is_canceled: bool, pub is_complete: bool, } #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct IsUninitializedResponse { - pub is_uninitialized: bool, + pub(crate) is_uninitialized: bool, } /// Counts of jobs grouped by status for a single workflow. @@ -2490,9 +2494,9 @@ pub struct WorkflowStatusResponse { pub total_exec_time_minutes: f64, #[serde(skip_serializing_if = "Option::is_none")] pub walltime_seconds: Option, - pub active_compute_nodes: i64, - pub pending_scheduled_nodes: i64, - pub active_scheduled_nodes: i64, + pub(crate) active_compute_nodes: i64, + pub(crate) pending_scheduled_nodes: i64, + pub(crate) active_scheduled_nodes: i64, pub is_complete: bool, pub is_canceled: bool, /// Ready jobs whose required runtime exceeds the remaining walltime of every @@ -2525,9 +2529,9 @@ pub struct SlurmJobCorrelationModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SlurmJobCorrelationsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, pub total_count: i64, pub has_more: bool, } @@ -2555,9 +2559,9 @@ pub struct RunningJobModel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RunningJobsResponse { pub items: Vec, - pub offset: i64, - pub max_limit: i64, - pub count: i64, + pub(crate) offset: i64, + pub(crate) max_limit: i64, + pub(crate) count: i64, pub total_count: i64, pub has_more: bool, } @@ -2565,11 +2569,11 @@ pub struct RunningJobsResponse { #[cfg_attr(feature = "openapi-codegen", derive(utoipa::ToSchema))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ResetJobStatusResponse { - pub workflow_id: i64, - pub updated_count: i64, - pub status: String, + workflow_id: i64, + pub(crate) updated_count: i64, + status: String, #[serde(skip_serializing_if = "Option::is_none")] - pub reset_type: Option, + reset_type: Option, } #[cfg(test)] @@ -2871,11 +2875,11 @@ pub struct TaskModel { pub status: TaskStatus, #[serde(rename = "created_at_ms")] - pub created_at_ms: i64, + pub(crate) created_at_ms: i64, #[serde(rename = "started_at_ms")] #[serde(skip_serializing_if = "Option::is_none")] - pub started_at_ms: Option, + pub(crate) started_at_ms: Option, #[serde(rename = "finished_at_ms")] #[serde(skip_serializing_if = "Option::is_none")] @@ -2887,7 +2891,7 @@ pub struct TaskModel { } impl TaskModel { - pub fn new( + pub(crate) fn new( id: i64, workflow_id: i64, operation: String, diff --git a/src/network_utils.rs b/src/network_utils.rs index 60b350d85..38666c4b4 100644 --- a/src/network_utils.rs +++ b/src/network_utils.rs @@ -6,7 +6,7 @@ use std::io::ErrorKind; use std::net::TcpListener; /// Maximum number of ports to try when searching for an available port. -pub const MAX_PORT_ATTEMPTS: u16 = 100; +const MAX_PORT_ATTEMPTS: u16 = 100; /// Try to bind to a port, incrementing if the port is in use. /// diff --git a/src/openapi_spec.rs b/src/openapi_spec.rs index e3d57053f..b9651645a 100644 --- a/src/openapi_spec.rs +++ b/src/openapi_spec.rs @@ -252,23 +252,23 @@ mod openapi_task_paths { #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct PingResponse { - pub status: String, + pub(crate) status: String, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct VersionResponse { - pub version: String, - pub api_version: String, + pub(crate) version: String, + pub(crate) api_version: String, #[serde(skip_serializing_if = "Option::is_none")] - pub git_hash: Option, + pub(crate) git_hash: Option, } #[derive(Debug, Clone)] pub struct OpenApiAppState { - pub version: String, - pub api_version: String, - pub git_hash: String, - pub access_control_enabled: bool, + pub(crate) version: String, + pub(crate) api_version: String, + pub(crate) git_hash: String, + pub(crate) access_control_enabled: bool, } impl Default for OpenApiAppState { @@ -679,7 +679,7 @@ fn resolve_schema_properties<'a>( description = "Rust-owned OpenAPI surface for Torc." ) )] -pub struct TorcOpenApi; +struct TorcOpenApi; fn openapi_doc() -> utoipa::openapi::OpenApi { let mut doc = TorcOpenApi::openapi(); @@ -859,7 +859,7 @@ fn flatten_nullable_refs_yaml(value: &mut serde_yaml::Value) { } } -pub fn openapi_value() -> Value { +fn openapi_value() -> Value { let mut value = serde_json::to_value(openapi_doc()).expect("OpenAPI document should serialize"); apply_env_property_name_pattern_json(&mut value); flatten_nullable_refs_json(&mut value); diff --git a/src/plot_resources_cmd.rs b/src/plot_resources_cmd.rs index 4f1395380..5e377804d 100644 --- a/src/plot_resources_cmd.rs +++ b/src/plot_resources_cmd.rs @@ -13,24 +13,24 @@ use std::path::{Path, PathBuf}; pub struct Args { /// Path to the resource metrics database file(s) #[arg(required = true)] - pub db_paths: Vec, + pub(crate) db_paths: Vec, /// Output directory for generated plots (default: current directory) #[arg(short, long, default_value = ".")] - pub output_dir: PathBuf, + pub(crate) output_dir: PathBuf, /// Only plot specific job IDs (comma-separated) #[arg(short, long, value_delimiter = ',')] - pub job_ids: Vec, + pub(crate) job_ids: Vec, /// Optional prefix for output filenames. When empty, files are named e.g. `job_4.html`, /// `summary.html`, `system_timeline.html`. #[arg(short = 'p', long, default_value = "")] - pub prefix: String, + pub(crate) prefix: String, /// Output format: html or json #[arg(short = 'f', long, default_value = "html")] - pub format: String, + pub(crate) format: String, } #[derive(Debug, Clone)] diff --git a/src/ro_crate_json_ld.rs b/src/ro_crate_json_ld.rs index 593b11c7c..a8ace069e 100644 --- a/src/ro_crate_json_ld.rs +++ b/src/ro_crate_json_ld.rs @@ -1,3 +1,3 @@ -pub fn typed_entity(primary_type: &str, prov_type: &str) -> serde_json::Value { +pub(crate) fn typed_entity(primary_type: &str, prov_type: &str) -> serde_json::Value { serde_json::json!([primary_type, prov_type]) } diff --git a/src/run_jobs_cmd.rs b/src/run_jobs_cmd.rs index 6cd3c7202..089b5e49a 100644 --- a/src/run_jobs_cmd.rs +++ b/src/run_jobs_cmd.rs @@ -18,7 +18,7 @@ use std::io::Write; use std::path::PathBuf; use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System}; -pub enum LogStream { +pub(crate) enum LogStream { Stdout, Stderr, } @@ -163,7 +163,7 @@ pub fn run(args: &Args) { let _ = run_with_log_stream(args, LogStream::Stdout); } -pub fn run_with_log_stream(args: &Args, log_stream: LogStream) -> WorkerResult { +pub(crate) fn run_with_log_stream(args: &Args, log_stream: LogStream) -> WorkerResult { let hostname = hostname::get() .expect("Failed to get hostname") .into_string() diff --git a/src/server.rs b/src/server.rs index 63599798b..c0d380649 100644 --- a/src/server.rs +++ b/src/server.rs @@ -4,7 +4,7 @@ //! authentication, transport, and database operations. pub mod api; -pub mod api_constants; +pub(crate) mod api_constants; pub mod api_contract; pub mod api_event_stream; pub mod api_responses; @@ -13,7 +13,7 @@ pub mod auth; pub mod authorization; pub mod context; pub mod credential_cache; -pub mod dashboard; +pub(crate) mod dashboard; pub mod event_broadcast; pub mod header; pub mod htpasswd; @@ -23,14 +23,14 @@ pub mod export; #[cfg(any(feature = "server-bin", feature = "openapi-codegen"))] pub mod http_server; #[cfg(feature = "openapi-codegen")] -pub mod http_transport; +pub(crate) mod http_transport; #[cfg(feature = "openapi-codegen")] pub mod live_router; #[cfg(any(feature = "server-bin", feature = "openapi-codegen"))] pub mod live_state; #[cfg(feature = "server-bin")] pub mod logging; -pub mod response_types; +pub(crate) mod response_types; #[cfg(feature = "server-bin")] pub mod service; pub mod transport_types; diff --git a/src/server/api.rs b/src/server/api.rs index 48be2465f..b435b2536 100644 --- a/src/server/api.rs +++ b/src/server/api.rs @@ -12,11 +12,11 @@ pub use crate::MAX_RECORD_TRANSFER_COUNT; /// Shared server context that all API modules can use #[derive(Clone)] pub struct ApiContext { - pub pool: Arc, + pool: Arc, } impl ApiContext { - pub fn new(pool: SqlitePool) -> Self { + pub(crate) fn new(pool: SqlitePool) -> Self { Self { pool: Arc::new(pool), } @@ -24,7 +24,10 @@ impl ApiContext { } /// Common error handling utilities -pub fn database_error_with_msg(e: impl std::fmt::Display, msg: impl Into) -> ApiError { +pub(crate) fn database_error_with_msg( + e: impl std::fmt::Display, + msg: impl Into, +) -> ApiError { let msg_str = msg.into(); error!("Database error ({}): {}", msg_str, e); ApiError(msg_str) @@ -34,7 +37,10 @@ pub fn database_error_with_msg(e: impl std::fmt::Display, msg: impl Into /// so that callers can detect lock contention and retry. Does not leak other database /// error details. Lock contention is logged at debug level (expected transient condition) /// while other database errors are logged at error level. -pub fn database_lock_aware_error(e: impl std::fmt::Display, msg: impl Into) -> ApiError { +pub(crate) fn database_lock_aware_error( + e: impl std::fmt::Display, + msg: impl Into, +) -> ApiError { let msg_str = msg.into(); let error_string = e.to_string().to_lowercase(); if error_string.contains("database is locked") @@ -49,16 +55,18 @@ pub fn database_lock_aware_error(e: impl std::fmt::Display, msg: impl Into ApiError { +pub(crate) fn json_parse_error(e: impl std::fmt::Display) -> ApiError { info!("Failed to parse JSON data: {}", e); ApiError("Failed to parse event data".to_string()) } -pub fn normalize_env_map(env: Option>) -> Option> { +pub(crate) fn normalize_env_map( + env: Option>, +) -> Option> { env.filter(|env_map| !env_map.is_empty()) } -pub fn serialize_env_map( +pub(crate) fn serialize_env_map( env: Option>, field_name: &str, ) -> Result, ApiError> { @@ -70,7 +78,7 @@ pub fn serialize_env_map( .transpose() } -pub fn deserialize_env_map( +pub(crate) fn deserialize_env_map( env_json: Option, field_name: &str, ) -> Result>, ApiError> { @@ -87,7 +95,7 @@ pub fn deserialize_env_map( .map(normalize_env_map) } -pub fn validate_env_map( +pub(crate) fn validate_env_map( env: Option<&HashMap>, field_name: &str, ) -> Result<(), ApiError> { @@ -121,7 +129,7 @@ pub fn validate_env_map( /// applies to lock acquisition and the snapshot-conflict path is impossible. Use /// this helper for any handler that mixes reads and writes inside a single /// transaction. -pub async fn begin_immediate( +pub(crate) async fn begin_immediate( pool: &SqlitePool, ) -> Result, sqlx::Error> { pool.begin_with("BEGIN IMMEDIATE").await @@ -136,7 +144,10 @@ pub async fn begin_immediate( /// `let status = parse_job_status(status_int, job_id)?;` instead of a 7-line /// match. The `job_id` is included in the log line so the offending row can /// be located. -pub fn parse_job_status(status_int: i32, job_id: i64) -> Result { +pub(crate) fn parse_job_status( + status_int: i32, + job_id: i64, +) -> Result { models::JobStatus::from_int(status_int).map_err(|e| { error!( "Failed to parse job status job_id={} status={} error={}", @@ -154,7 +165,7 @@ pub fn parse_job_status(status_int: i32, job_id: i64) -> Result) -> models::ErrorResponse { +pub(crate) fn message_error_response(message: impl Into) -> models::ErrorResponse { models::ErrorResponse::new(serde_json::json!({"message": message.into()})) } @@ -163,7 +174,7 @@ pub fn message_error_response(message: impl Into) -> models::ErrorRespon /// `resource` is the human-readable resource name (e.g., `"Workflow"`, `"Job"`). /// The wording matches what handlers already produce, so error bodies remain /// stable for clients. -pub fn resource_not_found_response( +pub(crate) fn resource_not_found_response( resource: &str, id: impl std::fmt::Display, ) -> models::ErrorResponse { @@ -172,7 +183,7 @@ pub fn resource_not_found_response( /// Escape SQL LIKE wildcard characters in user input. /// Escapes `%`, `_`, and `\` with a backslash prefix. -pub fn escape_like_pattern(input: &str) -> String { +pub(crate) fn escape_like_pattern(input: &str) -> String { input .replace('\\', "\\\\") .replace('%', "\\%") @@ -348,13 +359,13 @@ macro_rules! paginated_list_response { /// Common pagination response structure #[derive(Debug)] pub struct PaginationInfo { - pub offset: i64, - pub limit: Option, - pub total_count: i64, + offset: i64, + limit: Option, + total_count: i64, } impl PaginationInfo { - pub fn new(offset: Option, limit: Option, total_count: i64) -> Self { + fn new(offset: Option, limit: Option, total_count: i64) -> Self { Self { offset: offset.unwrap_or(0), limit, @@ -362,7 +373,7 @@ impl PaginationInfo { } } - pub fn has_more(&self) -> bool { + fn has_more(&self) -> bool { if let Some(limit) = self.limit { self.offset + limit < self.total_count } else { @@ -385,25 +396,25 @@ pub mod results; pub mod ro_crate; pub mod schedulers; pub mod slurm_stats; -pub mod sql_query_builder; +pub(crate) mod sql_query_builder; pub mod user_data; pub mod workflow_actions; pub mod workflows; // Re-export API traits and implementations -pub use access_groups::{AccessGroupsApi, AccessGroupsApiImpl}; -pub use compute_nodes::{ComputeNodesApi, ComputeNodesApiImpl}; -pub use events::{EventsApi, EventsApiImpl}; -pub use failure_handlers::{FailureHandlersApi, FailureHandlersApiImpl}; -pub use files::{FilesApi, FilesApiImpl}; -pub use jobs::{JobsApi, JobsApiImpl}; -pub use remote_workers::{RemoteWorkersApi, RemoteWorkersApiImpl}; -pub use resource_requirements::{ResourceRequirementsApi, ResourceRequirementsApiImpl}; -pub use results::{ResultsApi, ResultsApiImpl}; -pub use ro_crate::{RoCrateApi, RoCrateApiImpl}; -pub use schedulers::{SchedulersApi, SchedulersApiImpl}; -pub use slurm_stats::{SlurmStatsApi, SlurmStatsApiImpl}; -pub use sql_query_builder::SqlQueryBuilder; -pub use user_data::{UserDataApi, UserDataApiImpl}; -pub use workflow_actions::{WorkflowActionsApi, WorkflowActionsApiImpl}; -pub use workflows::{WorkflowsApi, WorkflowsApiImpl}; +pub(crate) use access_groups::{AccessGroupsApi, AccessGroupsApiImpl}; +pub(crate) use compute_nodes::{ComputeNodesApi, ComputeNodesApiImpl}; +pub(crate) use events::{EventsApi, EventsApiImpl}; +pub(crate) use failure_handlers::{FailureHandlersApi, FailureHandlersApiImpl}; +pub(crate) use files::{FilesApi, FilesApiImpl}; +pub(crate) use jobs::{JobsApi, JobsApiImpl}; +pub(crate) use remote_workers::{RemoteWorkersApi, RemoteWorkersApiImpl}; +pub(crate) use resource_requirements::{ResourceRequirementsApi, ResourceRequirementsApiImpl}; +pub(crate) use results::{ResultsApi, ResultsApiImpl}; +pub(crate) use ro_crate::{RoCrateApi, RoCrateApiImpl}; +pub(crate) use schedulers::{SchedulersApi, SchedulersApiImpl}; +pub(crate) use slurm_stats::{SlurmStatsApi, SlurmStatsApiImpl}; +pub(crate) use sql_query_builder::SqlQueryBuilder; +pub(crate) use user_data::{UserDataApi, UserDataApiImpl}; +pub(crate) use workflow_actions::{WorkflowActionsApi, WorkflowActionsApiImpl}; +pub(crate) use workflows::{WorkflowsApi, WorkflowsApiImpl}; diff --git a/src/server/api/access_groups.rs b/src/server/api/access_groups.rs index 7d1d61426..70ea07260 100644 --- a/src/server/api/access_groups.rs +++ b/src/server/api/access_groups.rs @@ -110,16 +110,16 @@ pub trait AccessGroupsApi { /// Implementation of access groups API for the server #[derive(Clone)] pub struct AccessGroupsApiImpl { - pub context: ApiContext, + context: ApiContext, } impl AccessGroupsApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } /// Get all group IDs that a user belongs to - pub async fn get_user_group_ids(&self, user_name: &str) -> Result, ApiError> { + async fn get_user_group_ids(&self, user_name: &str) -> Result, ApiError> { let records = match sqlx::query("SELECT group_id FROM user_group_membership WHERE user_name = $1") .bind(user_name) @@ -136,7 +136,7 @@ impl AccessGroupsApiImpl { } /// Check if user can access workflow (used internally by other APIs) - pub async fn check_workflow_access_internal( + async fn check_workflow_access_internal( &self, user_name: &str, workflow_id: i64, @@ -195,7 +195,7 @@ impl AccessGroupsApiImpl { // Group CRUD operations // ======================================================================== - pub async fn create_access_group( + pub(crate) async fn create_access_group( &self, body: models::AccessGroupModel, context: &C, @@ -244,7 +244,7 @@ impl AccessGroupsApiImpl { Ok(CreateAccessGroupResponse::SuccessfulResponse(group)) } - pub async fn get_access_group( + pub(crate) async fn get_access_group( &self, id: i64, context: &C, @@ -293,7 +293,7 @@ impl AccessGroupsApiImpl { Ok(GetAccessGroupResponse::SuccessfulResponse(group)) } - pub async fn list_access_groups( + pub(crate) async fn list_access_groups( &self, offset: i64, limit: i64, @@ -361,7 +361,7 @@ impl AccessGroupsApiImpl { Ok(ListAccessGroupsApiResponse::SuccessfulResponse(response)) } - pub async fn delete_access_group( + pub(crate) async fn delete_access_group( &self, id: i64, context: &C, @@ -414,7 +414,7 @@ impl AccessGroupsApiImpl { // User-Group membership operations // ======================================================================== - pub async fn add_user_to_group( + pub(crate) async fn add_user_to_group( &self, group_id: i64, body: models::UserGroupMembershipModel, @@ -481,7 +481,7 @@ impl AccessGroupsApiImpl { Ok(AddUserToGroupResponse::SuccessfulResponse(membership)) } - pub async fn remove_user_from_group( + pub(crate) async fn remove_user_from_group( &self, group_id: i64, user_name: &str, @@ -549,7 +549,7 @@ impl AccessGroupsApiImpl { } } - pub async fn list_group_members( + pub(crate) async fn list_group_members( &self, group_id: i64, offset: i64, @@ -639,7 +639,7 @@ impl AccessGroupsApiImpl { Ok(ListGroupMembersResponse::SuccessfulResponse(response)) } - pub async fn list_user_groups( + pub(crate) async fn list_user_groups( &self, user_name: &str, offset: i64, @@ -719,7 +719,7 @@ impl AccessGroupsApiImpl { // Workflow-Group association operations // ======================================================================== - pub async fn add_workflow_to_group( + pub(crate) async fn add_workflow_to_group( &self, workflow_id: i64, group_id: i64, @@ -814,7 +814,7 @@ impl AccessGroupsApiImpl { Ok(AddWorkflowToGroupResponse::SuccessfulResponse(association)) } - pub async fn remove_workflow_from_group( + pub(crate) async fn remove_workflow_from_group( &self, workflow_id: i64, group_id: i64, @@ -882,7 +882,7 @@ impl AccessGroupsApiImpl { } } - pub async fn list_workflow_groups( + pub(crate) async fn list_workflow_groups( &self, workflow_id: i64, offset: i64, @@ -983,7 +983,7 @@ impl AccessGroupsApiImpl { // Authorization check // ======================================================================== - pub async fn check_workflow_access( + pub(crate) async fn check_workflow_access( &self, workflow_id: i64, user_name: &str, diff --git a/src/server/api/admin.rs b/src/server/api/admin.rs index fc37573b5..dfcee526d 100644 --- a/src/server/api/admin.rs +++ b/src/server/api/admin.rs @@ -48,7 +48,7 @@ pub enum AdminSqlError { impl AdminSqlError { /// The human-readable message, regardless of kind. - pub fn message(&self) -> &str { + pub(crate) fn message(&self) -> &str { match self { AdminSqlError::User(m) | AdminSqlError::Internal(m) => m, } @@ -56,7 +56,7 @@ impl AdminSqlError { /// Whether this is a server-side failure (HTTP 500) rather than a caller /// SQL error (HTTP 422). - pub fn is_internal(&self) -> bool { + pub(crate) fn is_internal(&self) -> bool { matches!(self, AdminSqlError::Internal(_)) } } @@ -64,7 +64,7 @@ impl AdminSqlError { /// Clamp a caller-supplied row limit into `1..=MAX_RECORD_TRANSFER_COUNT`. The /// admin endpoints (`admin sql` SELECTs and the audit-log listing) use the same /// row cap (100,000) as the standard list endpoints. -pub fn clamp_limit(limit: Option) -> usize { +pub(crate) fn clamp_limit(limit: Option) -> usize { match limit { Some(n) if n > 0 => n.min(MAX_RECORD_TRANSFER_COUNT) as usize, _ => MAX_RECORD_TRANSFER_COUNT as usize, @@ -104,7 +104,11 @@ pub fn clamp_limit(limit: Option) -> usize { /// full SQL parser: a `WHERE` inside a subquery can satisfy the guard. /// /// Returns `Err(message)` describing a 422-class rejection. -pub fn validate_statement(sql: &str, is_write: bool, allow_full_table: bool) -> Result<(), String> { +pub(crate) fn validate_statement( + sql: &str, + is_write: bool, + allow_full_table: bool, +) -> Result<(), String> { if sql.trim().is_empty() { return Err("SQL statement is empty".to_string()); } @@ -315,7 +319,7 @@ fn depth0_keywords(upper: &str) -> Vec<&str> { /// `query_only` are server-side failures ([`Internal`](AdminSqlError::Internal), /// 500); a statement that fails to run is the caller's fault /// ([`User`](AdminSqlError::User), 422). -pub async fn execute_read_only( +pub(crate) async fn execute_read_only( pool: &SqlitePool, sql: &str, limit: usize, @@ -414,9 +418,9 @@ fn dedupe_columns(raw: Vec) -> Vec { } /// Attribution recorded alongside a committing write in `admin_audit_log`. -pub struct AuditContext<'a> { - pub user_name: &'a str, - pub allow_full_table: bool, +pub(crate) struct AuditContext<'a> { + pub(crate) user_name: &'a str, + pub(crate) allow_full_table: bool, } /// Execute a write statement inside a transaction. @@ -439,7 +443,7 @@ pub struct AuditContext<'a> { /// caller's fault ([`User`](AdminSqlError::User), 422); transaction begin/commit, /// rollback, and the atomic audit insert are server-side failures /// ([`Internal`](AdminSqlError::Internal), 500). -pub async fn execute_write( +pub(crate) async fn execute_write( pool: &SqlitePool, sql: &str, dry_run: bool, @@ -499,7 +503,7 @@ pub async fn execute_write( /// [`execute_write`] instead. Read-only queries and dry-run previews are not /// audited. #[allow(clippy::too_many_arguments)] -pub async fn record_audit( +pub(crate) async fn record_audit( pool: &SqlitePool, user_name: &str, sql: &str, @@ -566,7 +570,7 @@ where /// Reads run on the shared pool: the audit log is append-only and admin-only, so /// no read-only connection is needed here. Returns the page of typed entries and /// the unpaginated total for pagination metadata. -pub async fn list_audit_log( +pub(crate) async fn list_audit_log( pool: &SqlitePool, offset: i64, limit: i64, diff --git a/src/server/api/compute_nodes.rs b/src/server/api/compute_nodes.rs index 6b428cfcc..6aa367112 100644 --- a/src/server/api/compute_nodes.rs +++ b/src/server/api/compute_nodes.rs @@ -73,7 +73,7 @@ pub trait ComputeNodesApi { /// Implementation of compute nodes API for the server #[derive(Clone)] pub struct ComputeNodesApiImpl { - pub context: ApiContext, + context: ApiContext, } const COMPUTE_NODE_COLUMNS: &[&str] = &[ @@ -100,7 +100,7 @@ const COMPUTE_NODE_COLUMNS: &[&str] = &[ ]; impl ComputeNodesApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } diff --git a/src/server/api/events.rs b/src/server/api/events.rs index 0b3ca0afa..c9ec8187c 100644 --- a/src/server/api/events.rs +++ b/src/server/api/events.rs @@ -68,13 +68,13 @@ pub trait EventsApi { /// Implementation of events API for the server #[derive(Clone)] pub struct EventsApiImpl { - pub context: ApiContext, + context: ApiContext, } const EVENT_COLUMNS: &[&str] = &["id", "workflow_id", "timestamp", "data"]; impl EventsApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } diff --git a/src/server/api/failure_handlers.rs b/src/server/api/failure_handlers.rs index 8339ede01..d80680ecb 100644 --- a/src/server/api/failure_handlers.rs +++ b/src/server/api/failure_handlers.rs @@ -54,11 +54,11 @@ pub trait FailureHandlersApi { /// Implementation of failure handlers API for the server #[derive(Clone)] pub struct FailureHandlersApiImpl { - pub context: ApiContext, + context: ApiContext, } impl FailureHandlersApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } diff --git a/src/server/api/files.rs b/src/server/api/files.rs index 6f7177802..e287e570f 100644 --- a/src/server/api/files.rs +++ b/src/server/api/files.rs @@ -83,13 +83,13 @@ pub trait FilesApi { /// Implementation of files API for the server #[derive(Clone)] pub struct FilesApiImpl { - pub context: ApiContext, + context: ApiContext, } const FILE_COLUMNS: &[&str] = &["id", "workflow_id", "name", "path", "st_mtime"]; impl FilesApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } diff --git a/src/server/api/jobs.rs b/src/server/api/jobs.rs index 42ac8395a..58d961521 100644 --- a/src/server/api/jobs.rs +++ b/src/server/api/jobs.rs @@ -228,7 +228,7 @@ pub trait JobsApi { /// Implementation of jobs API for the server #[derive(Clone)] pub struct JobsApiImpl { - pub context: ApiContext, + context: ApiContext, } const JOB_COLUMNS: &[&str] = &[ @@ -270,7 +270,7 @@ fn vec_id_set_changed( } impl JobsApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } @@ -809,7 +809,7 @@ impl JobsApiImpl { /// - depends_on_job_ids /// /// The hash is used to detect if job inputs have changed, requiring re-execution. - pub async fn compute_job_input_hash(&self, job_id: i64) -> Result { + async fn compute_job_input_hash(&self, job_id: i64) -> Result { // Get the job with all relationships let job = self.get_job_with_relationships(job_id).await?; @@ -887,7 +887,7 @@ impl JobsApiImpl { /// Store job input hash in job_internal table /// /// Uses INSERT ON CONFLICT to upsert - will insert new record or update existing one. - pub async fn store_job_input_hash(&self, job_id: i64, hash: &str) -> Result<(), ApiError> { + async fn store_job_input_hash(&self, job_id: i64, hash: &str) -> Result<(), ApiError> { match sqlx::query!( r#" INSERT INTO job_internal (job_id, input_hash) @@ -916,7 +916,7 @@ impl JobsApiImpl { /// This is much more efficient than calling `compute_job_input_hash` per job because it /// fetches all relationship data in a small number of bulk queries instead of 7+ queries /// per job. For a workflow with 100K jobs, this reduces ~700K sequential queries to ~7. - pub async fn compute_and_store_all_input_hashes( + pub(crate) async fn compute_and_store_all_input_hashes( &self, workflow_id: i64, ) -> Result<(), ApiError> { @@ -1201,7 +1201,7 @@ impl JobsApiImpl { /// Get stored job input hash from job_internal table /// /// Returns None if no hash has been stored for this job yet. - pub async fn get_stored_job_input_hash(&self, job_id: i64) -> Result, ApiError> { + async fn get_stored_job_input_hash(&self, job_id: i64) -> Result, ApiError> { match sqlx::query!( r#" SELECT input_hash diff --git a/src/server/api/remote_workers.rs b/src/server/api/remote_workers.rs index b66ec9d3e..fa4a465d7 100644 --- a/src/server/api/remote_workers.rs +++ b/src/server/api/remote_workers.rs @@ -44,11 +44,11 @@ pub trait RemoteWorkersApi { /// Implementation of remote workers API for the server #[derive(Clone)] pub struct RemoteWorkersApiImpl { - pub context: ApiContext, + context: ApiContext, } impl RemoteWorkersApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } diff --git a/src/server/api/resource_requirements.rs b/src/server/api/resource_requirements.rs index 5f62fadc1..909ff4d44 100644 --- a/src/server/api/resource_requirements.rs +++ b/src/server/api/resource_requirements.rs @@ -80,7 +80,7 @@ pub trait ResourceRequirementsApi { /// Implementation of resource requirements API for the server #[derive(Clone)] pub struct ResourceRequirementsApiImpl { - pub context: ApiContext, + context: ApiContext, } const RESOURCE_REQUIREMENTS_COLUMNS: &[&str] = &[ @@ -95,7 +95,7 @@ const RESOURCE_REQUIREMENTS_COLUMNS: &[&str] = &[ ]; impl ResourceRequirementsApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } diff --git a/src/server/api/results.rs b/src/server/api/results.rs index 79517ac21..bd89fec2c 100644 --- a/src/server/api/results.rs +++ b/src/server/api/results.rs @@ -71,7 +71,7 @@ pub trait ResultsApi { /// Implementation of results API for the server #[derive(Clone)] pub struct ResultsApiImpl { - pub context: ApiContext, + context: ApiContext, } const RESULT_COLUMNS: &[&str] = &[ @@ -92,7 +92,7 @@ const RESULT_COLUMNS: &[&str] = &[ ]; impl ResultsApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } diff --git a/src/server/api/ro_crate.rs b/src/server/api/ro_crate.rs index 066043998..3929482c6 100644 --- a/src/server/api/ro_crate.rs +++ b/src/server/api/ro_crate.rs @@ -150,11 +150,11 @@ pub trait RoCrateApi { /// Implementation of RO-Crate entity API for the server #[derive(Clone)] pub struct RoCrateApiImpl { - pub context: ApiContext, + context: ApiContext, } impl RoCrateApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } @@ -166,7 +166,10 @@ impl RoCrateApiImpl { /// creates new entities otherwise. /// /// This is called during `initialize_jobs` when `enable_ro_crate` is true. - pub async fn create_entities_for_input_files(&self, workflow_id: i64) -> Result { + pub(crate) async fn create_entities_for_input_files( + &self, + workflow_id: i64, + ) -> Result { // Get all files with st_mtime set (input files) let input_files = match sqlx::query!( r#" @@ -315,7 +318,10 @@ impl RoCrateApiImpl { /// entity with `#software-torc-server-run-id-{run_id}` already exists for this workflow. /// /// Called during `initialize_jobs` regardless of `enable_ro_crate`. - pub async fn create_server_software_entity(&self, workflow_id: i64) -> Result<(), ApiError> { + pub(crate) async fn create_server_software_entity( + &self, + workflow_id: i64, + ) -> Result<(), ApiError> { // Get the current run_id from workflow let run_id: i64 = sqlx::query_scalar!("SELECT run_id FROM workflow WHERE id = $1", workflow_id,) diff --git a/src/server/api/schedulers.rs b/src/server/api/schedulers.rs index 1e03e8db4..385e075f3 100644 --- a/src/server/api/schedulers.rs +++ b/src/server/api/schedulers.rs @@ -175,7 +175,7 @@ pub trait SchedulersApi { /// Implementation of schedulers API for the server #[derive(Clone)] pub struct SchedulersApiImpl { - pub context: ApiContext, + context: ApiContext, } const LOCAL_SCHEDULER_COLUMNS: &[&str] = &["id", "workflow_id", "memory", "num_cpus"]; @@ -207,7 +207,7 @@ const SLURM_SCHEDULER_COLUMNS: &[&str] = &[ ]; impl SchedulersApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } diff --git a/src/server/api/slurm_stats.rs b/src/server/api/slurm_stats.rs index 47efd67cc..1f026f863 100644 --- a/src/server/api/slurm_stats.rs +++ b/src/server/api/slurm_stats.rs @@ -39,7 +39,7 @@ pub struct SlurmStatsApiImpl { } impl SlurmStatsApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { SlurmStatsApiImpl { context } } } diff --git a/src/server/api/sql_query_builder.rs b/src/server/api/sql_query_builder.rs index c0d7136da..3b389aaa9 100644 --- a/src/server/api/sql_query_builder.rs +++ b/src/server/api/sql_query_builder.rs @@ -3,7 +3,7 @@ use log::warn; /// Utility for building SQL queries with pagination and sorting -pub struct SqlQueryBuilder { +pub(crate) struct SqlQueryBuilder { base_query: String, where_clause: Option, order_by_clause: Option, @@ -12,7 +12,7 @@ pub struct SqlQueryBuilder { } impl SqlQueryBuilder { - pub fn new(base_query: String) -> Self { + pub(crate) fn new(base_query: String) -> Self { Self { base_query, where_clause: None, @@ -22,12 +22,12 @@ impl SqlQueryBuilder { } } - pub fn with_where(mut self, where_clause: String) -> Self { + pub(crate) fn with_where(mut self, where_clause: String) -> Self { self.where_clause = Some(where_clause); self } - pub fn with_pagination_and_sorting( + pub(crate) fn with_pagination_and_sorting( mut self, offset: i64, limit: i64, @@ -73,7 +73,7 @@ impl SqlQueryBuilder { self } - pub fn build(self) -> String { + pub(crate) fn build(self) -> String { let mut query = self.base_query; if let Some(where_clause) = self.where_clause { diff --git a/src/server/api/user_data.rs b/src/server/api/user_data.rs index d046d3755..eaf8bd865 100644 --- a/src/server/api/user_data.rs +++ b/src/server/api/user_data.rs @@ -90,13 +90,13 @@ pub trait UserDataApi { /// Implementation of user data API for the server #[derive(Clone)] pub struct UserDataApiImpl { - pub context: ApiContext, + context: ApiContext, } const USER_DATA_COLUMNS: &[&str] = &["id", "workflow_id", "name", "is_ephemeral", "data"]; impl UserDataApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } diff --git a/src/server/api/workflow_actions.rs b/src/server/api/workflow_actions.rs index cbefc5046..09d2096f0 100644 --- a/src/server/api/workflow_actions.rs +++ b/src/server/api/workflow_actions.rs @@ -175,11 +175,11 @@ pub trait WorkflowActionsApi { /// Implementation of workflow actions API for the server #[derive(Clone)] pub struct WorkflowActionsApiImpl { - pub context: ApiContext, + context: ApiContext, } impl WorkflowActionsApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } } @@ -783,7 +783,7 @@ where impl WorkflowActionsApiImpl { /// Check and trigger workflow actions based on trigger type and job state changes /// This is called by other API endpoints when state changes occur - pub async fn check_and_trigger_actions( + pub(crate) async fn check_and_trigger_actions( &self, workflow_id: i64, trigger_type: &str, @@ -1018,7 +1018,7 @@ impl WorkflowActionsApiImpl { /// slate: every action is re-armed, including `on_workflow_start`. Otherwise re-running /// `workflows init` on a workflow that already ran would leave its `on_workflow_start` actions /// suppressed forever. - pub async fn reset_actions_for_reinitialize( + pub(crate) async fn reset_actions_for_reinitialize( &self, workflow_id: i64, only_uninitialized: bool, diff --git a/src/server/api/workflows.rs b/src/server/api/workflows.rs index 69a018d0b..68e55a2a6 100644 --- a/src/server/api/workflows.rs +++ b/src/server/api/workflows.rs @@ -285,7 +285,7 @@ pub trait WorkflowsApi { /// Implementation of workflows API for the server #[derive(Clone)] pub struct WorkflowsApiImpl { - pub context: ApiContext, + context: ApiContext, } const WORKFLOW_COLUMNS: &[&str] = &[ @@ -342,7 +342,7 @@ const JOB_USER_DATA_RELATIONSHIP_COLUMNS: &[&str] = &[ ]; impl WorkflowsApiImpl { - pub fn new(context: ApiContext) -> Self { + pub(crate) fn new(context: ApiContext) -> Self { Self { context } } @@ -350,7 +350,7 @@ impl WorkflowsApiImpl { /// /// When `accessible_ids` is `Some(ids)`, only workflows with IDs in the list are returned. /// When `accessible_ids` is `None`, no ID-based filtering is applied. - pub async fn list_workflows_filtered( + pub(crate) async fn list_workflows_filtered( &self, offset: i64, sort_by: Option, diff --git a/src/server/api_constants.rs b/src/server/api_constants.rs index f5366e8f0..94f13b489 100644 --- a/src/server/api_constants.rs +++ b/src/server/api_constants.rs @@ -1,3 +1,3 @@ //! Server-owned API constants. -pub const API_VERSION: &str = crate::api_version::HTTP_API_VERSION; +pub(crate) const API_VERSION: &str = crate::api_version::HTTP_API_VERSION; diff --git a/src/server/api_contract.rs b/src/server/api_contract.rs index 676d07acf..1bd855e44 100644 --- a/src/server/api_contract.rs +++ b/src/server/api_contract.rs @@ -17,7 +17,7 @@ use std::task::{Context, Poll}; use tokio::sync::broadcast; /// Domain contract for artifact-centric APIs. -pub trait ArtifactDomainApi: +trait ArtifactDomainApi: FilesApi + ResultsApi + RoCrateApi + UserDataApi { } @@ -30,7 +30,7 @@ where } /// Domain contract for scheduler and execution-resource APIs. -pub trait SchedulingDomainApi: +trait SchedulingDomainApi: ComputeNodesApi + RemoteWorkersApi + ResourceRequirementsApi @@ -51,7 +51,7 @@ where } /// Domain contract for workflow and access-control APIs. -pub trait WorkflowDomainApi: +trait WorkflowDomainApi: AccessGroupsApi + WorkflowActionsApi + WorkflowsApi { } @@ -64,7 +64,7 @@ where } /// Domain contract for jobs and workflow execution state changes. -pub trait JobDomainApi: JobsApi {} +trait JobDomainApi: JobsApi {} impl JobDomainApi for T where @@ -74,7 +74,7 @@ where } /// Domain contract for event and failure-handler APIs. -pub trait EventDomainApi: EventsApi + FailureHandlersApi {} +trait EventDomainApi: EventsApi + FailureHandlersApi {} impl EventDomainApi for T where @@ -85,7 +85,7 @@ where /// Small shared surface for service-level behavior that is not tied to one resource family. #[async_trait] -pub trait SystemApi { +trait SystemApi { fn poll_ready( &self, _cx: &mut Context, @@ -1226,7 +1226,7 @@ pub trait TransportApiCore { } /// Transport contract for artifact-related HTTP endpoints. -pub trait ArtifactTransportApi: TransportApiCore {} +trait ArtifactTransportApi: TransportApiCore {} impl ArtifactTransportApi for T where C: Send + Sync, @@ -1235,7 +1235,7 @@ where } /// Transport contract for scheduler and compute-resource HTTP endpoints. -pub trait SchedulingTransportApi: TransportApiCore {} +trait SchedulingTransportApi: TransportApiCore {} impl SchedulingTransportApi for T where C: Send + Sync, @@ -1244,7 +1244,7 @@ where } /// Transport contract for workflow and workflow-action HTTP endpoints. -pub trait WorkflowTransportApi: TransportApiCore {} +trait WorkflowTransportApi: TransportApiCore {} impl WorkflowTransportApi for T where C: Send + Sync, @@ -1253,7 +1253,7 @@ where } /// Transport contract for job lifecycle and claiming HTTP endpoints. -pub trait JobTransportApi: TransportApiCore {} +trait JobTransportApi: TransportApiCore {} impl JobTransportApi for T where C: Send + Sync, @@ -1262,7 +1262,7 @@ where } /// Transport contract for event and failure-handler HTTP endpoints. -pub trait EventTransportApi: TransportApiCore {} +trait EventTransportApi: TransportApiCore {} impl EventTransportApi for T where C: Send + Sync, @@ -1271,7 +1271,7 @@ where } /// Transport contract for access-control and authorization HTTP endpoints. -pub trait AccessTransportApi: TransportApiCore {} +trait AccessTransportApi: TransportApiCore {} impl AccessTransportApi for T where C: Send + Sync, @@ -1280,7 +1280,7 @@ where } /// Public transport contract used by the HTTP layer. -pub trait TransportApi: +trait TransportApi: TransportApiCore + ArtifactTransportApi + SchedulingTransportApi @@ -1305,7 +1305,7 @@ where } /// Composed live server contract used by higher-level transport code. -pub trait Api: +trait Api: TransportApi + SystemApi + ArtifactDomainApi diff --git a/src/server/api_event_stream.rs b/src/server/api_event_stream.rs index 859dcdbdc..8c332c046 100644 --- a/src/server/api_event_stream.rs +++ b/src/server/api_event_stream.rs @@ -13,18 +13,18 @@ use tokio::sync::broadcast; /// Default cap on captured request/response body bytes per direction /// that are forwarded to subscribers. -pub const DEFAULT_BODY_CAPTURE_LIMIT: usize = 8 * 1024; +const DEFAULT_BODY_CAPTURE_LIMIT: usize = 8 * 1024; /// Environment variable that overrides [`DEFAULT_BODY_CAPTURE_LIMIT`]. -pub const BODY_CAPTURE_LIMIT_ENV: &str = "TORC_API_EVENT_BODY_MAX_BYTES"; +const BODY_CAPTURE_LIMIT_ENV: &str = "TORC_API_EVENT_BODY_MAX_BYTES"; /// Hard ceiling on bytes the middleware will buffer in memory in order /// to capture a body. Requests/responses whose advertised length /// exceeds this are passed through untouched (no body capture). -pub const BODY_CAPTURE_HARD_CAP_BYTES: usize = 1024 * 1024; +pub(crate) const BODY_CAPTURE_HARD_CAP_BYTES: usize = 1024 * 1024; /// Resolve the per-direction body display limit at runtime. -pub fn body_capture_limit() -> usize { +pub(crate) fn body_capture_limit() -> usize { std::env::var(BODY_CAPTURE_LIMIT_ENV) .ok() .and_then(|v| v.parse::().ok()) @@ -35,51 +35,51 @@ pub fn body_capture_limit() -> usize { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ApiRequestEvent { /// Unix epoch milliseconds at which the request finished. - pub timestamp_ms: i64, + pub(crate) timestamp_ms: i64, /// HTTP method (e.g. `GET`, `POST`). - pub method: String, + pub(crate) method: String, /// URL path component (without query string). - pub path: String, + pub(crate) path: String, /// URL query string, if any (without the leading `?`). #[serde(skip_serializing_if = "Option::is_none")] - pub query: Option, + pub(crate) query: Option, /// Final HTTP status code returned to the client. - pub status: u16, + pub(crate) status: u16, /// Wall-clock duration spent inside the router, in milliseconds. - pub latency_ms: u64, + pub(crate) latency_ms: u64, /// `x-span-id` assigned by `inject_request_context`, when available. #[serde(skip_serializing_if = "Option::is_none")] - pub request_id: Option, + pub(crate) request_id: Option, /// Authenticated subject extracted from the request, when available. #[serde(skip_serializing_if = "Option::is_none")] - pub user: Option, + pub(crate) user: Option, /// Captured request body when body capture was enabled and the /// payload was textual. #[serde(skip_serializing_if = "Option::is_none")] - pub request_body: Option, + pub(crate) request_body: Option, /// Captured response body when body capture was enabled and the /// payload was textual. #[serde(skip_serializing_if = "Option::is_none")] - pub response_body: Option, + pub(crate) response_body: Option, } /// Captured payload, possibly truncated. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CapturedBody { /// Total observed length in bytes (before truncation). - pub bytes: usize, + pub(crate) bytes: usize, /// Whether `text` was truncated to fit the capture limit. - pub truncated: bool, + pub(crate) truncated: bool, /// UTF-8 view of the (possibly truncated) body. `None` when the /// body was not valid UTF-8 — binary payloads are reported as /// metadata only. #[serde(skip_serializing_if = "Option::is_none")] - pub text: Option, + pub(crate) text: Option, } impl CapturedBody { /// Build a [`CapturedBody`] from raw bytes, truncating to `limit`. - pub fn from_bytes(bytes: &[u8], limit: usize) -> Self { + pub(crate) fn from_bytes(bytes: &[u8], limit: usize) -> Self { let total = bytes.len(); let truncated = total > limit; let slice = if truncated { &bytes[..limit] } else { bytes }; @@ -101,7 +101,7 @@ pub struct ApiEventBroadcaster { impl ApiEventBroadcaster { /// Create a broadcaster with the given channel capacity. - pub fn new(capacity: usize) -> Self { + fn new(capacity: usize) -> Self { let (sender, _) = broadcast::channel(capacity); Self { sender: Arc::new(sender), @@ -110,29 +110,29 @@ impl ApiEventBroadcaster { } /// Returns the number of currently connected receivers. - pub fn receiver_count(&self) -> usize { + pub(crate) fn receiver_count(&self) -> usize { self.sender.receiver_count() } /// Returns the number of receivers that asked for body capture. - pub fn body_subscriber_count(&self) -> usize { + pub(crate) fn body_subscriber_count(&self) -> usize { self.body_subscribers.load(Ordering::Relaxed) } /// Broadcast an event. Returns `true` if at least one receiver was /// notified. Drops silently when there are no subscribers. - pub fn broadcast(&self, event: ApiRequestEvent) -> bool { + pub(crate) fn broadcast(&self, event: ApiRequestEvent) -> bool { self.sender.send(event).is_ok() } /// Subscribe to the channel. - pub fn subscribe(&self) -> broadcast::Receiver { + pub(crate) fn subscribe(&self) -> broadcast::Receiver { self.sender.subscribe() } /// Register interest in body capture; the returned guard decrements /// the body-subscriber count when dropped. - pub fn body_subscriber_guard(&self) -> BodySubscriberGuard { + pub(crate) fn body_subscriber_guard(&self) -> BodySubscriberGuard { self.body_subscribers.fetch_add(1, Ordering::Relaxed); BodySubscriberGuard { counter: self.body_subscribers.clone(), diff --git a/src/server/api_stats.rs b/src/server/api_stats.rs index 51a77661c..690eb3f7e 100644 --- a/src/server/api_stats.rs +++ b/src/server/api_stats.rs @@ -27,33 +27,33 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::task::{Context, Poll}; /// Number of 1-second buckets retained. One hour of history. -pub const BUCKET_COUNT: usize = 3600; +const BUCKET_COUNT: usize = 3600; /// Default window the API endpoint reports when none is requested. -pub const DEFAULT_WINDOW_SECONDS: u64 = 3600; +pub(crate) const DEFAULT_WINDOW_SECONDS: u64 = 3600; /// Default aggregation interval for the API endpoint. -pub const DEFAULT_INTERVAL_SECONDS: u64 = 60; +pub(crate) const DEFAULT_INTERVAL_SECONDS: u64 = 60; /// One second's worth of accumulated request stats. #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct ApiStatsBucket { /// Bucket start, in Unix epoch milliseconds. - pub start_ms: i64, + start_ms: i64, /// Total requests handled during this bucket. - pub request_count: u64, + pub(crate) request_count: u64, /// Sum of data frame bytes received in inbound requests. - pub bytes_in: u64, + bytes_in: u64, /// Sum of data frame bytes written in outbound responses. - pub bytes_out: u64, + pub(crate) bytes_out: u64, /// Requests that returned a 2xx status. - pub status_2xx: u64, + status_2xx: u64, /// Requests that returned a 4xx status. - pub status_4xx: u64, + pub(crate) status_4xx: u64, /// Requests that returned a 5xx status. - pub status_5xx: u64, + status_5xx: u64, /// Requests that returned anything else (1xx, 3xx). - pub status_other: u64, + status_other: u64, } impl ApiStatsBucket { @@ -72,13 +72,13 @@ impl ApiStatsBucket { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ApiStatsSnapshot { /// Server-side current time in Unix epoch milliseconds. - pub now_ms: i64, + now_ms: i64, /// Width of each bucket in seconds. - pub interval_seconds: u64, + interval_seconds: u64, /// Total span covered by `buckets`, in seconds. - pub window_seconds: u64, + window_seconds: u64, /// Newest first: `buckets[0]` is the most recent interval. - pub buckets: Vec, + pub(crate) buckets: Vec, } /// Mutex-protected ring of per-second counters. @@ -111,7 +111,7 @@ impl Inner { } impl ApiStatsRing { - pub fn new() -> Self { + pub(crate) fn new() -> Self { let buckets = (0..BUCKET_COUNT) .map(|_| ApiStatsBucket::default()) .collect::>() @@ -127,7 +127,7 @@ impl ApiStatsRing { /// For streaming responses this is fired once when the response /// body starts (with `bytes_out = 0`); the streamed bytes are /// attributed second-by-second via [`Self::record_bytes_out`]. - pub fn record(&self, now_ms: i64, status: u16, bytes_in: u64, bytes_out: u64) { + pub(crate) fn record(&self, now_ms: i64, status: u16, bytes_in: u64, bytes_out: u64) { if now_ms <= 0 { return; } @@ -148,7 +148,7 @@ impl ApiStatsRing { /// touching the request count or status breakdown. Used to bucket /// each streamed response frame by the second it is actually sent, /// rather than deferring the whole stream to the disconnect second. - pub fn record_bytes_out(&self, now_ms: i64, bytes_out: u64) { + pub(crate) fn record_bytes_out(&self, now_ms: i64, bytes_out: u64) { if now_ms <= 0 || bytes_out == 0 { return; } @@ -158,7 +158,7 @@ impl ApiStatsRing { /// Aggregate the last `window_seconds` of recorded data into /// `interval_seconds`-wide buckets, newest first. - pub fn snapshot( + pub(crate) fn snapshot( &self, now_ms: i64, window_seconds: u64, @@ -234,7 +234,7 @@ impl CountingBody { /// `counter`. Use this for request bodies, where the consuming /// middleware reads the final count directly from `counter` after /// the handler returns. - pub fn new(inner: B, counter: Arc) -> Self { + pub(crate) fn new(inner: B, counter: Arc) -> Self { Self { inner, counter: Some(counter), @@ -249,7 +249,7 @@ impl CountingBody { /// the request as soon as it starts and attributes streamed bytes /// to the second they leave the server, rather than deferring the /// whole stream to completion. - pub fn with_recorder(inner: B, on_start: S, on_frame: F) -> Self + pub(crate) fn with_recorder(inner: B, on_start: S, on_frame: F) -> Self where S: FnOnce() + Send + 'static, F: Fn(u64) + Send + 'static, diff --git a/src/server/auth.rs b/src/server/auth.rs index ecc3211c0..0b7226071 100644 --- a/src/server/auth.rs +++ b/src/server/auth.rs @@ -19,12 +19,12 @@ pub type SharedCredentialCache = Arc>>; #[derive(Debug, Serialize, Deserialize)] pub struct Claims { - pub sub: String, - pub iss: String, - pub aud: String, - pub company: String, - pub exp: u64, - pub scopes: String, + sub: String, + iss: String, + aud: String, + company: String, + exp: u64, + scopes: String, } pub trait AuthenticationApi { @@ -49,7 +49,7 @@ impl HtpasswdAuthenticator { /// Create a new authenticator with optional htpasswd file /// If htpasswd is None and require_auth is false, all requests are allowed (backward compatible) /// If require_auth is true, authentication is required - pub fn new(htpasswd: Option, require_auth: bool) -> Self { + fn new(htpasswd: Option, require_auth: bool) -> Self { HtpasswdAuthenticator { htpasswd, require_auth, @@ -156,7 +156,7 @@ where RC: RcBound, RC::Result: Send + 'static, { - pub fn new( + fn new( inner: T, htpasswd: SharedHtpasswd, require_auth: bool, diff --git a/src/server/authorization.rs b/src/server/authorization.rs index 245836b23..ed900be1b 100644 --- a/src/server/authorization.rs +++ b/src/server/authorization.rs @@ -23,7 +23,7 @@ pub enum AccessCheckResult { } impl AccessCheckResult { - pub fn is_allowed(&self) -> bool { + pub(crate) fn is_allowed(&self) -> bool { matches!(self, AccessCheckResult::Allowed) } } @@ -40,12 +40,12 @@ pub struct AuthorizationService { impl AuthorizationService { /// If true, authorization checks are enforced /// If false, all access is allowed (for backward compatibility) - pub fn enforce_access_control(&self) -> bool { + pub(crate) fn enforce_access_control(&self) -> bool { self.enforce_access_control } /// Create a new authorization service - pub fn new(pool: Arc, enforce_access_control: bool) -> Self { + pub(crate) fn new(pool: Arc, enforce_access_control: bool) -> Self { Self { pool, enforce_access_control, @@ -54,7 +54,7 @@ impl AuthorizationService { /// Extract the username from the authorization context /// Returns None if no authorization is present or user is anonymous - pub fn get_username(auth: &Option) -> Option<&str> { + pub(crate) fn get_username(auth: &Option) -> Option<&str> { auth.as_ref().and_then(|a| { if a.subject == "anonymous" { None @@ -71,7 +71,7 @@ impl AuthorizationService { /// 2. The user is the owner of the workflow /// 3. The user is a system administrator /// 4. The user belongs to a group that has access to the workflow - pub async fn check_workflow_access( + pub(crate) async fn check_workflow_access( &self, auth: &Option, workflow_id: i64, @@ -181,7 +181,7 @@ impl AuthorizationService { } /// Check if a user can access a job (via workflow access) - pub async fn check_job_access( + pub(crate) async fn check_job_access( &self, auth: &Option, job_id: i64, @@ -233,7 +233,7 @@ impl AuthorizationService { ]; /// Check if a user can access a resource that has a workflow_id column - pub async fn check_resource_access( + pub(crate) async fn check_resource_access( &self, auth: &Option, resource_id: i64, @@ -282,7 +282,7 @@ impl AuthorizationService { /// Get all workflow IDs that a user can access /// This is useful for filtering list queries - pub async fn get_accessible_workflow_ids( + pub(crate) async fn get_accessible_workflow_ids( &self, auth: &Option, ) -> Result>, String> { @@ -343,7 +343,7 @@ impl AuthorizationService { /// Build a SQL WHERE clause fragment for filtering by accessible workflows /// Returns None if no filtering is needed, or Some(clause, bind_values) if filtering is needed - pub async fn build_workflow_access_filter( + async fn build_workflow_access_filter( &self, auth: &Option, workflow_id_column: &str, @@ -365,7 +365,7 @@ impl AuthorizationService { } /// Check if access control is enforced - pub fn is_enforced(&self) -> bool { + pub(crate) fn is_enforced(&self) -> bool { self.enforce_access_control } @@ -401,7 +401,10 @@ impl AuthorizationService { /// Check if a user is a system administrator /// /// A user is an admin if they are a member of the "admin" group (is_system = 1) - pub async fn check_admin_access(&self, auth: &Option) -> AccessCheckResult { + pub(crate) async fn check_admin_access( + &self, + auth: &Option, + ) -> AccessCheckResult { if !self.enforce_access_control { return AccessCheckResult::Allowed; } @@ -438,7 +441,7 @@ impl AuthorizationService { /// 2. They are an admin of that specific group (role = 'admin') /// /// Note: The system 'admin' group can only be managed via config - pub async fn check_group_admin_access( + pub(crate) async fn check_group_admin_access( &self, auth: &Option, group_id: i64, @@ -530,7 +533,7 @@ impl AuthorizationService { /// A user can add a workflow to a group if: /// 1. They are the owner of the workflow, OR /// 2. They are an admin of the group (or system admin) - pub async fn check_workflow_group_access( + pub(crate) async fn check_workflow_group_access( &self, auth: &Option, workflow_id: i64, @@ -584,7 +587,7 @@ impl AuthorizationService { } /// Check if a group is a system group (cannot be deleted) - pub async fn is_system_group(&self, group_id: i64) -> Result { + pub(crate) async fn is_system_group(&self, group_id: i64) -> Result { match sqlx::query("SELECT is_system FROM access_group WHERE id = $1") .bind(group_id) .fetch_optional(self.pool.as_ref()) diff --git a/src/server/context.rs b/src/server/context.rs index e5042db0c..abe46c4b1 100644 --- a/src/server/context.rs +++ b/src/server/context.rs @@ -19,7 +19,7 @@ where B: Push, Result = C>, C: Push, Result = D>, { - pub fn new(inner: T) -> MakeAddContext { + fn new(inner: T) -> MakeAddContext { MakeAddContext { inner, marker: PhantomData, @@ -71,7 +71,7 @@ where B: Push, Result = C>, C: Push, Result = D>, { - pub fn new(inner: T) -> Self { + fn new(inner: T) -> Self { AddContext { inner, marker: PhantomData, diff --git a/src/server/credential_cache.rs b/src/server/credential_cache.rs index b1fb9f02e..d3c58caa8 100644 --- a/src/server/credential_cache.rs +++ b/src/server/credential_cache.rs @@ -39,7 +39,7 @@ impl CredentialCache { /// /// # Arguments /// * `ttl` - How long successful authentications should be cached - pub fn new(ttl: Duration) -> Self { + pub(crate) fn new(ttl: Duration) -> Self { Self { cache: Arc::new(RwLock::new(HashMap::new())), ttl, @@ -59,7 +59,7 @@ impl CredentialCache { /// Check if credentials are cached and still valid. /// /// Returns `true` if the credentials are in the cache and haven't expired. - pub fn is_cached(&self, username: &str, password: &str) -> bool { + pub(crate) fn is_cached(&self, username: &str, password: &str) -> bool { let key = Self::cache_key(username, password); let cache = self.cache.read(); @@ -74,7 +74,7 @@ impl CredentialCache { /// Cache a successful authentication. /// /// Only call this after bcrypt verification succeeds. - pub fn cache_success(&self, username: &str, password: &str) { + pub(crate) fn cache_success(&self, username: &str, password: &str) { let key = Self::cache_key(username, password); let entry = CacheEntry { expires_at: Instant::now() + self.ttl, @@ -98,21 +98,9 @@ impl CredentialCache { /// Clear all cached entries. /// /// Used when the htpasswd file is reloaded to invalidate stale credentials. - pub fn clear(&self) { + pub(crate) fn clear(&self) { self.cache.write().clear(); } - - /// Get the number of entries in the cache (for debugging/monitoring). - #[allow(dead_code)] - pub fn len(&self) -> usize { - self.cache.read().len() - } - - /// Check if the cache is empty. - #[allow(dead_code)] - pub fn is_empty(&self) -> bool { - self.cache.read().is_empty() - } } impl std::fmt::Debug for CredentialCache { @@ -127,7 +115,6 @@ impl std::fmt::Debug for CredentialCache { #[cfg(test)] mod tests { use super::*; - use std::thread::sleep; #[test] fn test_cache_hit() { @@ -151,15 +138,13 @@ mod tests { #[test] fn test_cache_expiry() { - let cache = CredentialCache::new(Duration::from_millis(50)); + let cache = CredentialCache::new(Duration::from_secs(60)); + let key = CredentialCache::cache_key("user", "password"); cache.cache_success("user", "password"); - assert!(cache.is_cached("user", "password")); - - // Wait for expiry - sleep(Duration::from_millis(100)); + cache.cache.write().get_mut(&key).unwrap().expires_at = + Instant::now() - Duration::from_secs(1); - // Should no longer be cached assert!(!cache.is_cached("user", "password")); } diff --git a/src/server/dashboard.rs b/src/server/dashboard.rs index c604c80d5..f43b0ee24 100644 --- a/src/server/dashboard.rs +++ b/src/server/dashboard.rs @@ -21,7 +21,7 @@ const INFO_PAGE: &str = r#" /// /// Returns `Some(Response)` for root/dashboard paths, /// or `None` if the request should be handled by the API. -pub fn serve_dashboard(path: &str) -> Option> { +pub(crate) fn serve_dashboard(path: &str) -> Option> { let path = path.trim_start_matches('/'); if path.is_empty() || path == "dashboard" || path == "dashboard/" { diff --git a/src/server/event_broadcast.rs b/src/server/event_broadcast.rs index 06d081175..66e4cddd7 100644 --- a/src/server/event_broadcast.rs +++ b/src/server/event_broadcast.rs @@ -12,15 +12,15 @@ use tokio::sync::broadcast; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BroadcastEvent { /// The workflow ID this event belongs to. - pub workflow_id: i64, + pub(crate) workflow_id: i64, /// Timestamp in milliseconds since Unix epoch. - pub timestamp: i64, + pub(crate) timestamp: i64, /// The type of event (e.g., "job_started", "job_completed", "job_failed"). - pub event_type: String, + pub(crate) event_type: String, /// The severity level of the event. - pub severity: EventSeverity, + pub(crate) severity: EventSeverity, /// Event-specific data as JSON. - pub data: serde_json::Value, + pub(crate) data: serde_json::Value, } /// Event broadcaster that manages a broadcast channel for SSE events. @@ -34,7 +34,7 @@ impl EventBroadcaster { /// /// The capacity determines how many events can be buffered before slow /// receivers start missing events (lagging). - pub fn new(capacity: usize) -> Self { + pub(crate) fn new(capacity: usize) -> Self { let (sender, _) = broadcast::channel(capacity); Self { sender: Arc::new(sender), @@ -45,7 +45,7 @@ impl EventBroadcaster { /// /// If there are no subscribers, the event is silently dropped. /// This is intentional - events are ephemeral and not persisted. - pub fn broadcast(&self, event: BroadcastEvent) { + pub(crate) fn broadcast(&self, event: BroadcastEvent) { // Ignore the result - if there are no receivers, the event is dropped let _ = self.sender.send(event); } @@ -54,7 +54,7 @@ impl EventBroadcaster { /// /// Returns a receiver that will receive all future events broadcast /// after this subscription is created. - pub fn subscribe(&self) -> broadcast::Receiver { + pub(crate) fn subscribe(&self) -> broadcast::Receiver { self.sender.subscribe() } } diff --git a/src/server/htpasswd.rs b/src/server/htpasswd.rs index c9d7fc2b4..59ccf42a0 100644 --- a/src/server/htpasswd.rs +++ b/src/server/htpasswd.rs @@ -68,14 +68,14 @@ impl HtpasswdFile { } /// Create an empty htpasswd file (for testing or programmatic creation) - pub fn new() -> Self { + fn new() -> Self { HtpasswdFile { users: HashMap::new(), } } /// Add a user with an already-hashed password - pub fn add_user(&mut self, username: String, bcrypt_hash: String) { + fn add_user(&mut self, username: String, bcrypt_hash: String) { self.users.insert(username, bcrypt_hash); } @@ -103,7 +103,7 @@ impl HtpasswdFile { } /// Check if a username exists - pub fn has_user(&self, username: &str) -> bool { + fn has_user(&self, username: &str) -> bool { self.users.contains_key(username) } } diff --git a/src/server/http_server.rs b/src/server/http_server.rs index edd877f65..99067691f 100644 --- a/src/server/http_server.rs +++ b/src/server/http_server.rs @@ -39,7 +39,7 @@ enum CreateTaskError { } /// Result of `create_or_get_initialize_jobs_task`. -pub(super) enum TaskCreation { +enum TaskCreation { /// A new task was inserted; the caller must spawn the background work. Created(models::TaskModel), /// An identical task is already active; returned idempotently. No work spawned. @@ -383,7 +383,7 @@ impl Deref for Server { } impl Server { - pub fn new( + pub(crate) fn new( pool: SqlitePool, enforce_access_control: bool, htpasswd: crate::server::auth::SharedHtpasswd, @@ -414,11 +414,11 @@ impl Server { } /// Get a reference to the event broadcaster for SSE subscriptions. - pub fn get_event_broadcaster(&self) -> &EventBroadcaster { + fn get_event_broadcaster(&self) -> &EventBroadcaster { &self.event_broadcaster } - pub fn shared_state(&self) -> Arc { + fn shared_state(&self) -> Arc { self.shared.clone() } @@ -474,7 +474,7 @@ impl Server { } /// Load the single active async task for a workflow, if any. - pub(super) async fn get_active_task( + async fn get_active_task( &self, workflow_id: i64, ) -> Result, ApiError> { @@ -1039,7 +1039,7 @@ impl Server { } #[cfg(feature = "openapi-codegen")] - pub fn openapi_app_state(&self) -> crate::openapi_spec::OpenApiAppState { + pub(crate) fn openapi_app_state(&self) -> crate::openapi_spec::OpenApiAppState { self.shared.openapi_app_state( full_version(), API_VERSION.to_string(), diff --git a/src/server/http_server/bootstrap.rs b/src/server/http_server/bootstrap.rs index 8afe2f017..881c5f7ca 100644 --- a/src/server/http_server/bootstrap.rs +++ b/src/server/http_server/bootstrap.rs @@ -12,10 +12,7 @@ use tokio::net::TcpListener; #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod}; -pub(super) async fn sync_admin_group( - pool: &SqlitePool, - admin_users: &[String], -) -> Result<(), sqlx::Error> { +async fn sync_admin_group(pool: &SqlitePool, admin_users: &[String]) -> Result<(), sqlx::Error> { sqlx::query( r#" INSERT INTO access_group (name, description, is_system) diff --git a/src/server/http_server/lifecycle_support.rs b/src/server/http_server/lifecycle_support.rs index ee0cbe47a..2fc8665f5 100644 --- a/src/server/http_server/lifecycle_support.rs +++ b/src/server/http_server/lifecycle_support.rs @@ -384,10 +384,7 @@ impl Server { Ok(()) } - pub(super) async fn update_jobs_from_completion_reversal( - &self, - job_id: i64, - ) -> Result<(), ApiError> { + async fn update_jobs_from_completion_reversal(&self, job_id: i64) -> Result<(), ApiError> { debug!( "update_jobs_from_completion_reversal: resetting downstream jobs for job_id={}", job_id diff --git a/src/server/http_transport/path_parsing.rs b/src/server/http_transport/path_parsing.rs index b127e561b..23a4fcb32 100644 --- a/src/server/http_transport/path_parsing.rs +++ b/src/server/http_transport/path_parsing.rs @@ -1,5 +1,5 @@ #[cfg(test)] -pub(super) fn decode_path_segment(segment: &str) -> Option { +fn decode_path_segment(segment: &str) -> Option { percent_encoding::percent_decode_str(segment) .decode_utf8() .ok() @@ -75,7 +75,7 @@ pub(super) fn parse_workflow_failure_handlers_path(path: &str) -> Option { } #[cfg(test)] -pub(super) fn parse_workflow_suffix_path(path: &str, suffix: &str) -> Option { +fn parse_workflow_suffix_path(path: &str, suffix: &str) -> Option { strip_prefix_and_suffix(path, "/torc-service/v1/workflows/", suffix)? .parse::() .ok() diff --git a/src/server/live_router.rs b/src/server/live_router.rs index ec7618791..49da9e389 100644 --- a/src/server/live_router.rs +++ b/src/server/live_router.rs @@ -35,16 +35,16 @@ use utoipa::IntoParams; #[derive(Clone)] pub struct LiveRouterState { - pub openapi_state: OpenApiAppState, - pub server: Server, - pub auth: LiveAuthState, + pub(crate) openapi_state: OpenApiAppState, + pub(crate) server: Server, + pub(crate) auth: LiveAuthState, } #[derive(Clone)] pub struct LiveAuthState { - pub htpasswd: SharedHtpasswd, - pub require_auth: bool, - pub credential_cache: SharedCredentialCache, + pub(crate) htpasswd: SharedHtpasswd, + pub(crate) require_auth: bool, + pub(crate) credential_cache: SharedCredentialCache, } macro_rules! path_handler { @@ -77,7 +77,7 @@ fn max_bulk_request_body_bytes() -> usize { }) } -pub fn app_router(state: LiveRouterState) -> Router { +pub(crate) fn app_router(state: LiveRouterState) -> Router { Router::new() .merge( Router::new() @@ -433,16 +433,16 @@ pub fn app_router(state: LiveRouterState) -> Router { #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct AccessPaginationQuery { #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] #[into_params(parameter_in = Query)] pub struct PendingActionsQuery { #[param(nullable = true)] - pub trigger_type: Option>, + trigger_type: Option>, } fn parse_pending_actions_query(query: Option<&str>) -> PendingActionsQuery { @@ -459,83 +459,83 @@ fn parse_pending_actions_query(query: Option<&str>) -> PendingActionsQuery { #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct WorkflowsListQuery { #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, #[param(nullable = true)] - pub name: Option, + name: Option, #[param(nullable = true)] - pub user: Option, + user: Option, #[param(nullable = true)] - pub description: Option, + description: Option, #[param(nullable = true)] - pub is_archived: Option, + is_archived: Option, /// Filter to workflows shared with this access group (by group name). #[param(nullable = true)] - pub access_group: Option, + access_group: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct InitializeJobsQuery { #[param(nullable = true)] - pub only_uninitialized: Option, + only_uninitialized: Option, #[param(nullable = true)] - pub clear_ephemeral_user_data: Option, + clear_ephemeral_user_data: Option, #[serde(rename = "async")] #[param(nullable = true)] - pub async_: Option, + async_: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ResetWorkflowStatusQuery { #[param(nullable = true)] - pub force: Option, + force: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ResetJobStatusQuery { #[param(nullable = true)] - pub failed_only: Option, + failed_only: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ClaimJobsBasedOnResourcesQuery { #[param(nullable = true)] - pub strict_scheduler_match: Option, + strict_scheduler_match: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ClaimNextJobsQuery { #[param(nullable = true)] - pub limit: Option, + limit: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct WorkflowRelationshipsQuery { #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ProcessChangedJobInputsQuery { #[param(nullable = true)] - pub dry_run: Option, + dry_run: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ReadyJobRequirementsQuery { #[param(nullable = true)] - pub scheduler_config_id: Option, + scheduler_config_id: Option, } #[utoipa::path( @@ -614,12 +614,12 @@ pub struct AdminAuditLogQuery { /// Offset for pagination (0-based). Defaults to 0. #[serde(default)] #[param(nullable = true)] - pub offset: Option, + offset: Option, /// Maximum number of entries to return. Defaults to and is capped at 100,000 /// (the server-wide list cap); values above the cap are clamped. #[serde(default)] #[param(nullable = true)] - pub limit: Option, + limit: Option, } #[utoipa::path( @@ -659,7 +659,7 @@ pub struct ApiEventStreamQuery { /// payloads aren't streamed unless requested. #[serde(default)] #[param(nullable = true)] - pub include_bodies: Option, + include_bodies: Option, } #[utoipa::path( @@ -672,7 +672,7 @@ pub struct ApiEventStreamQuery { (status = 200, description = "Server-Sent Events stream of inbound API requests") ) )] -pub async fn admin_api_events_stream( +async fn admin_api_events_stream( State(state): State, Extension(context): Extension, Query(params): Query, @@ -730,11 +730,11 @@ pub struct ApiStatsQuery { /// (1 hour). Capped at the ring buffer's retention (3600s). #[serde(default)] #[param(nullable = true)] - pub window_seconds: Option, + window_seconds: Option, /// Aggregation bucket width in seconds. Defaults to 60. #[serde(default)] #[param(nullable = true)] - pub interval_seconds: Option, + interval_seconds: Option, } #[utoipa::path( @@ -747,7 +747,7 @@ pub struct ApiStatsQuery { (status = 200, description = "Aggregated request counts and bytes per bucket") ) )] -pub async fn admin_api_stats( +async fn admin_api_stats( State(state): State, Extension(context): Extension, Query(params): Query, @@ -1155,26 +1155,26 @@ pub async fn check_workflow_access( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ComputeNodesQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, #[param(nullable = true)] - pub hostname: Option, + hostname: Option, #[param(nullable = true)] - pub is_active: Option, + is_active: Option, #[param(nullable = true)] - pub scheduled_compute_node_id: Option, + scheduled_compute_node_id: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct DeleteComputeNodesQuery { - pub workflow_id: i64, + workflow_id: i64, } #[utoipa::path( @@ -1343,19 +1343,19 @@ pub async fn delete_compute_node( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct EventsQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, #[param(nullable = true)] - pub category: Option, + category: Option, #[param(nullable = true)] - pub after_timestamp: Option, + after_timestamp: Option, } #[utoipa::path( @@ -1523,23 +1523,23 @@ pub async fn delete_event( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct FilesQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub produced_by_job_id: Option, + produced_by_job_id: Option, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, #[param(nullable = true)] - pub name: Option, + name: Option, #[param(nullable = true)] - pub path: Option, + path: Option, #[param(nullable = true)] - pub is_output: Option, + is_output: Option, } #[utoipa::path( @@ -1705,19 +1705,19 @@ pub async fn delete_file( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct LocalSchedulersQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, #[param(nullable = true)] - pub memory: Option, + memory: Option, #[param(nullable = true)] - pub num_cpus: Option, + num_cpus: Option, } #[utoipa::path( @@ -1889,52 +1889,52 @@ pub async fn delete_local_scheduler( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ResourceRequirementsQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub job_id: Option, + job_id: Option, #[param(nullable = true)] - pub name: Option, + name: Option, #[param(nullable = true)] - pub memory: Option, + memory: Option, #[param(nullable = true)] - pub num_cpus: Option, + num_cpus: Option, #[param(nullable = true)] - pub num_gpus: Option, + num_gpus: Option, #[param(nullable = true)] - pub num_nodes: Option, + num_nodes: Option, #[param(nullable = true)] - pub runtime: Option, + runtime: Option, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct FailureHandlersListQuery { #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct SlurmStatsQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub job_id: Option, + job_id: Option, #[param(nullable = true)] - pub run_id: Option, + run_id: Option, #[param(nullable = true)] - pub attempt_id: Option, + attempt_id: Option, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, } #[utoipa::path( @@ -2282,25 +2282,25 @@ pub async fn list_slurm_stats( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct JobsListQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub status: Option, + status: Option, #[param(nullable = true)] - pub needs_file_id: Option, + needs_file_id: Option, #[param(nullable = true)] - pub upstream_job_id: Option, + upstream_job_id: Option, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, #[param(nullable = true)] - pub include_relationships: Option, + include_relationships: Option, #[param(nullable = true)] - pub active_compute_node_id: Option, + active_compute_node_id: Option, /// When set, filters by job provenance: `true` returns only jobs with /// `origin IS NOT NULL` (failure-handler retries and `spawn_jobs` /// children); `false` returns only originally-declared jobs. @@ -2308,25 +2308,25 @@ pub struct JobsListQuery { /// Slurm allocations with `limit=1` (the response's `total_count` /// suffices — no rows downloaded). #[param(nullable = true)] - pub origin_is_set: Option, + origin_is_set: Option, /// Substring filter on the job name (SQL `LIKE %value%`, case-insensitive /// for ASCII). #[param(nullable = true)] - pub name: Option, + name: Option, /// Substring filter on the job command (SQL `LIKE %value%`, case-insensitive /// for ASCII). #[param(nullable = true)] - pub command: Option, + command: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct DeleteJobsQuery { - pub workflow_id: i64, + workflow_id: i64, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct RetryJobQuery { - pub max_retries: i32, + max_retries: i32, } #[utoipa::path( @@ -2683,30 +2683,30 @@ pub async fn retry_job( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct CreateUserDataQuery { #[param(nullable = true)] - pub consumer_job_id: Option, + consumer_job_id: Option, #[param(nullable = true)] - pub producer_job_id: Option, + producer_job_id: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct UserDataQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub consumer_job_id: Option, + consumer_job_id: Option, #[param(nullable = true)] - pub producer_job_id: Option, + producer_job_id: Option, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, #[param(nullable = true)] - pub name: Option, + name: Option, #[param(nullable = true)] - pub is_ephemeral: Option, + is_ephemeral: Option, } #[utoipa::path( @@ -2882,27 +2882,27 @@ pub async fn delete_user_data( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ResultsQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub job_id: Option, + job_id: Option, #[param(nullable = true)] - pub run_id: Option, + run_id: Option, #[param(nullable = true)] - pub return_code: Option, + return_code: Option, #[param(nullable = true)] - pub status: Option, + status: Option, #[param(nullable = true)] - pub compute_node_id: Option, + compute_node_id: Option, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, #[param(nullable = true)] - pub all_runs: Option, + all_runs: Option, } #[utoipa::path( @@ -3074,34 +3074,34 @@ pub async fn delete_result( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct ScheduledComputeNodesQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, #[param(nullable = true)] - pub scheduler_id: Option, + scheduler_id: Option, #[param(nullable = true)] - pub scheduler_config_id: Option, + scheduler_config_id: Option, #[param(nullable = true)] - pub status: Option, + status: Option, } #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct SlurmSchedulersQuery { - pub workflow_id: i64, + workflow_id: i64, #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, } #[utoipa::path( @@ -3916,9 +3916,9 @@ pub async fn get_workflow_status( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct SlurmJobCorrelationsQuery { #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, } #[utoipa::path( @@ -3953,9 +3953,9 @@ pub async fn get_slurm_job_correlations( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct RunningJobsQuery { #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, } #[utoipa::path( @@ -4313,17 +4313,17 @@ pub async fn list_required_existing_files( #[derive(Debug, Clone, Deserialize, IntoParams)] pub struct RoCrateEntitiesQuery { #[param(nullable = true)] - pub offset: Option, + offset: Option, #[param(nullable = true)] - pub limit: Option, + limit: Option, #[param(nullable = true)] - pub file_id: Option, + file_id: Option, #[param(nullable = true)] - pub entity_id: Option, + entity_id: Option, #[param(nullable = true)] - pub sort_by: Option, + sort_by: Option, #[param(nullable = true)] - pub reverse_sort: Option, + reverse_sort: Option, } #[utoipa::path( diff --git a/src/server/live_state.rs b/src/server/live_state.rs index f4683f59a..8352245af 100644 --- a/src/server/live_state.rs +++ b/src/server/live_state.rs @@ -41,36 +41,36 @@ impl Default for AdminSqlConfig { #[derive(Clone)] pub struct LiveServerState { - pub pool: Arc, - pub last_completion_time: Arc, - pub workflows_with_failures: Arc>>, - pub authorization_service: AuthorizationService, - pub event_broadcaster: EventBroadcaster, - pub api_event_broadcaster: ApiEventBroadcaster, - pub api_stats: ApiStatsRing, - pub htpasswd: SharedHtpasswd, - pub auth_file_path: Option, - pub credential_cache: SharedCredentialCache, - pub admin_sql: AdminSqlConfig, - pub access_groups_api: AccessGroupsApiImpl, - pub compute_nodes_api: ComputeNodesApiImpl, - pub events_api: EventsApiImpl, - pub failure_handlers_api: FailureHandlersApiImpl, - pub files_api: FilesApiImpl, - pub jobs_api: JobsApiImpl, - pub remote_workers_api: RemoteWorkersApiImpl, - pub resource_requirements_api: ResourceRequirementsApiImpl, - pub results_api: ResultsApiImpl, - pub ro_crate_api: RoCrateApiImpl, - pub schedulers_api: SchedulersApiImpl, - pub slurm_stats_api: SlurmStatsApiImpl, - pub user_data_api: UserDataApiImpl, - pub workflow_actions_api: WorkflowActionsApiImpl, - pub workflows_api: WorkflowsApiImpl, + pub(crate) pool: Arc, + pub(crate) last_completion_time: Arc, + pub(crate) workflows_with_failures: Arc>>, + pub(crate) authorization_service: AuthorizationService, + pub(crate) event_broadcaster: EventBroadcaster, + pub(crate) api_event_broadcaster: ApiEventBroadcaster, + pub(crate) api_stats: ApiStatsRing, + pub(crate) htpasswd: SharedHtpasswd, + pub(crate) auth_file_path: Option, + pub(crate) credential_cache: SharedCredentialCache, + pub(crate) admin_sql: AdminSqlConfig, + pub(crate) access_groups_api: AccessGroupsApiImpl, + pub(crate) compute_nodes_api: ComputeNodesApiImpl, + pub(crate) events_api: EventsApiImpl, + pub(crate) failure_handlers_api: FailureHandlersApiImpl, + pub(crate) files_api: FilesApiImpl, + pub(crate) jobs_api: JobsApiImpl, + pub(crate) remote_workers_api: RemoteWorkersApiImpl, + pub(crate) resource_requirements_api: ResourceRequirementsApiImpl, + pub(crate) results_api: ResultsApiImpl, + pub(crate) ro_crate_api: RoCrateApiImpl, + pub(crate) schedulers_api: SchedulersApiImpl, + pub(crate) slurm_stats_api: SlurmStatsApiImpl, + pub(crate) user_data_api: UserDataApiImpl, + pub(crate) workflow_actions_api: WorkflowActionsApiImpl, + pub(crate) workflows_api: WorkflowsApiImpl, } impl LiveServerState { - pub fn new( + pub(crate) fn new( pool: SqlitePool, enforce_access_control: bool, htpasswd: SharedHtpasswd, @@ -114,7 +114,7 @@ impl LiveServerState { } #[cfg(feature = "openapi-codegen")] - pub fn openapi_app_state( + pub(crate) fn openapi_app_state( &self, version: String, api_version: String, diff --git a/src/server/response_types.rs b/src/server/response_types.rs index 100da4c53..41eccf720 100644 --- a/src/server/response_types.rs +++ b/src/server/response_types.rs @@ -4,8 +4,8 @@ //! domain-grouped modules so the rest of the server no longer depends directly on one large //! response barrel. -pub mod access { - pub use crate::server::api_responses::{ +pub(crate) mod access { + pub(crate) use crate::server::api_responses::{ AddUserToGroupResponse, AddWorkflowToGroupResponse, CheckWorkflowAccessResponse, CreateAccessGroupResponse, DeleteAccessGroupResponse, GetAccessGroupResponse, ListAccessGroupsApiResponse, ListGroupMembersResponse, ListUserGroupsApiResponse, @@ -13,8 +13,8 @@ pub mod access { }; } -pub mod artifacts { - pub use crate::server::api_responses::{ +pub(crate) mod artifacts { + pub(crate) use crate::server::api_responses::{ CreateFileResponse, CreateFilesResponse, CreateResultResponse, CreateRoCrateEntityResponse, CreateUserDataListResponse, CreateUserDataResponse, DeleteAllUserDataResponse, DeleteFileResponse, DeleteFilesResponse, DeleteResultResponse, DeleteResultsResponse, @@ -26,8 +26,8 @@ pub mod artifacts { }; } -pub mod events { - pub use crate::server::api_responses::{ +pub(crate) mod events { + pub(crate) use crate::server::api_responses::{ CreateEventResponse, CreateFailureHandlerResponse, DeleteEventResponse, DeleteEventsResponse, DeleteFailureHandlerResponse, GetEventResponse, GetFailureHandlerResponse, ListEventsResponse, ListFailureHandlersResponse, @@ -35,8 +35,8 @@ pub mod events { }; } -pub mod jobs { - pub use crate::server::api_responses::{ +pub(crate) mod jobs { + pub(crate) use crate::server::api_responses::{ BatchCompleteJobsResponse, ClaimJobsBasedOnResources, ClaimNextJobsResponse, CompleteJobResponse, CreateJobResponse, CreateJobsResponse, DeleteJobResponse, DeleteJobsResponse, GetJobResponse, GetReadyJobRequirementsResponse, @@ -47,8 +47,8 @@ pub mod jobs { }; } -pub mod scheduling { - pub use crate::server::api_responses::{ +pub(crate) mod scheduling { + pub(crate) use crate::server::api_responses::{ CreateComputeNodeResponse, CreateLocalSchedulerResponse, CreateRemoteWorkersResponse, CreateResourceRequirementsResponse, CreateScheduledComputeNodeResponse, CreateSlurmSchedulerResponse, CreateSlurmStatsResponse, @@ -67,15 +67,15 @@ pub mod scheduling { }; } -pub mod system { - pub use crate::server::api_responses::{ +pub(crate) mod system { + pub(crate) use crate::server::api_responses::{ AdminSqlResponse, GetTaskResponse, GetVersionResponse, ListAdminAuditLogResponse, PingResponse, ReloadAuthResponse, }; } -pub mod workflows { - pub use crate::server::api_responses::{ +pub(crate) mod workflows { + pub(crate) use crate::server::api_responses::{ ArchiveWorkflowResponse, CancelWorkflowResponse, ClaimActionResponse, CreateWorkflowActionResponse, CreateWorkflowResponse, DeleteWorkflowActionResponse, DeleteWorkflowResponse, GetActiveTaskResponse, GetPendingActionsResponse, diff --git a/src/server/service.rs b/src/server/service.rs index 838473015..3c1fbfdb7 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -46,10 +46,10 @@ pub struct ServiceConfig { impl ServiceConfig { /// Default completion check interval for services (5 seconds) - pub const DEFAULT_SERVICE_INTERVAL_SECS: f64 = 5.0; + const DEFAULT_SERVICE_INTERVAL_SECS: f64 = 5.0; /// Default credential cache TTL in seconds (must match clap default in ServerConfig) - pub const DEFAULT_CREDENTIAL_CACHE_TTL_SECS: u64 = 60; + const DEFAULT_CREDENTIAL_CACHE_TTL_SECS: u64 = 60; /// Create default configuration for system-level service /// Uses a shorter completion check interval (5s) since local services @@ -207,7 +207,7 @@ fn service_label() -> ServiceLabel { } /// Install the service with the given configuration -pub fn install_service(config: &ServiceConfig, user_level: bool) -> Result<()> { +fn install_service(config: &ServiceConfig, user_level: bool) -> Result<()> { // Validate HTTPS configuration upfront to avoid installing a service that fails to start if config.https { if config.tls_cert.is_none() { @@ -359,7 +359,7 @@ pub fn install_service(config: &ServiceConfig, user_level: bool) -> Result<()> { } /// Uninstall the service -pub fn uninstall_service(user_level: bool) -> Result<()> { +fn uninstall_service(user_level: bool) -> Result<()> { let manager = get_service_manager(user_level)?; manager @@ -377,7 +377,7 @@ pub fn uninstall_service(user_level: bool) -> Result<()> { } /// Start the service -pub fn start_service(user_level: bool) -> Result<()> { +fn start_service(user_level: bool) -> Result<()> { let manager = get_service_manager(user_level)?; let label = service_label(); @@ -394,7 +394,7 @@ pub fn start_service(user_level: bool) -> Result<()> { } /// Stop the service -pub fn stop_service(user_level: bool) -> Result<()> { +fn stop_service(user_level: bool) -> Result<()> { let manager = get_service_manager(user_level)?; let label = service_label(); @@ -411,7 +411,7 @@ pub fn stop_service(user_level: bool) -> Result<()> { } /// Check service status -pub fn service_status(user_level: bool) -> Result<()> { +fn service_status(user_level: bool) -> Result<()> { let service_type = if user_level { "user" } else { "system" }; println!( "Service status check varies by platform ({} service):", diff --git a/src/server/transport_types/auth_types.rs b/src/server/transport_types/auth_types.rs index e57330692..c13e95f5f 100644 --- a/src/server/transport_types/auth_types.rs +++ b/src/server/transport_types/auth_types.rs @@ -14,9 +14,9 @@ pub enum Scopes { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Authorization { - pub subject: String, - pub scopes: Scopes, - pub issuer: Option, + pub(crate) subject: String, + pub(crate) scopes: Scopes, + pub(crate) issuer: Option, } #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -24,12 +24,12 @@ pub struct AuthData; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Basic { - pub username: String, - pub password: Option, + pub(crate) username: String, + pub(crate) password: Option, } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Bearer(pub String); +pub struct Bearer(pub(crate) String); pub trait RcBound { type Result; @@ -47,7 +47,7 @@ where } } -pub fn from_headers(headers: &HeaderMap) -> Option { +pub(crate) fn from_headers(headers: &HeaderMap) -> Option { let header = headers.get(header::AUTHORIZATION)?; let header = header.to_str().ok()?; let encoded = header.strip_prefix("Basic ")?; @@ -69,7 +69,7 @@ pub struct AllowAllAuthenticator { } impl AllowAllAuthenticator { - pub fn new() -> Self { + fn new() -> Self { Self { _inner: PhantomData, _context: PhantomData, diff --git a/src/server/transport_types/context_types.rs b/src/server/transport_types/context_types.rs index beb9e1e5c..d37c72462 100644 --- a/src/server/transport_types/context_types.rs +++ b/src/server/transport_types/context_types.rs @@ -7,7 +7,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use super::auth_types; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct ApiError(pub String); +pub struct ApiError(pub(crate) String); impl Display for ApiError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -105,10 +105,10 @@ impl Push> for EmptyContext { } #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct XSpanIdString(pub String); +pub struct XSpanIdString(pub(crate) String); impl XSpanIdString { - pub fn get_or_generate(request: &Request) -> Self { + pub(crate) fn get_or_generate(request: &Request) -> Self { let span = request .headers() .get("x-span-id") diff --git a/src/tui.rs b/src/tui.rs index 3750fcf5e..93f5fd4ae 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -37,7 +37,7 @@ fn check_server_connection( apis::system_api::ping(&config).is_ok() } -pub fn run( +pub(crate) fn run( standalone: bool, port: u16, database: Option, diff --git a/src/tui/components.rs b/src/tui/components.rs index 13fb4c83a..7d311c9e5 100644 --- a/src/tui/components.rs +++ b/src/tui/components.rs @@ -16,11 +16,11 @@ use crate::client::utils::format_local_timestamp; /// A confirmation dialog that asks the user to confirm an action #[derive(Debug, Clone)] pub struct ConfirmationDialog { - pub title: String, - pub message: String, - pub confirm_text: String, - pub cancel_text: String, - pub is_destructive: bool, + title: String, + message: String, + confirm_text: String, + cancel_text: String, + is_destructive: bool, } impl Default for ConfirmationDialog { @@ -36,7 +36,7 @@ impl Default for ConfirmationDialog { } impl ConfirmationDialog { - pub fn new(title: &str, message: &str) -> Self { + pub(crate) fn new(title: &str, message: &str) -> Self { Self { title: title.to_string(), message: message.to_string(), @@ -44,17 +44,17 @@ impl ConfirmationDialog { } } - pub fn destructive(mut self) -> Self { + pub(crate) fn destructive(mut self) -> Self { self.is_destructive = true; self } - pub fn with_confirm_text(mut self, text: &str) -> Self { + fn with_confirm_text(mut self, text: &str) -> Self { self.confirm_text = text.to_string(); self } - pub fn render(&self, f: &mut Frame, area: Rect) { + pub(crate) fn render(&self, f: &mut Frame, area: Rect) { // Calculate dialog size - center in screen let dialog_width = 60.min(area.width.saturating_sub(4)); // Estimate the wrapped height of the message so multi-line content @@ -136,19 +136,19 @@ impl ConfirmationDialog { /// serves as the confirmation gate: pressing Enter applies, Esc cancels. #[derive(Debug, Clone)] pub struct RecoverPromptDialog { - pub title: String, - pub message: String, - pub memory_input: String, - pub runtime_input: String, + title: String, + message: String, + memory_input: String, + runtime_input: String, /// 0 = memory field, 1 = runtime field - pub active_field: u8, + active_field: u8, /// Validation error to show under the inputs (e.g. unparseable number) - pub error: Option, - pub is_destructive: bool, + error: Option, + is_destructive: bool, } impl RecoverPromptDialog { - pub fn new(title: &str, message: &str, is_destructive: bool) -> Self { + pub(crate) fn new(title: &str, message: &str, is_destructive: bool) -> Self { Self { title: title.to_string(), message: message.to_string(), @@ -160,7 +160,7 @@ impl RecoverPromptDialog { } } - pub fn toggle_field(&mut self) { + pub(crate) fn toggle_field(&mut self) { self.active_field = 1 - self.active_field; } @@ -172,7 +172,7 @@ impl RecoverPromptDialog { } } - pub fn add_char(&mut self, c: char) { + pub(crate) fn add_char(&mut self, c: char) { // Allow only digits and a single decimal point. if c.is_ascii_digit() || c == '.' { let buf = self.active_buf_mut(); @@ -186,14 +186,14 @@ impl RecoverPromptDialog { } } - pub fn backspace(&mut self) { + pub(crate) fn backspace(&mut self) { self.active_buf_mut().pop(); self.error = None; } /// Parse the two fields. Returns (memory_multiplier, runtime_multiplier) /// or an error message suitable for inline display. - pub fn parse(&self) -> Result<(f64, f64), String> { + pub(crate) fn parse(&self) -> Result<(f64, f64), String> { let mem = self .memory_input .parse::() @@ -208,11 +208,11 @@ impl RecoverPromptDialog { Ok((mem, rt)) } - pub fn set_error(&mut self, message: String) { + pub(crate) fn set_error(&mut self, message: String) { self.error = Some(message); } - pub fn render(&self, f: &mut Frame, area: Rect) { + pub(crate) fn render(&self, f: &mut Frame, area: Rect) { let dialog_width = 60.min(area.width.saturating_sub(4)); let dialog_height = 12.min(area.height.saturating_sub(2)); @@ -344,13 +344,13 @@ pub enum StatusLevel { /// A status message to display in the status bar #[derive(Debug, Clone)] pub struct StatusMessage { - pub message: String, - pub level: StatusLevel, - pub timestamp: std::time::Instant, + pub(crate) message: String, + level: StatusLevel, + timestamp: std::time::Instant, } impl StatusMessage { - pub fn info(message: &str) -> Self { + pub(crate) fn info(message: &str) -> Self { Self { message: message.to_string(), level: StatusLevel::Info, @@ -358,7 +358,7 @@ impl StatusMessage { } } - pub fn success(message: &str) -> Self { + pub(crate) fn success(message: &str) -> Self { Self { message: message.to_string(), level: StatusLevel::Success, @@ -366,7 +366,7 @@ impl StatusMessage { } } - pub fn warning(message: &str) -> Self { + pub(crate) fn warning(message: &str) -> Self { Self { message: message.to_string(), level: StatusLevel::Warning, @@ -374,7 +374,7 @@ impl StatusMessage { } } - pub fn error(message: &str) -> Self { + pub(crate) fn error(message: &str) -> Self { Self { message: message.to_string(), level: StatusLevel::Error, @@ -382,7 +382,7 @@ impl StatusMessage { } } - pub fn color(&self) -> Color { + pub(crate) fn color(&self) -> Color { match self.level { StatusLevel::Info => Color::Cyan, StatusLevel::Success => Color::Green, @@ -392,7 +392,7 @@ impl StatusMessage { } /// Check if message should still be displayed (auto-dismiss after 5 seconds for success/info) - pub fn is_visible(&self) -> bool { + pub(crate) fn is_visible(&self) -> bool { match self.level { StatusLevel::Success | StatusLevel::Info => { self.timestamp.elapsed() < std::time::Duration::from_secs(5) @@ -406,19 +406,19 @@ impl StatusMessage { /// Used when error messages are too long for the status bar #[derive(Debug, Clone)] pub struct ErrorDialog { - pub title: String, - pub message: String, + title: String, + message: String, } impl ErrorDialog { - pub fn new(title: &str, message: &str) -> Self { + pub(crate) fn new(title: &str, message: &str) -> Self { Self { title: title.to_string(), message: message.to_string(), } } - pub fn render(&self, f: &mut Frame, area: Rect) { + pub(crate) fn render(&self, f: &mut Frame, area: Rect) { // Calculate dialog size based on message length // Allow more width and height for long messages let dialog_width = 80.min(area.width.saturating_sub(4)); @@ -501,10 +501,10 @@ pub enum HelpContext { } /// A help popup showing keybindings relevant to the current context. -pub struct HelpPopup; +pub(crate) struct HelpPopup; impl HelpPopup { - pub fn render(f: &mut Frame, area: Rect, context: HelpContext) { + pub(crate) fn render(f: &mut Frame, area: Rect, context: HelpContext) { let popup_width = 70.min(area.width.saturating_sub(4)); let popup_height = 48.min(area.height.saturating_sub(2)); @@ -691,17 +691,17 @@ impl HelpPopup { /// Job details popup showing full job information #[derive(Debug, Clone)] pub struct JobDetailsPopup { - pub job_id: i64, - pub job_name: String, - pub command: String, - pub status: String, - pub compute_node_id: Option, - pub start_time: Option, - pub scroll_offset: u16, + job_id: i64, + job_name: String, + command: String, + status: String, + compute_node_id: Option, + start_time: Option, + scroll_offset: u16, } impl JobDetailsPopup { - pub fn new( + pub(crate) fn new( job_id: i64, job_name: String, command: String, @@ -720,15 +720,15 @@ impl JobDetailsPopup { } } - pub fn scroll_down(&mut self) { + pub(crate) fn scroll_down(&mut self) { self.scroll_offset = self.scroll_offset.saturating_add(1); } - pub fn scroll_up(&mut self) { + pub(crate) fn scroll_up(&mut self) { self.scroll_offset = self.scroll_offset.saturating_sub(1); } - pub fn render(&self, f: &mut Frame, area: Rect) { + pub(crate) fn render(&self, f: &mut Frame, area: Rect) { let popup_width = 80.min(area.width.saturating_sub(4)); let popup_height = 20.min(area.height.saturating_sub(2)); @@ -810,16 +810,16 @@ impl JobDetailsPopup { /// large/nested objects are readable. Scrollable for payloads taller than the popup. #[derive(Debug, Clone)] pub struct UserDataDetailsPopup { - pub user_data_id: i64, - pub name: String, - pub is_ephemeral: Option, + user_data_id: i64, + name: String, + is_ephemeral: Option, /// Pre-formatted, pretty-printed JSON payload (one entry per visual line). - pub data_pretty: String, - pub scroll_offset: u16, + data_pretty: String, + scroll_offset: u16, } impl UserDataDetailsPopup { - pub fn new( + pub(crate) fn new( user_data_id: i64, name: String, is_ephemeral: Option, @@ -840,15 +840,15 @@ impl UserDataDetailsPopup { } } - pub fn scroll_down(&mut self) { + pub(crate) fn scroll_down(&mut self) { self.scroll_offset = self.scroll_offset.saturating_add(1); } - pub fn scroll_up(&mut self) { + pub(crate) fn scroll_up(&mut self) { self.scroll_offset = self.scroll_offset.saturating_sub(1); } - pub fn render(&self, f: &mut Frame, area: Rect) { + pub(crate) fn render(&self, f: &mut Frame, area: Rect) { let popup_width = 90.min(area.width.saturating_sub(4)); let popup_height = 24.min(area.height.saturating_sub(2)); @@ -907,15 +907,19 @@ impl UserDataDetailsPopup { /// Popup showing expanded details for a single workflow. Rows are built by the /// caller as `(label, value)` pairs so this component stays decoupled from the /// workflow model. Scrollable for workflows with many configured fields. -pub struct WorkflowDetailsPopup { - pub workflow_id: i64, - pub workflow_name: String, - pub rows: Vec<(String, String)>, - pub scroll_offset: u16, +pub(crate) struct WorkflowDetailsPopup { + workflow_id: i64, + workflow_name: String, + rows: Vec<(String, String)>, + scroll_offset: u16, } impl WorkflowDetailsPopup { - pub fn new(workflow_id: i64, workflow_name: String, rows: Vec<(String, String)>) -> Self { + pub(crate) fn new( + workflow_id: i64, + workflow_name: String, + rows: Vec<(String, String)>, + ) -> Self { Self { workflow_id, workflow_name, @@ -924,15 +928,15 @@ impl WorkflowDetailsPopup { } } - pub fn scroll_down(&mut self) { + pub(crate) fn scroll_down(&mut self) { self.scroll_offset = self.scroll_offset.saturating_add(1); } - pub fn scroll_up(&mut self) { + pub(crate) fn scroll_up(&mut self) { self.scroll_offset = self.scroll_offset.saturating_sub(1); } - pub fn render(&self, f: &mut Frame, area: Rect) { + pub(crate) fn render(&self, f: &mut Frame, area: Rect) { let popup_width = 90.min(area.width.saturating_sub(4)); let popup_height = 24.min(area.height.saturating_sub(2)); @@ -982,18 +986,18 @@ impl WorkflowDetailsPopup { /// Log viewer for displaying job stdout/stderr #[derive(Debug, Clone)] pub struct LogViewer { - pub job_id: i64, - pub job_name: String, - pub stdout_path: Option, - pub stderr_path: Option, - pub stdout_content: String, - pub stderr_content: String, - pub active_tab: LogTab, - pub scroll_offset: u16, - pub search_query: String, - pub search_matches: Vec, // Line numbers with matches - pub current_match: usize, - pub is_searching: bool, + pub(crate) job_id: i64, + job_name: String, + pub(crate) stdout_path: Option, + pub(crate) stderr_path: Option, + pub(crate) stdout_content: String, + pub(crate) stderr_content: String, + active_tab: LogTab, + scroll_offset: u16, + search_query: String, + search_matches: Vec, // Line numbers with matches + current_match: usize, + pub(crate) is_searching: bool, } #[derive(Debug, Clone, Copy, PartialEq)] @@ -1003,7 +1007,7 @@ pub enum LogTab { } impl LogViewer { - pub fn new(job_id: i64, job_name: String) -> Self { + pub(crate) fn new(job_id: i64, job_name: String) -> Self { Self { job_id, job_name, @@ -1020,7 +1024,7 @@ impl LogViewer { } } - pub fn toggle_tab(&mut self) { + pub(crate) fn toggle_tab(&mut self) { self.active_tab = match self.active_tab { LogTab::Stdout => LogTab::Stderr, LogTab::Stderr => LogTab::Stdout, @@ -1028,58 +1032,58 @@ impl LogViewer { self.scroll_offset = 0; } - pub fn scroll_down(&mut self, amount: u16) { + pub(crate) fn scroll_down(&mut self, amount: u16) { self.scroll_offset = self.scroll_offset.saturating_add(amount); } - pub fn scroll_up(&mut self, amount: u16) { + pub(crate) fn scroll_up(&mut self, amount: u16) { self.scroll_offset = self.scroll_offset.saturating_sub(amount); } - pub fn scroll_to_top(&mut self) { + pub(crate) fn scroll_to_top(&mut self) { self.scroll_offset = 0; } - pub fn scroll_to_bottom(&mut self, visible_height: u16) { + pub(crate) fn scroll_to_bottom(&mut self, visible_height: u16) { let content = self.current_content(); let line_count = content.lines().count() as u16; self.scroll_offset = line_count.saturating_sub(visible_height); } - pub fn current_content(&self) -> &str { + fn current_content(&self) -> &str { match self.active_tab { LogTab::Stdout => &self.stdout_content, LogTab::Stderr => &self.stderr_content, } } - pub fn current_path(&self) -> Option<&str> { + pub(crate) fn current_path(&self) -> Option<&str> { match self.active_tab { LogTab::Stdout => self.stdout_path.as_deref(), LogTab::Stderr => self.stderr_path.as_deref(), } } - pub fn start_search(&mut self) { + pub(crate) fn start_search(&mut self) { self.is_searching = true; self.search_query.clear(); } - pub fn cancel_search(&mut self) { + pub(crate) fn cancel_search(&mut self) { self.is_searching = false; } - pub fn add_search_char(&mut self, c: char) { + pub(crate) fn add_search_char(&mut self, c: char) { self.search_query.push(c); self.update_search_matches(); } - pub fn remove_search_char(&mut self) { + pub(crate) fn remove_search_char(&mut self) { self.search_query.pop(); self.update_search_matches(); } - pub fn apply_search(&mut self) { + pub(crate) fn apply_search(&mut self) { self.is_searching = false; if !self.search_matches.is_empty() { self.jump_to_match(0); @@ -1107,14 +1111,14 @@ impl LogViewer { self.current_match = 0; } - pub fn next_match(&mut self) { + pub(crate) fn next_match(&mut self) { if !self.search_matches.is_empty() { self.current_match = (self.current_match + 1) % self.search_matches.len(); self.jump_to_current_match(); } } - pub fn prev_match(&mut self) { + pub(crate) fn prev_match(&mut self) { if !self.search_matches.is_empty() { self.current_match = if self.current_match == 0 { self.search_matches.len() - 1 @@ -1135,7 +1139,7 @@ impl LogViewer { self.jump_to_match(self.current_match); } - pub fn render(&self, f: &mut Frame, area: Rect) { + pub(crate) fn render(&self, f: &mut Frame, area: Rect) { f.render_widget(Clear, area); let block = Block::default() @@ -1272,19 +1276,19 @@ impl LogViewer { /// File viewer for displaying file contents #[derive(Debug, Clone)] pub struct FileViewer { - pub file_name: String, - pub file_path: String, - pub content: String, - pub scroll_offset: u16, - pub search_query: String, - pub search_matches: Vec, // Line numbers with matches - pub current_match: usize, - pub is_searching: bool, - pub is_binary: bool, + file_name: String, + pub(crate) file_path: String, + content: String, + scroll_offset: u16, + search_query: String, + search_matches: Vec, // Line numbers with matches + current_match: usize, + pub(crate) is_searching: bool, + is_binary: bool, } impl FileViewer { - pub fn new(file_name: String, file_path: String) -> Self { + pub(crate) fn new(file_name: String, file_path: String) -> Self { Self { file_name, file_path, @@ -1298,7 +1302,7 @@ impl FileViewer { } } - pub fn load_content(&mut self) -> Result<(), String> { + pub(crate) fn load_content(&mut self) -> Result<(), String> { let path = Path::new(&self.file_path); if !path.exists() { @@ -1374,43 +1378,43 @@ impl FileViewer { Ok(()) } - pub fn scroll_down(&mut self, amount: u16) { + pub(crate) fn scroll_down(&mut self, amount: u16) { self.scroll_offset = self.scroll_offset.saturating_add(amount); } - pub fn scroll_up(&mut self, amount: u16) { + pub(crate) fn scroll_up(&mut self, amount: u16) { self.scroll_offset = self.scroll_offset.saturating_sub(amount); } - pub fn scroll_to_top(&mut self) { + pub(crate) fn scroll_to_top(&mut self) { self.scroll_offset = 0; } - pub fn scroll_to_bottom(&mut self, visible_height: u16) { + pub(crate) fn scroll_to_bottom(&mut self, visible_height: u16) { let line_count = self.content.lines().count() as u16; self.scroll_offset = line_count.saturating_sub(visible_height); } - pub fn start_search(&mut self) { + pub(crate) fn start_search(&mut self) { self.is_searching = true; self.search_query.clear(); } - pub fn cancel_search(&mut self) { + pub(crate) fn cancel_search(&mut self) { self.is_searching = false; } - pub fn add_search_char(&mut self, c: char) { + pub(crate) fn add_search_char(&mut self, c: char) { self.search_query.push(c); self.update_search_matches(); } - pub fn remove_search_char(&mut self) { + pub(crate) fn remove_search_char(&mut self) { self.search_query.pop(); self.update_search_matches(); } - pub fn apply_search(&mut self) { + pub(crate) fn apply_search(&mut self) { self.is_searching = false; if !self.search_matches.is_empty() { self.jump_to_match(0); @@ -1437,14 +1441,14 @@ impl FileViewer { self.current_match = 0; } - pub fn next_match(&mut self) { + pub(crate) fn next_match(&mut self) { if !self.search_matches.is_empty() { self.current_match = (self.current_match + 1) % self.search_matches.len(); self.jump_to_current_match(); } } - pub fn prev_match(&mut self) { + pub(crate) fn prev_match(&mut self) { if !self.search_matches.is_empty() { self.current_match = if self.current_match == 0 { self.search_matches.len() - 1 @@ -1465,7 +1469,7 @@ impl FileViewer { self.jump_to_match(self.current_match); } - pub fn render(&self, f: &mut Frame, area: Rect) { + pub(crate) fn render(&self, f: &mut Frame, area: Rect) { f.render_widget(Clear, area); let block = Block::default() @@ -1577,18 +1581,18 @@ use std::sync::mpsc::{self, Receiver, TryRecvError}; use std::thread; pub struct ProcessViewer { - pub title: String, - pub output_lines: Vec, - pub scroll_offset: u16, - pub auto_scroll: bool, - pub is_running: bool, - pub kill_confirm: bool, + pub(crate) title: String, + output_lines: Vec, + scroll_offset: u16, + auto_scroll: bool, + pub(crate) is_running: bool, + pub(crate) kill_confirm: bool, child: Option, output_receiver: Option>, } impl ProcessViewer { - pub fn new(title: String) -> Self { + pub(crate) fn new(title: String) -> Self { Self { title, output_lines: Vec::new(), @@ -1603,7 +1607,7 @@ impl ProcessViewer { /// Start a process and capture its output. Takes a prebuilt `Command` so /// callers can attach connection arguments and environment variables. - pub fn start(&mut self, mut cmd: Command) -> Result<(), String> { + pub(crate) fn start(&mut self, mut cmd: Command) -> Result<(), String> { cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); let mut child = cmd @@ -1654,7 +1658,7 @@ impl ProcessViewer { } /// Poll for new output from the process - pub fn poll_output(&mut self) { + pub(crate) fn poll_output(&mut self) { // Don't poll if process is not running and we've already cleaned up if !self.is_running && self.output_receiver.is_none() { return; @@ -1720,19 +1724,19 @@ impl ProcessViewer { } /// Request kill confirmation - pub fn request_kill(&mut self) { + pub(crate) fn request_kill(&mut self) { if self.is_running { self.kill_confirm = true; } } /// Cancel kill confirmation - pub fn cancel_kill(&mut self) { + pub(crate) fn cancel_kill(&mut self) { self.kill_confirm = false; } /// Kill the running process - pub fn kill(&mut self) { + pub(crate) fn kill(&mut self) { self.kill_confirm = false; if let Some(ref mut child) = self.child { let _ = child.kill(); @@ -1742,34 +1746,34 @@ impl ProcessViewer { } } - pub fn scroll_down(&mut self, amount: u16) { + pub(crate) fn scroll_down(&mut self, amount: u16) { self.auto_scroll = false; self.scroll_offset = self.scroll_offset.saturating_add(amount); } - pub fn scroll_up(&mut self, amount: u16) { + pub(crate) fn scroll_up(&mut self, amount: u16) { self.auto_scroll = false; self.scroll_offset = self.scroll_offset.saturating_sub(amount); } - pub fn scroll_to_top(&mut self) { + pub(crate) fn scroll_to_top(&mut self) { self.auto_scroll = false; self.scroll_offset = 0; } - pub fn scroll_to_bottom(&mut self) { + pub(crate) fn scroll_to_bottom(&mut self) { self.auto_scroll = true; self.scroll_offset = u16::MAX; } - pub fn toggle_auto_scroll(&mut self) { + pub(crate) fn toggle_auto_scroll(&mut self) { self.auto_scroll = !self.auto_scroll; if self.auto_scroll { self.scroll_offset = u16::MAX; } } - pub fn render(&self, f: &mut Frame, area: Rect) { + pub(crate) fn render(&self, f: &mut Frame, area: Rect) { f.render_widget(Clear, area); let status_indicator = if self.is_running { diff --git a/src/tui_runner.rs b/src/tui_runner.rs index c948844ba..d9ba0271b 100644 --- a/src/tui_runner.rs +++ b/src/tui_runner.rs @@ -8,23 +8,23 @@ use crate::client::apis::configuration::BasicAuth; pub struct Args { /// Start in standalone mode: automatically start a torc-server #[arg(long)] - pub standalone: bool, + standalone: bool, /// Port for the server in standalone mode (default: 8080) #[arg(long, default_value = "8080")] - pub port: u16, + port: u16, /// Database path for standalone mode #[arg(long)] - pub database: Option, + database: Option, /// Path to a PEM-encoded CA certificate to trust for TLS connections #[arg(long, env = "TORC_TLS_CA_CERT")] - pub tls_ca_cert: Option, + tls_ca_cert: Option, /// Skip TLS certificate verification (for testing only) #[arg(long, env = "TORC_TLS_INSECURE")] - pub tls_insecure: bool, + tls_insecure: bool, } pub fn run(args: &Args, basic_auth: Option) -> Result<()> { diff --git a/tests/test_slurm_commands.rs b/tests/test_slurm_commands.rs index 97873f0ff..9ac0c0991 100644 --- a/tests/test_slurm_commands.rs +++ b/tests/test_slurm_commands.rs @@ -1,8 +1,8 @@ mod common; use common::{ - ServerProcess, create_minimal_resources_workflow, create_test_workflow, run_cli_with_json, - start_server, + ServerProcess, create_minimal_resources_workflow, create_test_workflow, run_cli_command, + run_cli_with_json, start_server, }; use rstest::rstest; use serde_json::json; @@ -573,21 +573,23 @@ fn test_schedule_nodes_rejects_job_prefix_for_serialized_scheduler(start_server: .expect("Failed to create Slurm scheduler"); let scheduler_id = scheduler.id.unwrap(); - let result = torc::client::commands::slurm::schedule_slurm_nodes( - config, - workflow_id, - scheduler_id, - 1, - false, - "run1_", - "torc_output", - 30, - None, - false, + let workflow_id = workflow_id.to_string(); + let scheduler_id = scheduler_id.to_string(); + let err = run_cli_command( + &[ + "slurm", + "schedule-nodes", + &workflow_id, + "--scheduler-config-id", + &scheduler_id, + "--job-prefix", + "run1_", + ], + start_server, None, - ); + ) + .expect_err("job_prefix must be rejected for a serialized scheduler"); - let err = result.expect_err("job_prefix must be rejected for a serialized scheduler"); assert!( err.to_string().contains("--job-prefix"), "Error should explain the job_prefix rejection, got: {}",