diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/database/file/mod.rs | 83 |
1 files changed, 78 insertions, 5 deletions
diff --git a/src/database/file/mod.rs b/src/database/file/mod.rs index d641779..a059d22 100644 --- a/src/database/file/mod.rs +++ b/src/database/file/mod.rs @@ -27,9 +27,69 @@ async fn get_lockable_file(file: File) -> RwLock<File> { spawn_blocking(move || RwLock::new(file)).await } +// Copied from https://stackoverflow.com/questions/39340924 +// This routine is adapted from the *old* Path's `path_relative_from` +// function, which works differently from the new `relative_from` function. +// In particular, this handles the case on unix where both paths are +// absolute but with only the root as the common directory. +fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> { + use std::path::Component; + + if path.is_absolute() != base.is_absolute() { + if path.is_absolute() { + Some(PathBuf::from(path)) + } else { + None + } + } else { + let mut ita = path.components(); + let mut itb = base.components(); + let mut comps: Vec<Component> = vec![]; + loop { + match (ita.next(), itb.next()) { + (None, None) => break, + (Some(a), None) => { + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + (None, _) => comps.push(Component::ParentDir), + (Some(a), Some(b)) if comps.is_empty() && a == b => (), + (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), + (Some(_), Some(b)) if b == Component::ParentDir => return None, + (Some(a), Some(_)) => { + comps.push(Component::ParentDir); + for _ in itb { + comps.push(Component::ParentDir); + } + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + } + } + Some(comps.iter().map(|c| c.as_os_str()).collect()) + } +} + +mod tests { + #[test] + fn test_relative_path_resolving() { + let path1 = std::path::Path::new("/home/vika/Projects/kittybox"); + let path2 = std::path::Path::new("/home/vika/Projects/nixpkgs"); + let relative_path = super::path_relative_from(path2, path1).unwrap(); + + assert_eq!(relative_path, std::path::Path::new("../nixpkgs")) + } +} + fn url_to_path(root: &Path, url: &str) -> PathBuf { + url_to_relative_path(url).to_path(root).to_path_buf() +} + +fn url_to_relative_path(url: &str) -> relative_path::RelativePathBuf { let url = http_types::Url::parse(url).expect("Couldn't parse a URL"); - let mut path: PathBuf = root.to_owned(); + let mut path = relative_path::RelativePathBuf::new(); path.push(url.origin().ascii_serialization() + &url.path().to_string() + ".json"); path @@ -197,14 +257,27 @@ impl Storage for FileStorage { .iter() .map(|i| i.as_str().unwrap()) { - // TODO consider using the symlink crate - // to promote cross-platform compat on Windows - // do we even need to support Windows?... if url != key && url.starts_with(user) { let link = url_to_path(&self.root_dir, url); debug!("Creating a symlink at {:?}", link); let orig = path.clone(); - spawn_blocking(move || { std::os::unix::fs::symlink(orig, link) }).await?; + spawn_blocking::<_, Result<()>>(move || { + // We're supposed to have a parent here. + let basedir = link.parent().ok_or(StorageError::new(ErrorKind::Backend, "Failed to calculate parent directory when creating a symlink"))?; + let relative = path_relative_from(&orig, &basedir).unwrap(); + println!("{:?} - {:?} = {:?}", &orig, &basedir, &relative); + println!("Created a symlink at {:?}", &link); + let symlink_result; + #[cfg(unix)] + { symlink_result = std::os::unix::fs::symlink(relative, link); } + // Wow it even supports windows. Not sure if I need it to run on Windows but oh well + #[cfg(windows)] + { symlink_result = std::os::windows::fs::symlink_file(relative, link); } + match symlink_result { + Ok(()) => Ok(()), + Err(e) => Err(e.into()) + } + }).await?; } } } |