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    /// `.siblings_doctor_cache/`: `siblings doctor` run entries.
23    Siblings,
24}
25
26impl CacheLevel {
27    #[must_use]
28    pub fn as_str(self) -> &'static str {
29        match self {
30            Self::ByInput => "by-input",
31            Self::ByContent => "by-content",
32            Self::Siblings => "siblings",
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, covering both workspace caches.
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    pub siblings: CacheLevelStats,
52    /// `by-input` entries created as aliases of a content-level hit.
53    pub aliases: u64,
54    pub total_bytes: u64,
55    /// RFC3339 write times of the oldest and newest entries across
56    /// every level; absent when all caches are empty.
57    #[serde(default, skip_serializing_if = "Option::is_none")]
58    pub oldest_entry_at: Option<String>,
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub newest_entry_at: Option<String>,
61}
62
63/// One row in `cache list`.
64#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
66pub struct CacheListRow {
67    /// The entry's key hash (the file-name stem after the format
68    /// prefix); unique prefixes of it address entries in `show` and
69    /// `evict`.
70    pub key: String,
71    pub level: CacheLevel,
72    /// The evaluated commit, for verdict entries that carry one.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub commit: Option<String>,
75    /// The series or pin target, when the pre-image names one.
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub series: Option<String>,
78    pub age_days: u32,
79    pub bytes: u64,
80    /// True for `by-input` entries created from a content-level hit.
81    #[serde(default)]
82    pub alias: bool,
83}
84
85/// The `cache list` payload.
86#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
87#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
88pub struct CacheListPayload {
89    pub rows: Vec<CacheListRow>,
90}
91
92/// The `cache show` payload: one entry, pre-image included.
93#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
94#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
95pub struct CacheShowPayload {
96    pub key: String,
97    pub level: CacheLevel,
98    pub created_at: String,
99    pub age_days: u32,
100    pub bytes: u64,
101    /// True for `by-input` entries created as aliases of a
102    /// content-level hit.
103    #[serde(default)]
104    pub alias: bool,
105    /// The canonical-JSON key pre-image stored inside the entry.
106    pub key_inputs: serde_json::Value,
107    /// Verdict counts for verdict entries; absent for siblings
108    /// entries.
109    #[serde(default, skip_serializing_if = "Option::is_none")]
110    pub verdict_summary: Option<serde_json::Value>,
111    /// The full stored value, under `--full` only.
112    #[serde(default, skip_serializing_if = "Option::is_none")]
113    pub value: Option<serde_json::Value>,
114}
115
116/// Payload for `cache evict`, `cache prune`, and `cache clear`.
117#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
118#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
119pub struct CacheMutationPayload {
120    pub removed_entries: u64,
121    pub removed_bytes: u64,
122}