pharos_native/runtime/runtime_stack.rs
1use std::cmp::Ordering;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5use super::{RuntimeStack, RuntimeStack as RuntimeStackState};
6use crate::config::ServeConfig;
7
8/// Compares strings.
9///
10/// # Parameters
11///
12/// - `left`: Input consumed by [`crate::runtime::runtime_stack::compare_strings`].
13/// - `right`: Input consumed by [`crate::runtime::runtime_stack::compare_strings`].
14///
15/// # Returns
16///
17/// Returns the computed value produced by [`crate::runtime::runtime_stack::compare_strings`].
18///
19/// # Examples
20///
21/// ```rust,ignore
22/// let _result = pharos_native::runtime::runtime_stack::compare_strings(todo!(), todo!());
23/// ```
24///
25/// # Related
26///
27/// - [`crate::runtime::runtime_stack`] for adjacent helpers that share the same runtime surface.
28pub fn compare_strings(left: &str, right: &str) -> Ordering {
29 left.cmp(right)
30}
31
32const MAX_MATCHING_FILES: usize = 4096;
33const MAX_MATCHING_DEPTH: usize = 32;
34
35/// Collects regular files below a bounded root that match a suffix.
36///
37/// # Parameters
38///
39/// - `root`: Input consumed by [`crate::runtime::runtime_stack::collect_matching_files`].
40/// - `suffix`: Input consumed by [`crate::runtime::runtime_stack::collect_matching_files`].
41///
42/// # Returns
43///
44/// Returns the successful value produced by [`crate::runtime::runtime_stack::collect_matching_files`] or an error when the operation fails.
45///
46/// # Errors
47///
48/// Returns an error value when [`crate::runtime::runtime_stack::collect_matching_files`] cannot complete successfully.
49///
50/// # Performance
51///
52/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::runtime_stack::collect_matching_files`].
53///
54/// # Examples
55///
56/// ```rust,ignore
57/// let _result = pharos_native::runtime::runtime_stack::collect_matching_files(std::path::Path::new("."), todo!());
58/// ```
59///
60/// # Related
61///
62/// - [`crate::runtime::runtime_stack`] for adjacent helpers that share the same runtime surface.
63pub fn collect_matching_files(root: &Path, suffix: &str) -> Result<Vec<PathBuf>, &'static str> {
64 if root.as_os_str().is_empty() || suffix.is_empty() || !root.is_dir() {
65 return Err("matching-file root is invalid");
66 }
67 /// Performs visit.
68 fn visit(
69 root: &Path,
70 suffix: &str,
71 depth: usize,
72 output: &mut Vec<PathBuf>,
73 ) -> Result<(), &'static str> {
74 if depth > MAX_MATCHING_DEPTH {
75 return Err("matching-file tree is too deep");
76 }
77 for entry in fs::read_dir(root).map_err(|_| "matching-file directory read failed")? {
78 let path = entry
79 .map_err(|_| "matching-file directory entry failed")?
80 .path();
81 if path.is_dir() {
82 visit(&path, suffix, depth + 1, output)?;
83 } else if path.is_file()
84 && path
85 .file_name()
86 .and_then(|name| name.to_str())
87 .is_some_and(|name| name.ends_with(suffix))
88 {
89 output.push(path);
90 if output.len() >= MAX_MATCHING_FILES {
91 return Ok(());
92 }
93 }
94 }
95 Ok(())
96 }
97 let mut output = Vec::new();
98 visit(root, suffix, 0, &mut output)?;
99 output.sort();
100 Ok(output)
101}
102
103/// Releases all owned runtime-stack application state.
104///
105/// # Parameters
106///
107/// - `stack`: Input consumed by [`crate::runtime::runtime_stack::free_runtime_stack`].
108///
109/// # Returns
110///
111/// Returns after [`crate::runtime::runtime_stack::free_runtime_stack`] applies its in-place effect.
112///
113/// # Examples
114///
115/// ```rust,ignore
116/// let _result = pharos_native::runtime::runtime_stack::free_runtime_stack(todo!());
117/// ```
118///
119/// # Related
120///
121/// - [`crate::runtime::runtime_stack`] for adjacent helpers that share the same runtime surface.
122pub fn free_runtime_stack(stack: &mut RuntimeStackState) {
123 stack.apps.clear();
124 stack.failed_apps.clear();
125 stack.host = None;
126 stack.port = None;
127 stack.socket_path = None;
128}
129
130/// Builds the runtime startup banner from the loaded stack and config.
131///
132/// # Parameters
133///
134/// - `stack`: Input consumed by [`crate::runtime::runtime_stack::runtime_log_banner`].
135/// - `config`: Input consumed by [`crate::runtime::runtime_stack::runtime_log_banner`].
136/// - `load_ms`: Input consumed by [`crate::runtime::runtime_stack::runtime_log_banner`].
137/// - `timestamp`: Input consumed by [`crate::runtime::runtime_stack::runtime_log_banner`].
138///
139/// # Returns
140///
141/// Returns the computed value produced by [`crate::runtime::runtime_stack::runtime_log_banner`].
142///
143/// # Examples
144///
145/// ```rust,ignore
146/// let _result = pharos_native::runtime::runtime_stack::runtime_log_banner(todo!(), todo!(), todo!(), todo!());
147/// ```
148///
149/// # Related
150///
151/// - [`crate::runtime::runtime_stack`] for adjacent helpers that share the same runtime surface.
152pub fn runtime_log_banner(
153 stack: &RuntimeStack,
154 config: &ServeConfig,
155 load_ms: i64,
156 timestamp: &str,
157) -> Vec<String> {
158 let mut lines = vec![
159 format!("pharos runtime {}", super::PHAROS_VERSION),
160 format!("loaded_apps={}", stack.apps.len()),
161 format!("load_ms={}", load_ms.max(0)),
162 format!("timestamp={timestamp}"),
163 ];
164 if let Some(host) = config.host.as_deref() {
165 lines.push(format!("host={host}"));
166 }
167 if let Some(port) = config.port.as_deref() {
168 lines.push(format!("port={port}"));
169 }
170 lines
171}
172
173/// Reports status or build readiness without mutating source or artifact roots.
174///
175/// # Parameters
176///
177/// - `verb`: Input consumed by [`crate::runtime::runtime_stack::shell_status_or_build`].
178/// - `artifact_root`: Input consumed by [`crate::runtime::runtime_stack::shell_status_or_build`].
179///
180/// # Returns
181///
182/// Returns the successful value produced by [`crate::runtime::runtime_stack::shell_status_or_build`] or an error when the operation fails.
183///
184/// # Errors
185///
186/// Returns an error value when [`crate::runtime::runtime_stack::shell_status_or_build`] cannot complete successfully.
187///
188/// # Examples
189///
190/// ```rust,ignore
191/// let _result = pharos_native::runtime::runtime_stack::shell_status_or_build(todo!(), todo!());
192/// ```
193///
194/// # Related
195///
196/// - [`crate::runtime::runtime_stack`] for adjacent helpers that share the same runtime surface.
197pub fn shell_status_or_build(verb: &str, artifact_root: &Path) -> Result<String, &'static str> {
198 if !matches!(verb, "status" | "build") || artifact_root.as_os_str().is_empty() {
199 return Err("unsupported status or build operation");
200 }
201 let manifest = artifact_root.join("pharos.app.json");
202 let present = manifest.is_file();
203 serde_json::to_string(&serde_json::json!({
204 "operation": verb,
205 "artifact_root": artifact_root,
206 "manifest_present": present,
207 "status": if present { "ready" } else { "missing" },
208 }))
209 .map_err(|_| "status report encoding failed")
210}
211
212/// Validates the filename classes included in a runtime fingerprint.
213///
214/// # Parameters
215///
216/// - `name`: Input consumed by [`crate::runtime::runtime_stack::is_fingerprint_target_name`].
217///
218/// # Returns
219///
220/// Returns `true` when the condition checked by [`crate::runtime::runtime_stack::is_fingerprint_target_name`] holds and `false` otherwise.
221///
222/// # Examples
223///
224/// ```rust,ignore
225/// let _result = pharos_native::runtime::runtime_stack::is_fingerprint_target_name("name");
226/// ```
227///
228/// # Related
229///
230/// - [`crate::runtime::runtime_stack`] for adjacent helpers that share the same runtime surface.
231pub fn is_fingerprint_target_name(name: &str) -> bool {
232 !name.is_empty()
233 && !name.starts_with('.')
234 && (name.ends_with(".json")
235 || name.ends_with(".yml")
236 || name.ends_with(".yaml")
237 || name == "runtime-route-map.tsv")
238}