1use once_cell::sync::OnceCell;
2use std::{
3 borrow::Cow,
4 fs::FileType,
5 path::{Path, PathBuf},
6 sync::Arc,
7 time::SystemTime,
8};
9
10use crate::{description::DescriptionData, Error, RResult, Resolver};
11
12#[derive(Debug, Default, Clone, Copy)]
13pub struct EntryStat {
14 file_type: Option<FileType>,
16
17 modified: Option<SystemTime>,
19}
20
21impl EntryStat {
22 fn new(file_type: Option<FileType>, modified: Option<SystemTime>) -> Self {
23 Self {
24 file_type,
25 modified,
26 }
27 }
28
29 pub fn file_type(&self) -> Option<FileType> {
31 self.file_type
32 }
33
34 pub fn modified(&self) -> Option<SystemTime> {
36 self.modified
37 }
38
39 fn stat(path: &Path) -> Self {
40 if let Ok(meta) = path.metadata() {
41 let modified = meta.modified().ok();
44 Self::new(Some(meta.file_type()), modified)
45 } else {
46 Self::new(None, None)
47 }
48 }
49}
50
51#[derive(Debug)]
52pub struct Entry {
53 parent: Option<Arc<Entry>>,
54 path: Box<Path>,
55 pkg_info: OnceCell<Option<Arc<DescriptionData>>>,
57 stat: OnceCell<EntryStat>,
58 symlink: OnceCell<Option<Box<Path>>>,
60 real: OnceCell<Box<Path>>,
63}
64
65impl Entry {
66 pub fn path(&self) -> &Path {
67 &self.path
68 }
69
70 pub fn parent(&self) -> Option<&Arc<Entry>> {
71 self.parent.as_ref()
72 }
73
74 pub fn pkg_info(&self, resolver: &Resolver) -> RResult<&Option<Arc<DescriptionData>>> {
75 self.pkg_info.get_or_try_init(|| {
76 let pkg_name = &resolver.options.description_file;
77 let path = self.path();
78 let is_pkg_suffix = path.ends_with(pkg_name);
79 if self.is_dir() || is_pkg_suffix {
80 let pkg_path = if is_pkg_suffix {
81 Cow::Borrowed(path)
82 } else {
83 Cow::Owned(path.join(pkg_name))
84 };
85 match resolver
86 .cache
87 .fs
88 .read_description_file(&pkg_path, EntryStat::default())
89 {
90 Ok(info) => {
91 return Ok(Some(info));
92 }
93 Err(error @ (Error::UnexpectedJson(_) | Error::UnexpectedValue(_))) => {
94 return Err(error);
96 }
97 Err(Error::Io(_)) => {
98 }
100 _ => unreachable!(),
101 };
102 }
103 if let Some(parent) = &self.parent() {
104 return parent.pkg_info(resolver).cloned();
105 }
106 Ok(None)
107 })
108 }
109
110 pub fn is_file(&self) -> bool {
111 self.cached_stat()
112 .file_type()
113 .map_or(false, |ft| ft.is_file())
114 }
115
116 pub fn is_dir(&self) -> bool {
117 self.cached_stat()
118 .file_type()
119 .map_or(false, |ft| ft.is_dir())
120 }
121
122 pub fn exists(&self) -> bool {
123 self.cached_stat().file_type().is_some()
124 }
125
126 pub fn cached_stat(&self) -> EntryStat {
127 *self.stat.get_or_init(|| EntryStat::stat(&self.path))
128 }
129
130 pub fn real(&self) -> Option<&Path> {
131 self.real.get().map(|p| &**p)
132 }
133
134 pub fn init_real(&self, path: Box<Path>) {
135 self.real.get_or_init(|| path);
136 }
137
138 pub fn symlink(&self) -> &Option<Box<Path>> {
141 self.symlink.get_or_init(|| {
142 debug_assert!(self.path.is_absolute());
143 if self.path.read_link().is_err() {
144 return None;
145 }
146 match dunce::canonicalize(&self.path) {
147 Ok(symlink_path) => Some(Box::from(symlink_path)),
148 Err(_) => None,
149 }
150 })
151 }
152}
153
154impl Resolver {
155 pub(super) fn load_entry(&self, path: &Path) -> Arc<Entry> {
156 if let Some(cached) = self.cache.entries.get(path) {
157 cached.clone()
158 } else {
159 let entry = Arc::new(self.load_entry_uncached(path));
160 self.cache
161 .entries
162 .entry(path.into())
163 .or_insert(entry.clone());
164 entry
165 }
166 }
167
168 fn load_entry_uncached(&self, path: &Path) -> Entry {
169 let parent = if let Some(parent) = path.parent() {
170 let entry = self.load_entry(parent);
171 Some(entry)
172 } else {
173 None
174 };
175 Entry {
176 parent,
177 path: path.into(),
178 pkg_info: OnceCell::default(),
179 stat: OnceCell::default(),
180 symlink: OnceCell::default(),
181 real: OnceCell::default(),
182 }
183 }
184
185 pub fn clear_entries(&self) {
187 self.cache.entries.clear();
188 }
189
190 #[must_use]
191 pub fn get_dependency_from_entry(&self) -> (Vec<PathBuf>, Vec<PathBuf>) {
192 todo!("get_dependency_from_entry")
193 }
194}
195
196#[test]
197#[ignore]
198fn dependency_test() {
199 let case_path = super::test_helper::p(vec!["full", "a"]);
200 let request = "package2";
201 let resolver = Resolver::new(Default::default());
202 resolver.resolve(&case_path, request).unwrap();
203 let (file, missing) = resolver.get_dependency_from_entry();
204 assert_eq!(file.len(), 3);
205 assert_eq!(missing.len(), 1);
206}