Skip to main content

backhopper_core/model/
cache.rs

1// Copyright (C) 2026 Michael S. Klishin and Contributors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// See LICENSE-APACHE and LICENSE-MIT for details.
4
5//! Wire payloads for the `cache` verb group.
6//!
7//! The data behind these types is produced by `backhopper-cache`
8//! (which depends on this crate); the shapes live here so the schema
9//! generator and the driver see them without a dependency cycle.
10
11use serde::{Deserialize, Serialize};
12
13/// Which workspace cache an entry belongs to.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
16#[serde(rename_all = "snake_case")]
17pub enum CacheLevel {
18    /// `.verdict_cache/by-input/`: SHA-keyed verdict entries.
19    ByInput,
20    /// `.verdict_cache/by-content/`: content-keyed verdict entries.
21    ByContent,
22}
23
24impl CacheLevel {
25    /// The on-disk directory name, hyphenated. Distinct from the wire
26    /// spelling (`#[serde(rename_all = "snake_case")]`): this name is a
27    /// path component, not a label a `vocabulary!` enum would emit.
28    #[must_use]
29    pub fn dir_name(self) -> &'static str {
30        match self {
31            Self::ByInput => "by-input",
32            Self::ByContent => "by-content",
33        }
34    }
35}
36
37/// Per-level counters inside `cache stats`.
38#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
39#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
40pub struct CacheLevelStats {
41    pub entries: u64,
42    pub bytes: u64,
43}
44
45/// The `cache stats` payload.
46#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
47#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
48pub struct CacheStatsPayload {
49    pub by_input: CacheLevelStats,
50    pub by_content: CacheLevelStats,
51    /// `by-input` entries created as aliases of a content-level hit.
52    pub aliases: u64,
53    pub total_bytes: u64,
54    /// RFC3339 write times of the oldest and newest entries across
55    /// every level; absent when all caches are empty.
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub oldest_entry_at: Option<String>,
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub newest_entry_at: Option<String>,
60}
61
62/// One row in `cache list`.
63#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
64#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
65pub struct CacheListRow {
66    /// The entry's key hash (the file-name stem after the format
67    /// prefix); unique prefixes of it address entries in `show` and
68    /// `evict`.
69    pub key: String,
70    pub level: CacheLevel,
71    /// The evaluated commit, for verdict entries that carry one.
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub commit: Option<String>,
74    /// The series or pin target, when the pre-image names one.
75    #[serde(default, skip_serializing_if = "Option::is_none")]
76    pub series: Option<String>,
77    pub age_days: u32,
78    pub bytes: u64,
79    /// True for `by-input` entries created from a content-level hit.
80    #[serde(default)]
81    pub alias: bool,
82}
83
84/// The `cache list` payload.
85#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
86#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
87pub struct CacheListPayload {
88    pub rows: Vec<CacheListRow>,
89}
90
91/// The `cache show` payload: one entry, pre-image included.
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
94pub struct CacheShowPayload {
95    pub key: String,
96    pub level: CacheLevel,
97    pub created_at: String,
98    pub age_days: u32,
99    pub bytes: u64,
100    /// True for `by-input` entries created as aliases of a
101    /// content-level hit.
102    #[serde(default)]
103    pub alias: bool,
104    /// The canonical-JSON key pre-image stored inside the entry.
105    pub key_inputs: serde_json::Value,
106    /// Verdict counts for the stored evaluation.
107    #[serde(default, skip_serializing_if = "Option::is_none")]
108    pub verdict_summary: Option<serde_json::Value>,
109    /// The full stored value, under `--full` only.
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    pub value: Option<serde_json::Value>,
112}
113
114/// Payload for `cache evict`, `cache prune`, and `cache clear`.
115#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
116#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
117pub struct CacheMutationPayload {
118    pub removed_entries: u64,
119    pub removed_bytes: u64,
120}