Skip to main content

apimock_server/
json_path_util.rs

1//! Filesystem helpers for resolving URL paths onto JSON-compatible files.
2//!
3//! # Why these live in server, not routing
4//!
5//! Pre-5.0 these sat alongside `json_value_by_jsonpath` in
6//! `core::util::json`. But `resolve_with_json_compatible_extensions`
7//! and `is_equivalent_json_file` both touch the filesystem to answer
8//! "does this path exist as a file?". That's a server-layer activity —
9//! the routing layer stays abstract and doesn't care where bytes come
10//! from — so they moved here in 5.0.
11
12use std::path::Path;
13
14use apimock_routing::util::json::JSON_COMPATIBLE_EXTENSIONS;
15
16/// Directory index filename (same spirit as `index.html`).
17pub const ROOT_DIRECTORY_FILE_NAME: &str = "index";
18
19/// Try to resolve a user-visible URL path onto a real file on disk.
20///
21/// Resolution order (first hit wins):
22/// 1. Exact match (`unknown_path` is already a file).
23/// 2. `unknown_path.<ext>` for each extension in `JSON_COMPATIBLE_EXTENSIONS`.
24/// 3. `unknown_path/index.<ext>` for the same extensions.
25/// 4. `unknown_path/index.html` — present so the fallback responder
26///    can also serve a directory root HTML, matching common
27///    web-server conventions.
28pub fn resolve_with_json_compatible_extensions(unknown_path: &str) -> Option<String> {
29    let p = Path::new(unknown_path);
30    if p.is_file() {
31        return Some(unknown_path.to_owned());
32    }
33
34    for ext in JSON_COMPATIBLE_EXTENSIONS {
35        let with_ext = format!("{}.{}", unknown_path, ext);
36        if Path::new(&with_ext).is_file() {
37            return Some(with_ext);
38        }
39    }
40
41    for ext in JSON_COMPATIBLE_EXTENSIONS {
42        let p = Path::new(unknown_path).join(format!("{}.{}", ROOT_DIRECTORY_FILE_NAME, ext));
43        if p.is_file() {
44            return p
45                .canonicalize()
46                .ok()
47                .and_then(|c| c.to_str().map(|s| s.to_owned()));
48        }
49    }
50
51    let p = Path::new(unknown_path).join(format!("{}.html", ROOT_DIRECTORY_FILE_NAME));
52    if p.is_file() {
53        return p
54            .canonicalize()
55            .ok()
56            .and_then(|c| c.to_str().map(|s| s.to_owned()));
57    }
58
59    None
60}