about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/database/file/mod.rs83
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?;
                 }
             }
         }