zshrs 0.12.55

The first JIT-compiled Unix shell — bytecode VM, Cranelift JIT, worker pool, AOP intercept, Rkyv caching
Documentation
//! Port of `_retrieve_cache` from
//! `Completion/Base/Utility/_retrieve_cache`.
//!
//! Full upstream body (31 lines verbatim):
//! ```text
//! sh: 1  #autoload
//! sh: 5  local _cache_ident _cache_dir _cache_path _cache_policy
//! sh: 6  _cache_ident="$1"
//! sh: 8  if zstyle -t ":completion:${curcontext}:" use-cache; then
//! sh:10    zstyle -s ":completion:${curcontext}:" cache-path _cache_dir
//! sh:11    : ${_cache_dir:=${ZDOTDIR:-$HOME}/.zcompcache}
//! sh:12    if [[ ! -d "$_cache_dir" ]]; then
//! sh:13      [[ -e "$_cache_dir" ]] &&
//! sh:14        _message "cache-dir ($_cache_dir) isn't a directory\!"
//! sh:15      return 1
//! sh:16    fi
//! sh:18    _cache_path="$_cache_dir/$_cache_ident"
//! sh:20    if [[ -e "$_cache_path" ]]; then
//! sh:21      _cache_invalid "$_cache_ident" && return 1
//! sh:22
//! sh:23      . "$_cache_path"
//! sh:24      return 0
//! sh:25    else
//! sh:26      return 1
//! sh:27    fi
//! sh:28  else
//! sh:29    return 1
//! sh:30  fi
//! ```
//!
//! `. "$_cache_path"` — sources the cache file in the current
//! shell via `execute_script(". '<path>'")`, running the real `.`
//! builtin (`bin_dot`) through the executor pipeline so the cached
//! parameters are restored at the caller's locallevel. Without an
//! executor in scope the load step is a no-op and we still return
//! success when the file exists + is fresh.

use crate::compsys::ported::_cache_invalid::_cache_invalid;
use crate::compsys::ported::_message::_message;
use crate::ported::exec::execute_script;
use crate::ported::modules::zutil::{lookupstyle, testforstyle};
use crate::ported::params::getsparam;
use std::path::Path;

/// Reach `_retrieve_cache` as a BARE COMMAND WORD, the way every upstream caller
/// writes it — `_retrieve_cache luarocks_installed_list` (Completion/Unix/Command/_luarocks sh:213) — so the normal function lookup runs.
///
/// This is the DEFAULT entry point for the port, and the one a sibling port
/// should call. It goes through
/// [`crate::compsys::ported::shared::call_compfn`], which supplies both of
/// the things a bare Rust call to the body would skip: `$fpath` / shfunc
/// arbitration (the user's own copy of the function wins instead of being
/// inert) and the `doshfunc` frame (a `FUNCSTACK` entry, and the callee's
/// `declare_locals` landing in its OWN param scope rather than the caller's).
///
/// [`_retrieve_cache_impl`] is the raw body, reserved for the two callers that must not
/// re-enter dispatch: this wrapper's own fallback (it runs only when neither
/// a shell function nor a registered port claims the name — i.e. unit tests
/// with no executor installed), and the `compsys::router` arm, which has to
/// target the body or dispatch would re-enter this wrapper forever.
pub fn _retrieve_cache(args: &[String]) -> i32 {
    crate::compsys::ported::shared::call_compfn("_retrieve_cache", args, || {
        _retrieve_cache_impl(args)
    })
}

/// `_retrieve_cache` — load `$cache_ident` from disk if the cache
/// is enabled, exists, and isn't invalid per `_cache_invalid`.
/// Returns 0 on successful load, 1 otherwise.
pub fn _retrieve_cache_impl(args: &[String]) -> i32 {
    let _fn_scope = crate::compsys::ported::shared::FnScope::enter("_retrieve_cache");
    let cache_ident = args.first().cloned().unwrap_or_default();
    let curcontext = getsparam("curcontext").unwrap_or_default();
    let ctx = format!(":completion:{}:", curcontext);

    // sh:8
    if testforstyle(&ctx, "use-cache") != 0 {
        return 1;
    }

    // sh:10-11
    let cache_dir = lookupstyle(&ctx, "cache-path")
        .first()
        .cloned()
        .unwrap_or_else(|| {
            let home = getsparam("ZDOTDIR")
                .filter(|s| !s.is_empty())
                .or_else(|| getsparam("HOME"))
                .unwrap_or_default();
            format!("{}/.zcompcache", home)
        });

    // sh:12-16
    let dir_meta = std::fs::metadata(&cache_dir);
    match dir_meta {
        Ok(m) if m.is_dir() => {}
        Ok(_) => {
            let _ = _message(&[format!("cache-dir ({}) isn't a directory!", cache_dir)]);
            return 1;
        }
        Err(_) => return 1,
    }

    // sh:18
    let cache_path = format!("{}/{}", cache_dir, cache_ident);

    // sh:20
    if Path::new(&cache_path).exists() {
        // sh:21
        if _cache_invalid(&[cache_ident]) == 0 {
            return 1;
        }
        // sh:23  `. "$_cache_path"` — source the cache file in the
        // current shell so the cached parameters (e.g.
        // `_docker_subcommands`) are restored in the caller's scope.
        // `source` is a BUILTIN (bin_dot), not a shell function, so
        // `dispatch_function_call("source", …)` found no shfunc/Rust
        // port and silently no-op'd: the file was never sourced, yet
        // this returned 0 ("loaded"), so the completer skipped
        // regeneration and produced 0 matches (docker <tab> under
        // `use-cache on`). Run the real `.` builtin through the
        // executor pipeline — it restores the params at the current
        // locallevel exactly like zsh's `. file` inside a function.
        let quoted = format!("'{}'", cache_path.replace('\'', "'\\''"));
        let _ = execute_script(&format!(". {}", quoted));
        // sh:24
        0
    } else {
        // sh:26
        1
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn returns_one_when_use_cache_disabled() {
        let _g = crate::test_util::global_state_lock();
        assert_eq!(_retrieve_cache_impl(&["my-cache".to_string()]), 1);
    }
}