reth_node_core/
dirs.rs
1use crate::{args::DatadirArgs, utils::parse_path};
4use reth_chainspec::Chain;
5use std::{
6 env::VarError,
7 fmt::{Debug, Display, Formatter},
8 path::{Path, PathBuf},
9 str::FromStr,
10};
11
12pub fn config_path_prefix(chain: Chain) -> String {
14 chain.to_string()
15}
16
17pub fn data_dir() -> Option<PathBuf> {
21 dirs_next::data_dir().map(|root| root.join("reth"))
22}
23
24pub fn database_path() -> Option<PathBuf> {
28 data_dir().map(|root| root.join("db"))
29}
30
31pub fn config_dir() -> Option<PathBuf> {
35 dirs_next::config_dir().map(|root| root.join("reth"))
36}
37
38pub fn cache_dir() -> Option<PathBuf> {
42 dirs_next::cache_dir().map(|root| root.join("reth"))
43}
44
45pub fn logs_dir() -> Option<PathBuf> {
49 cache_dir().map(|root| root.join("logs"))
50}
51
52#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
57#[non_exhaustive]
58pub struct DataDirPath;
59
60impl XdgPath for DataDirPath {
61 fn resolve() -> Option<PathBuf> {
62 data_dir()
63 }
64}
65
66#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
70#[non_exhaustive]
71pub struct LogsDir;
72
73impl XdgPath for LogsDir {
74 fn resolve() -> Option<PathBuf> {
75 logs_dir()
76 }
77}
78
79pub trait XdgPath {
82 fn resolve() -> Option<PathBuf>;
84}
85
86#[derive(Debug, PartialEq, Eq)]
106pub struct PlatformPath<D>(PathBuf, std::marker::PhantomData<D>);
107
108impl<D> Display for PlatformPath<D> {
109 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
110 write!(f, "{}", self.0.display())
111 }
112}
113
114impl<D> Clone for PlatformPath<D> {
115 fn clone(&self) -> Self {
116 Self(self.0.clone(), std::marker::PhantomData)
117 }
118}
119
120impl<D: XdgPath> Default for PlatformPath<D> {
121 fn default() -> Self {
122 Self(
123 D::resolve().expect("Could not resolve default path. Set one manually."),
124 std::marker::PhantomData,
125 )
126 }
127}
128
129impl<D> FromStr for PlatformPath<D> {
130 type Err = shellexpand::LookupError<VarError>;
131
132 fn from_str(s: &str) -> Result<Self, Self::Err> {
133 Ok(Self(parse_path(s)?, std::marker::PhantomData))
134 }
135}
136
137impl<D> AsRef<Path> for PlatformPath<D> {
138 fn as_ref(&self) -> &Path {
139 self.0.as_path()
140 }
141}
142
143impl<D> From<PlatformPath<D>> for PathBuf {
144 fn from(value: PlatformPath<D>) -> Self {
145 value.0
146 }
147}
148
149impl<D> PlatformPath<D> {
150 pub fn join<P: AsRef<Path>>(&self, path: P) -> Self {
152 Self(self.0.join(path), std::marker::PhantomData)
153 }
154}
155
156impl<D> PlatformPath<D> {
157 pub fn with_chain(&self, chain: Chain, datadir_args: DatadirArgs) -> ChainPath<D> {
159 let platform_path = self.platform_path_from_chain(chain);
161
162 ChainPath::new(platform_path, chain, datadir_args)
163 }
164
165 fn platform_path_from_chain(&self, chain: Chain) -> Self {
166 let chain_name = config_path_prefix(chain);
167 let path = self.0.join(chain_name);
168 Self(path, std::marker::PhantomData)
169 }
170
171 pub fn map_to<T>(&self) -> PlatformPath<T> {
173 PlatformPath(self.0.clone(), std::marker::PhantomData)
174 }
175}
176
177#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct MaybePlatformPath<D>(Option<PlatformPath<D>>);
182
183impl<D: XdgPath> MaybePlatformPath<D> {
186 pub fn unwrap_or_chain_default(&self, chain: Chain, datadir_args: DatadirArgs) -> ChainPath<D> {
188 ChainPath(
189 self.0
190 .clone()
191 .unwrap_or_else(|| PlatformPath::default().platform_path_from_chain(chain)),
192 chain,
193 datadir_args,
194 )
195 }
196
197 pub fn chain_default(chain: Chain) -> ChainPath<D> {
199 PlatformPath::default().with_chain(chain, DatadirArgs::default())
200 }
201
202 pub const fn is_some(&self) -> bool {
204 self.0.is_some()
205 }
206
207 pub fn as_ref(&self) -> Option<&Path> {
209 self.0.as_ref().map(|p| p.as_ref())
210 }
211
212 pub fn unwrap_or_default(&self) -> PlatformPath<D> {
215 self.0.clone().unwrap_or_default()
216 }
217}
218
219impl<D: XdgPath> Display for MaybePlatformPath<D> {
220 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
221 if let Some(path) = &self.0 {
222 path.fmt(f)
223 } else {
224 write!(f, "default")
227 }
228 }
229}
230
231impl<D> Default for MaybePlatformPath<D> {
232 fn default() -> Self {
233 Self(None)
234 }
235}
236
237impl<D> FromStr for MaybePlatformPath<D> {
238 type Err = shellexpand::LookupError<VarError>;
239
240 fn from_str(s: &str) -> Result<Self, Self::Err> {
241 let p = match s {
242 "default" => {
243 None
246 }
247 _ => Some(PlatformPath::from_str(s)?),
248 };
249 Ok(Self(p))
250 }
251}
252
253impl<D> From<PathBuf> for MaybePlatformPath<D> {
254 fn from(path: PathBuf) -> Self {
255 Self(Some(PlatformPath(path, std::marker::PhantomData)))
256 }
257}
258
259#[derive(Clone, Debug, PartialEq, Eq)]
270pub struct ChainPath<D>(PlatformPath<D>, Chain, DatadirArgs);
271
272impl<D> ChainPath<D> {
273 pub const fn new(path: PlatformPath<D>, chain: Chain, datadir_args: DatadirArgs) -> Self {
275 Self(path, chain, datadir_args)
276 }
277
278 pub fn data_dir(&self) -> &Path {
282 self.0.as_ref()
283 }
284
285 pub fn db(&self) -> PathBuf {
289 self.data_dir().join("db")
290 }
291
292 pub fn static_files(&self) -> PathBuf {
296 let datadir_args = &self.2;
297 if let Some(static_files_path) = &datadir_args.static_files_path {
298 static_files_path.clone()
299 } else {
300 self.data_dir().join("static_files")
301 }
302 }
303
304 pub fn p2p_secret(&self) -> PathBuf {
308 self.data_dir().join("discovery-secret")
309 }
310
311 pub fn known_peers(&self) -> PathBuf {
315 self.data_dir().join("known-peers.json")
316 }
317
318 pub fn blobstore(&self) -> PathBuf {
323 self.data_dir().join("blobstore")
324 }
325
326 pub fn txpool_transactions(&self) -> PathBuf {
330 self.data_dir().join("txpool-transactions-backup.rlp")
331 }
332
333 pub fn config(&self) -> PathBuf {
337 self.data_dir().join("reth.toml")
338 }
339
340 pub fn jwt(&self) -> PathBuf {
344 self.data_dir().join("jwt.hex")
345 }
346
347 pub fn invalid_block_hooks(&self) -> PathBuf {
351 self.data_dir().join("invalid_block_hooks")
352 }
353
354 pub fn exex_wal(&self) -> PathBuf {
356 self.data_dir().join("exex/wal")
357 }
358}
359
360impl<D> AsRef<Path> for ChainPath<D> {
361 fn as_ref(&self) -> &Path {
362 self.0.as_ref()
363 }
364}
365
366impl<D> Display for ChainPath<D> {
367 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
368 write!(f, "{}", self.0)
369 }
370}
371
372impl<D> From<ChainPath<D>> for PathBuf {
373 fn from(value: ChainPath<D>) -> Self {
374 value.0.into()
375 }
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381
382 #[test]
383 fn test_maybe_data_dir_path() {
384 let path = MaybePlatformPath::<DataDirPath>::default();
385 let path = path.unwrap_or_chain_default(Chain::mainnet(), DatadirArgs::default());
386 assert!(path.as_ref().ends_with("reth/mainnet"), "{path:?}");
387
388 let db_path = path.db();
389 assert!(db_path.ends_with("reth/mainnet/db"), "{db_path:?}");
390
391 let path = MaybePlatformPath::<DataDirPath>::from_str("my/path/to/datadir").unwrap();
392 let path = path.unwrap_or_chain_default(Chain::mainnet(), DatadirArgs::default());
393 assert!(path.as_ref().ends_with("my/path/to/datadir"), "{path:?}");
394 }
395
396 #[test]
397 fn test_maybe_testnet_datadir_path() {
398 let path = MaybePlatformPath::<DataDirPath>::default();
399 let path = path.unwrap_or_chain_default(Chain::holesky(), DatadirArgs::default());
400 assert!(path.as_ref().ends_with("reth/holesky"), "{path:?}");
401
402 let path = MaybePlatformPath::<DataDirPath>::default();
403 let path = path.unwrap_or_chain_default(Chain::sepolia(), DatadirArgs::default());
404 assert!(path.as_ref().ends_with("reth/sepolia"), "{path:?}");
405 }
406}