objectpath.rs
1 use std::path::{Path, PathBuf}; 2 use std::ffi::OsStr; 3 use std::sync::{Arc, Weak}; 4 use std::sync::atomic::{self, AtomicBool}; 5 use std::io; 6 7 use parking_lot::Mutex; 8 use derivative::Derivative; 9 10 #[allow(unused_imports)] 11 use crate::{debug, error, info, trace, warn}; 12 use crate::{Dir, Gatherer, GathererInner, InternedName}; 13 14 /// Space efficient storage of paths. Instead storing full path-names it stores only interned 15 /// strings of the actual object names and a reference to its parent. ObjectPaths are reference counted. 16 #[derive(Derivative)] 17 #[derivative(Hash, PartialOrd, PartialEq, Ord, Eq)] 18 pub struct ObjectPath(Arc<ObjectPathInner>); 19 20 impl ObjectPath { 21 /// Creates a new ObjectPath without a parent. 22 // pub fn new<P: AsRef<Path>>(path: P, gatherer: &Gatherer) -> ObjectPath { 23 pub fn new(path: impl AsRef<Path>, gatherer: &Gatherer) -> ObjectPath { 24 ObjectPath(Arc::new(ObjectPathInner { 25 parent: None, 26 name: gatherer.name_interning(path.as_ref().as_os_str()), 27 dir: Mutex::new(None), 28 gatherer: gatherer.downgrade(), 29 watched: AtomicBool::new(false), 30 })) 31 } 32 33 /// Creates a new ObjectPath without a parent and associated gatherer. 34 pub fn without_gatherer<P: AsRef<Path>>(path: P) -> ObjectPath { 35 ObjectPath(Arc::new(ObjectPathInner { 36 parent: None, 37 name: InternedName::new(path.as_ref().as_os_str()), 38 dir: Mutex::new(None), 39 gatherer: Weak::new(), 40 watched: AtomicBool::new(false), 41 })) 42 } 43 44 /// Creates a new ObjectPath as sub-object to some existing ObjectPath object. 45 #[must_use] 46 pub fn sub_object<P: AsRef<Path>>(&self, name: P, gatherer: &Gatherer) -> ObjectPath { 47 ObjectPath(Arc::new(ObjectPathInner { 48 parent: Some(self.clone()), 49 name: gatherer.name_interning(name.as_ref().as_os_str()), 50 dir: Mutex::new(None), 51 gatherer: gatherer.downgrade(), 52 watched: AtomicBool::new(false), 53 })) 54 } 55 56 /// Creates a new ObjectPath as sub-object to some existing ObjectPath object without 57 /// associated gatherer. 58 #[must_use] 59 pub fn sub_object_without_gatherer<P: AsRef<Path>>(&self, name: P) -> ObjectPath { 60 ObjectPath(Arc::new(ObjectPathInner { 61 parent: Some(self.clone()), 62 name: InternedName::new(name.as_ref().as_os_str()), 63 dir: Mutex::new(None), 64 gatherer: Weak::new(), 65 watched: AtomicBool::new(false), 66 })) 67 } 68 69 fn pathbuf_push_parents(&self, target: &mut PathBuf, len: usize) { 70 if let Some(parent) = &self.0.parent { 71 parent.pathbuf_push_parents( 72 target, 73 len + self.0.name.len() + 1, // delimiter char 74 ) 75 } else { 76 target.reserve(len + self.0.name.len()); 77 }; 78 target.push(&*self.0.name); 79 } 80 81 /// Writes the full ObjectPath including all parents to the given PathBuf. 82 pub fn write_pathbuf<'a>(&self, target: &'a mut PathBuf) -> &'a PathBuf { 83 target.clear(); 84 self.pathbuf_push_parents(target, 1 /* for root delimiter */); 85 target 86 } 87 88 /// Create a new PathBuf from the given ObjectPath. 89 pub fn to_pathbuf(&self) -> PathBuf { 90 // TODO: iterative impl 91 let mut target = PathBuf::new(); 92 self.pathbuf_push_parents(&mut target, 1 /* for root delimiter */); 93 target 94 } 95 96 // Returns path length in bytes including delimiters. 97 // pub fn len(&self) -> usize { 98 // 99 // } 100 101 /// Returns the number of components in the path. 102 pub fn depth(&self) -> u16 { 103 let mut counter = 1u16; 104 let mut itr = &self.0; 105 while let Some(parent) = &itr.parent { 106 itr = &parent.0; 107 counter += 1; 108 } 109 counter 110 } 111 112 /// Returns an reference to the name of the object, without any preceding path components. 113 pub fn name(&self) -> &OsStr { 114 &self.0.name 115 } 116 117 /// Return the metadata of an objectpath 118 pub fn metadata(&self) -> std::io::Result<crate::openat::Metadata> { 119 // FIXME: use parents dir handle if available 120 let parent = if let Some(parent) = &self.0.parent { 121 parent.to_pathbuf() 122 } else { 123 PathBuf::from(if Path::new(&*self.0.name).is_absolute() { 124 std::path::Component::RootDir.as_os_str() 125 } else { 126 std::path::Component::CurDir.as_os_str() 127 }) 128 }; 129 130 crate::openat::Dir::open(&parent)?.metadata(&*self.0.name) 131 } 132 133 /// Returns the number of strong references pointing to this object 134 pub fn strong_count(&self) -> usize { 135 Arc::strong_count(&self.0) 136 } 137 138 /// Sets the notification state on this ObjectPath. When true and all processing handles get 139 /// dropped a notification is issued. Watching an ObjectPath also retains its dir handle. 140 pub fn watch(&self, watch: bool) { 141 self.0.watched.store(watch, atomic::Ordering::Relaxed); 142 } 143 144 /// Queries the notification state on this ObjectPath. When true and all processing handles get 145 /// dropped a notification is issued. 146 pub fn is_watched(&self) -> bool { 147 self.0.watched.load(atomic::Ordering::Relaxed) 148 } 149 150 /// Gets a dir handle for this object. 151 pub fn dir(&self) -> io::Result<Arc<Dir>> { 152 let mut locked_dir = self.0.dir.lock(); 153 154 Ok(match locked_dir.as_ref().map(Weak::upgrade) { 155 Some(Some(arc)) => { 156 trace!("OPEN reuse handle {:?}", self); 157 arc 158 } 159 _ => { 160 let dir_arc = Arc::new( 161 match self 162 .parent() 163 .map(|parent| parent.0.dir.lock().as_ref().map(Weak::upgrade)) 164 { 165 Some(Some(Some(parent_handle))) => { 166 trace!("OPEN subdir {:?}", self); 167 parent_handle.sub_dir(self.name())? 168 } 169 _ => { 170 trace!("OPEN new {:?}", self); 171 Dir::open(&self.to_pathbuf())? 172 } 173 }, 174 ); 175 *locked_dir = Some(Arc::downgrade(&dir_arc)); 176 177 dir_arc 178 } 179 }) 180 } 181 182 /// Returns an Arc handle to the Gatherer if available 183 pub fn gatherer(&self) -> Option<Gatherer> { 184 Gatherer::upgrade(&self.0.gatherer) 185 } 186 187 /// Returns the parent if available 188 pub fn parent(&self) -> Option<&ObjectPath> { 189 self.0.parent.as_ref() 190 } 191 } 192 193 impl Clone for ObjectPath { 194 fn clone(&self) -> Self { 195 ObjectPath(self.0.clone()) 196 } 197 } 198 199 impl Drop for ObjectPath { 200 fn drop(&mut self) { 201 // Ordering::Relaxed suffices here because the last reference is always the gatherer 202 // itself (which starts with two references). The gatherer will never increase the 203 // refcount when it becomes dropped to one. Disable watching, so that notify() can 204 // clone the path without retriggering the notifier. 2 because this is the refcount 205 // before this drop happend. 206 if self.strong_count() <= 2 && self.0.watched.swap(false, atomic::Ordering::Relaxed) { 207 if let Some(gatherer) = self.gatherer() { 208 gatherer.notify_path_dropped(self.clone()); 209 } 210 } 211 } 212 } 213 214 #[derive(Derivative)] 215 #[derivative(Hash, PartialOrd, PartialEq, Ord, Eq)] 216 pub struct ObjectPathInner { 217 parent: Option<ObjectPath>, 218 name: InternedName, 219 220 #[derivative( 221 Hash = "ignore", 222 PartialEq = "ignore", 223 PartialOrd = "ignore", 224 Ord = "ignore" 225 )] 226 dir: Mutex<Option<Weak<Dir>>>, 227 228 #[derivative( 229 Hash = "ignore", 230 PartialEq = "ignore", 231 PartialOrd = "ignore", 232 Ord = "ignore" 233 )] 234 gatherer: Weak<GathererInner>, 235 236 #[derivative( 237 Hash = "ignore", 238 PartialEq = "ignore", 239 PartialOrd = "ignore", 240 Ord = "ignore" 241 )] 242 watched: AtomicBool, 243 } 244 245 use std::fmt; 246 impl fmt::Debug for ObjectPath { 247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 248 write!(f, "{:?}", self.to_pathbuf()) 249 } 250 } 251 252 #[cfg(test)] 253 mod test { 254 use std::path::PathBuf; 255 use std::ffi::OsStr; 256 257 #[allow(unused_imports)] 258 use crate::{debug, error, info, trace, warn}; 259 use crate::InternedName; 260 use super::ObjectPath; 261 262 #[test] 263 fn smoke() { 264 crate::test::init_env_logging(); 265 assert_eq!( 266 ObjectPath::without_gatherer(".").to_pathbuf(), 267 PathBuf::from(".") 268 ); 269 } 270 271 #[test] 272 fn path_subobject() { 273 crate::test::init_env_logging(); 274 use std::ffi::OsStr; 275 let p = ObjectPath::without_gatherer("."); 276 let mut pathbuf = PathBuf::new(); 277 assert_eq!( 278 p.sub_object_without_gatherer(InternedName::new(OsStr::new("foo"))) 279 .write_pathbuf(&mut pathbuf), 280 &PathBuf::from("./foo") 281 ); 282 } 283 284 #[test] 285 fn path_ordering() { 286 crate::test::init_env_logging(); 287 let foo = ObjectPath::without_gatherer("foo"); 288 let bar = ObjectPath::without_gatherer("bar"); 289 assert!(bar < foo); 290 291 let bar2 = ObjectPath::without_gatherer("bar"); 292 assert!(bar == bar2); 293 294 let foobar = foo.sub_object_without_gatherer(InternedName::new(OsStr::new("bar"))); 295 let barfoo = bar.sub_object_without_gatherer(InternedName::new(OsStr::new("foo"))); 296 assert!(barfoo < foobar); 297 } 298 299 #[test] 300 fn metadata() { 301 crate::test::init_env_logging(); 302 let cargo = ObjectPath::without_gatherer("Cargo.toml"); 303 assert!(cargo.metadata().is_ok()); 304 } 305 }