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}