ruff_db/files/
file_root.rs1use path_slash::PathExt;
2use salsa::Durability;
3
4use crate::Db;
5use crate::system::{SystemPath, SystemPathBuf};
6
7#[salsa::input(debug, heap_size=ruff_memory_usage::heap_size)]
15pub struct FileRoot {
16 #[returns(deref)]
18 pub path: Box<SystemPath>,
19
20 #[returns(copy)]
22 pub kind_at_time_of_creation: FileRootKind,
23}
24
25impl FileRoot {
26 pub(crate) fn durability(self, db: &dyn Db) -> salsa::Durability {
27 self.kind_at_time_of_creation(db).durability()
28 }
29}
30
31#[derive(Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
32pub enum FileRootKind {
33 Project,
35
36 SearchPath,
38}
39
40impl FileRootKind {
41 const fn durability(self) -> Durability {
42 match self {
43 FileRootKind::Project => Durability::LOW,
44 FileRootKind::SearchPath => Durability::HIGH,
45 }
46 }
47}
48
49#[derive(Default)]
50pub(super) struct FileRoots {
51 by_path: matchit::Router<FileRoot>,
52}
53
54impl FileRoots {
55 pub(super) fn try_add(
59 &mut self,
60 db: &dyn Db,
61 path: SystemPathBuf,
62 kind: FileRootKind,
63 ) -> FileRoot {
64 let normalized_path = path.as_std_path().to_slash().unwrap();
66
67 if let Ok(existing) = self.by_path.at(&normalized_path) {
68 if existing.value.path(db) == &*path {
70 return *existing.value;
71 }
72 }
73
74 tracing::debug!("Adding new file root '{path}' of kind {kind:?}");
75
76 let mut route = normalized_path.replace('{', "{{").replace('}', "}}");
79
80 let root = FileRoot::builder(path.into(), kind)
82 .durability(Durability::NEVER_CHANGE)
83 .new(db);
84
85 self.by_path.insert(route.clone(), root).unwrap();
87
88 if !route.ends_with("/") {
90 route.push('/');
91 }
92 route.push_str("{*filepath}");
93
94 self.by_path.insert(route, root).unwrap();
95
96 root
97 }
98
99 pub(super) fn at(&self, path: &SystemPath) -> Option<FileRoot> {
101 let normalized_path = path.as_std_path().to_slash().unwrap();
103 let entry = self.by_path.at(&normalized_path).ok()?;
104 Some(*entry.value)
105 }
106}