Skip to main content

yt_dlp/cache/
config.rs

1//! Cache configuration types.
2//!
3//! Provides `CacheConfig` for configuring the tiered cache system including
4//! TTL values and backend-specific connection settings.
5
6use std::fmt;
7use std::path::PathBuf;
8
9use typed_builder::TypedBuilder;
10
11#[cfg(persistent_cache)]
12use crate::error::{Error, Result};
13
14/// Number of persistent backends compiled into this binary.
15///
16/// Used by `PersistentBackendKind::resolve` to detect ambiguity when
17/// `persistent_backend` is left as `None` in `CacheConfig`.
18#[cfg(persistent_cache)]
19const PERSISTENT_BACKEND_COUNT: usize = cfg!(feature = "cache-json") as usize
20    + cfg!(feature = "cache-redb") as usize
21    + cfg!(feature = "cache-redis") as usize;
22
23/// Selects which persistent L2 cache backend to use at runtime.
24///
25/// When exactly one persistent feature is compiled in, the backend is
26/// deduced automatically and this field can be left as `None` in `CacheConfig`.
27/// When several features are enabled simultaneously, the caller **must** set
28/// `CacheConfig::persistent_backend` explicitly; leaving it as `None` causes
29/// `CacheLayer::from_config` to return `Error::AmbiguousCacheBackend`.
30#[cfg(persistent_cache)]
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32pub enum PersistentBackendKind {
33    /// JSON-file backend (`cache-json` feature).
34    #[cfg(feature = "cache-json")]
35    Json,
36    /// Embedded redb backend (`cache-redb` feature).
37    #[cfg(feature = "cache-redb")]
38    Redb,
39    /// Distributed Redis backend (`cache-redis` feature).
40    #[cfg(feature = "cache-redis")]
41    Redis,
42}
43
44#[cfg(persistent_cache)]
45impl PersistentBackendKind {
46    /// Resolves the backend to use.
47    ///
48    /// # Arguments
49    ///
50    /// * `kind` - An explicit selection, or `None` to auto-detect.
51    ///
52    /// # Errors
53    ///
54    /// Returns `Error::AmbiguousCacheBackend` when `kind` is `None` and
55    /// more than one persistent feature is compiled in.
56    ///
57    /// # Returns
58    ///
59    /// The resolved `PersistentBackendKind`.
60    pub fn resolve(kind: Option<Self>) -> Result<Self> {
61        if let Some(k) = kind {
62            return Ok(k);
63        }
64        if PERSISTENT_BACKEND_COUNT > 1 {
65            return Err(Error::ambiguous_cache_backend(PERSISTENT_BACKEND_COUNT));
66        }
67        // Exactly one persistent feature compiled in — auto-detect via exclusive cfg guards.
68        // Each combination compiles exactly one `let backend` binding.
69        #[cfg(feature = "cache-json")]
70        let backend = Self::Json;
71        #[cfg(all(feature = "cache-redb", not(feature = "cache-json")))]
72        let backend = Self::Redb;
73        #[cfg(all(feature = "cache-redis", not(feature = "cache-json"), not(feature = "cache-redb")))]
74        let backend = Self::Redis;
75
76        Ok(backend)
77    }
78}
79
80#[cfg(persistent_cache)]
81impl fmt::Display for PersistentBackendKind {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        match self {
84            #[cfg(feature = "cache-json")]
85            Self::Json => f.write_str("Json"),
86            #[cfg(feature = "cache-redb")]
87            Self::Redb => f.write_str("Redb"),
88            #[cfg(feature = "cache-redis")]
89            Self::Redis => f.write_str("Redis"),
90        }
91    }
92}
93
94/// Configuration for the tiered cache system.
95///
96/// Uses `TypedBuilder` for ergonomic construction with sensible defaults.
97///
98/// # Examples
99///
100/// ```rust,no_run
101/// use std::path::PathBuf;
102///
103/// use yt_dlp::cache::CacheConfig;
104///
105/// let config = CacheConfig::builder()
106///     .cache_dir(PathBuf::from("cache"))
107///     .build();
108/// ```
109#[derive(Debug, Clone, TypedBuilder)]
110pub struct CacheConfig {
111    /// Directory where cache data will be stored.
112    pub cache_dir: PathBuf,
113
114    /// Connection URL for Redis backend (e.g. "redis://127.0.0.1/").
115    /// Only used when `cache-redis` feature is enabled.
116    #[builder(default)]
117    pub redis_url: Option<String>,
118
119    /// Time-to-live for video cache entries in seconds.
120    /// Default: 24 hours (86400 seconds).
121    #[builder(default)]
122    pub video_ttl: Option<u64>,
123
124    /// Time-to-live for playlist cache entries in seconds.
125    /// Default: 6 hours (21600 seconds).
126    #[builder(default)]
127    pub playlist_ttl: Option<u64>,
128
129    /// Time-to-live for download/file cache entries in seconds.
130    /// Default: 7 days (604800 seconds).
131    #[builder(default)]
132    pub download_ttl: Option<u64>,
133
134    /// Which persistent backend to activate at runtime.
135    ///
136    /// Leave as `None` when exactly one persistent feature is compiled in
137    /// (it is deduced automatically). Must be set explicitly when several
138    /// persistent features (`cache-json`, `cache-redb`, `cache-redis`) are
139    /// compiled in simultaneously.
140    #[cfg(persistent_cache)]
141    #[builder(default)]
142    pub persistent_backend: Option<PersistentBackendKind>,
143}
144
145impl fmt::Display for CacheConfig {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        write!(
148            f,
149            "CacheConfig(dir={:?}, redis={}, video_ttl={:?}, playlist_ttl={:?}, download_ttl={:?}",
150            self.cache_dir,
151            self.redis_url.as_deref().unwrap_or("none"),
152            self.video_ttl,
153            self.playlist_ttl,
154            self.download_ttl,
155        )?;
156        #[cfg(persistent_cache)]
157        write!(
158            f,
159            ", backend={}",
160            self.persistent_backend.map_or("auto".to_string(), |k| k.to_string()),
161        )?;
162        f.write_str(")")
163    }
164}