Skip to main content

docling_core/
assets.rs

1//! Runtime-asset path resolution shared by every crate.
2//!
3//! All optional runtime assets (ONNX models, OCR dictionaries, the chunker
4//! tokenizer) live under one directory, [`MODELS_DIR`] (`.models/`) —
5//! dot-prefixed like `.pdfium/`, so the plain `models` name stays free for
6//! source code. Resolution is CWD-relative with an executable-directory
7//! fallback.
8
9/// The runtime-asset directory, relative to the working directory.
10pub const MODELS_DIR: &str = ".models";
11
12/// Resolve a default (CWD-relative) asset path. If it doesn't exist relative
13/// to the current directory, try next to the executable and one level above
14/// it (following symlinks — the layout `scripts/install/install.sh` produces:
15/// `/usr/local/bin/docling-rs` → `/usr/local/docling.rs/bin/docling-rs` with
16/// `.models/` and `.pdfium/` in `/usr/local/docling.rs`). Returns `rel`
17/// unchanged when nothing exists anywhere, so callers' error messages keep
18/// the familiar path. Explicit env overrides never reach this.
19pub fn resolve(rel: &str) -> String {
20    if std::path::Path::new(rel).exists() {
21        return rel.to_string();
22    }
23    let dir = std::env::current_exe()
24        .ok()
25        .and_then(|p| p.canonicalize().ok())
26        .and_then(|p| p.parent().map(std::path::Path::to_path_buf));
27    if let Some(dir) = dir {
28        for base in [Some(dir.as_path()), dir.parent()].into_iter().flatten() {
29            let p = base.join(rel);
30            if p.exists() {
31                return p.to_string_lossy().into_owned();
32            }
33        }
34    }
35    rel.to_string()
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn missing_everywhere_returns_input() {
44        assert_eq!(
45            resolve(".models/definitely/not/there.onnx"),
46            ".models/definitely/not/there.onnx"
47        );
48    }
49}