Skip to main content

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}