Skip to main content

MtimeFileCache

Struct MtimeFileCache 

Source
pub struct MtimeFileCache<T> { /* private fields */ }
Expand description

Per-path memoization of a file’s parsed contents, invalidated by mtime.

Caches whatever parse produces for a file’s raw content — validation, expansion, and policy checks are expected to run per call on the returned value, never cached. Absence is never cached: only a file that existed, was a regular file, and parsed successfully gets an entry, so a file created after the cache first found nothing is picked up on the very next call with no extra bookkeeping.

Implementations§

Source§

impl<T> MtimeFileCache<T>

Source

pub fn new(capacity: usize, label: &'static str) -> Self

Creates an empty cache holding at most capacity entries, labeled label for diagnostics (e.g. "cargo config", "npm config") when the capacity is reached.

Source

pub fn get_or_parse( &self, path: &Path, parse: impl FnOnce(&str) -> T, ) -> Option<Arc<T>>

Returns path’s parsed contents, from cache if path’s mtime is unchanged, else re-reading and re-parsing with parse. None if path does not exist, is not a regular file, exceeds MAX_CACHED_FILE_BYTES, or cannot be read.

Rejects anything but a regular file (a FIFO, socket, character device, or directory) as observed at stat time — reading one of those can block the calling thread indefinitely, and std::fs::metadata follows symlinks, so a symlinked regular file still resolves. This check is necessarily a point-in-time observation, not a guarantee about what crate::fs_probe::read_to_string_capped will see: a symlink that resolved to a regular file at stat time can still be swapped to a FIFO before the subsequent open (the same TOCTOU class as the size check below), which would still block on open. Fixing that blocking-open race is out of scope here (it needs O_NONBLOCK or equivalent); this doc only avoids overclaiming that the gate rules it out.

A file over MAX_CACHED_FILE_BYTES never reaches parse — every content-based safety guard (nesting-depth, expansion) only sees content already read into memory, so it cannot bound the read itself. This is enforced twice: the stat result is checked first as a cheap pre-filter (skips opening an obviously huge file), and crate::fs_probe::read_to_string_capped then bounds the read itself regardless of what the stat reported — so a symlink swap or concurrent growth between the stat and the read cannot let an oversized file’s content slip through (CWE-367).

Always performs one stat (the mtime check) — that cost is unavoidable and paid on every call, cache hit or not — but reads and parses the file’s content only on a miss. Compares mtime with !=, not >: a git checkout that restores an older file moves the mtime backwards, and > would then keep serving the stale cached entry.

§Examples
use deps_core::mtime_cache::{DEFAULT_MAX_CACHED_FILES, MtimeFileCache};
use std::sync::Arc;

let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("config.toml");
std::fs::write(&path, "value = 1").unwrap();

let cache: MtimeFileCache<String> = MtimeFileCache::new(DEFAULT_MAX_CACHED_FILES, "example");
let first = cache.get_or_parse(&path, str::to_owned).unwrap();
let second = cache.get_or_parse(&path, str::to_owned).unwrap();
assert!(Arc::ptr_eq(&first, &second), "an unchanged file is served from cache");

Trait Implementations§

Source§

impl<T> Debug for MtimeFileCache<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<T> !RefUnwindSafe for MtimeFileCache<T>

§

impl<T> Freeze for MtimeFileCache<T>

§

impl<T> Send for MtimeFileCache<T>
where T: Sync + Send,

§

impl<T> Sync for MtimeFileCache<T>
where T: Sync + Send,

§

impl<T> Unpin for MtimeFileCache<T>

§

impl<T> UnsafeUnpin for MtimeFileCache<T>

§

impl<T> UnwindSafe for MtimeFileCache<T>
where T: RefUnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more