From ba37eafd179486f76db9a87978df9d16656ab493 Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Mon, 23 Oct 2023 06:49:05 +0200
Subject: [PATCH] MISC: Depends on less crates + add emails to authors when
 known + MRCV=1.70

---
 Cargo.lock                       |   7 --
 Cargo.toml                       |  37 +++-----
 amadeus/Cargo.toml               |   1 +
 kurisu_api/Cargo.toml            |   1 +
 lektor_lib/Cargo.toml            |   1 +
 lektor_mpris/Cargo.toml          |   1 +
 lektor_nkdb/Cargo.toml           |   1 +
 lektor_payloads/Cargo.toml       |   2 +
 lektor_procmacros/Cargo.toml     |   2 +-
 lektor_repo/Cargo.toml           |   1 +
 lektor_utils/Cargo.toml          |   5 +-
 lektor_utils/src/lib.rs          |   5 +-
 lektor_utils/src/open/haiku.rs   |   4 +-
 lektor_utils/src/open/ios.rs     |   4 +-
 lektor_utils/src/open/macos.rs   |   4 +-
 lektor_utils/src/open/mod.rs     |  72 +++++-----------
 lektor_utils/src/open/redox.rs   |   4 +-
 lektor_utils/src/open/unix.rs    |  37 ++++----
 lektor_utils/src/open/windows.rs |   6 +-
 lektor_utils/src/pathdiff.rs     | 144 +++++++++++++++++++++++++++++++
 lektord/Cargo.toml               |   1 +
 lkt/Cargo.toml                   |   1 +
 22 files changed, 222 insertions(+), 119 deletions(-)
 create mode 100644 lektor_utils/src/pathdiff.rs

diff --git a/Cargo.lock b/Cargo.lock
index a6801f35..930ecd33 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1890,7 +1890,6 @@ dependencies = [
  "dirs",
  "libc",
  "log",
- "pathdiff",
  "serde",
  "serde_json",
  "tokio",
@@ -2553,12 +2552,6 @@ dependencies = [
  "windows-targets 0.48.5",
 ]
 
-[[package]]
-name = "pathdiff"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
-
 [[package]]
 name = "percent-encoding"
 version = "2.3.0"
diff --git a/Cargo.toml b/Cargo.toml
index 43d99fff..0951ca66 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,36 +1,26 @@
 [workspace]
 resolver = "2"
 members = [
-    # Lektord
-    "lektord",
-    "lektor_nkdb",
-    "lektor_repo",
-
-    # Common things
-    "kurisu_api",
-    "lektor_lib",
-    "lektor_utils",
-    "lektor_mpris",
-    "lektor_payloads",
-    "lektor_procmacros",
-
-    # Clients
-    "amadeus",
-    "lkt",
+    "amadeus",    # GUI Client
+    "lkt",        # CLI Client
+    "lektord",    # Lektord
+    "kurisu_api", # The Kurisu API
+    "lektor_*",   # Common things
 ]
 
 [workspace.package]
 edition = "2021"
 authors = [
     "Maël MARTIN <mael.martin@protonmail.com>",
-    "Louis GOYARD",
-    "Loïc ALLEGRE",
-    "Kevin COCCHI",
-    "Félix GOUEDARD",
-    "Hubert HIRTZ",
-    "Étienne BRATEAU",
-    "Tristan DEROUET",
+    "Louis GOYARD <elliu@hashi.re>",
+    "Loïc ALLEGRE <lallegre26@gmail.com>",
+    "Kevin COCCHI <salixor@pm.me>",
+    "Félix GOUEDARD <felix.gouedard@ensiie.fr>",
+    "Hubert HIRTZ <hubert.hirtz@laposte.net>",
+    "Étienne BRATEAU <etienne.brateau@gmail.com>",
+    "Tristan DEROUET <tristan.derouet@gmail.com>",
 ]
+rust-version = "1.70"
 version = "3.0.1"
 license = "MIT"
 
@@ -55,7 +45,6 @@ regex = { version = "1.9", default-features = false, features = [
 ] }
 url = { version = "2", default-features = false }
 rand = "*"
-libc = { version = "0.2", default-features = false }
 zbus = { version = "3", default-features = false, features = ["tokio"] }
 chrono = { version = "0.4", default-features = false, features = ["clock"] }
 sha256 = { version = "1", default-features = false, features = ["async"] }
diff --git a/amadeus/Cargo.toml b/amadeus/Cargo.toml
index 27249060..e3bc99b9 100644
--- a/amadeus/Cargo.toml
+++ b/amadeus/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
 description = "Amadeus-RS, graphical interface for lektord"
 
 [dependencies]
diff --git a/kurisu_api/Cargo.toml b/kurisu_api/Cargo.toml
index 14fa321d..b2d25cc3 100644
--- a/kurisu_api/Cargo.toml
+++ b/kurisu_api/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
 description = "Crate used to deserialize what Kurisu returns"
 
 [lib]
diff --git a/lektor_lib/Cargo.toml b/lektor_lib/Cargo.toml
index ecf79f7d..190cd320 100644
--- a/lektor_lib/Cargo.toml
+++ b/lektor_lib/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
 description = "Client library for lektord, used to factorize the code between lkt and amadeus"
 
 [dependencies]
diff --git a/lektor_mpris/Cargo.toml b/lektor_mpris/Cargo.toml
index 5faa2c30..2418277d 100644
--- a/lektor_mpris/Cargo.toml
+++ b/lektor_mpris/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
 description = "Implement the mpris spec, designed to be plugable into lektord or amadeus"
 
 [dependencies]
diff --git a/lektor_nkdb/Cargo.toml b/lektor_nkdb/Cargo.toml
index c6aedf35..55417008 100644
--- a/lektor_nkdb/Cargo.toml
+++ b/lektor_nkdb/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
 description = "New database implementation for lektord (New Kara DataBase)"
 
 [dependencies]
diff --git a/lektor_payloads/Cargo.toml b/lektor_payloads/Cargo.toml
index 9a935095..315a1e2e 100644
--- a/lektor_payloads/Cargo.toml
+++ b/lektor_payloads/Cargo.toml
@@ -4,6 +4,8 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
+description = "The payloads of the requests used to exchange messages between lektord and its client, this is a utility crate, you can build your own thing because they are all serialized to json under the hood"
 
 [dependencies]
 serde.workspace = true
diff --git a/lektor_procmacros/Cargo.toml b/lektor_procmacros/Cargo.toml
index e319eb76..11c1c252 100644
--- a/lektor_procmacros/Cargo.toml
+++ b/lektor_procmacros/Cargo.toml
@@ -1,10 +1,10 @@
-# Add https://crates.io/crates/derivative
 [package]
 name = "lektor_procmacros"
 authors.workspace = true
 license.workspace = true
 edition.workspace = true
 version.workspace = true
+rust-version.workspace = true
 description = "A collection of procedural macros for the workspace"
 
 [lib]
diff --git a/lektor_repo/Cargo.toml b/lektor_repo/Cargo.toml
index f32c7d41..2a02274e 100644
--- a/lektor_repo/Cargo.toml
+++ b/lektor_repo/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
 description = "Structure used to update the lektord's database from a repo, can be kurisu or something else - only kurisu for now btw"
 
 [lib]
diff --git a/lektor_utils/Cargo.toml b/lektor_utils/Cargo.toml
index ef4f59a2..e3d8b453 100644
--- a/lektor_utils/Cargo.toml
+++ b/lektor_utils/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
 description = "Utilities in common for all the crates in the workspace"
 
 [lib]
@@ -21,9 +22,5 @@ chrono.workspace = true
 serde_json.workspace = true
 
 # Some specific things for the open module.
-
-[target.'cfg(all(unix, not(macos)))'.dependencies]
-pathdiff = "0.2.0"
-
 [target."cfg(unix)".dependencies]
 libc = "0.2"
diff --git a/lektor_utils/src/lib.rs b/lektor_utils/src/lib.rs
index 05ec6081..ce8d14b2 100644
--- a/lektor_utils/src/lib.rs
+++ b/lektor_utils/src/lib.rs
@@ -6,7 +6,7 @@ mod base64;
 mod iterator;
 mod macros;
 
-// Import the is_wsl and is_docker code here...
+/// Import the is_wsl and is_docker code here...
 #[cfg(any(
     target_os = "linux",
     target_os = "android",
@@ -19,6 +19,9 @@ mod macros;
 ))]
 pub mod is;
 
+/// Pathdiff to handle some errors with WSL...
+pub mod pathdiff;
+
 pub mod config;
 pub mod log;
 pub mod logger;
diff --git a/lektor_utils/src/open/haiku.rs b/lektor_utils/src/open/haiku.rs
index 2fbcd138..1eb43ad8 100644
--- a/lektor_utils/src/open/haiku.rs
+++ b/lektor_utils/src/open/haiku.rs
@@ -1,12 +1,12 @@
 use std::{ffi::OsStr, process::Command};
 
-pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
+pub fn commands(path: impl AsRef<OsStr>) -> Vec<Command> {
     let mut cmd = Command::new("/bin/open");
     cmd.arg(path.as_ref());
     vec![cmd]
 }
 
-pub fn with_command<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Command {
+pub fn with_command(path: impl AsRef<OsStr>, app: impl Into<String>) -> Command {
     let mut cmd = Command::new(app.into());
     cmd.arg(path.as_ref());
     cmd
diff --git a/lektor_utils/src/open/ios.rs b/lektor_utils/src/open/ios.rs
index e1d78f1d..d1fbdbe4 100644
--- a/lektor_utils/src/open/ios.rs
+++ b/lektor_utils/src/open/ios.rs
@@ -1,12 +1,12 @@
 use std::{ffi::OsStr, process::Command};
 
-pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
+pub fn commands(path: impl AsRef<OsStr>) -> Vec<Command> {
     let mut cmd = Command::new("uiopen");
     cmd.arg("--url").arg(path.as_ref());
     vec![cmd]
 }
 
-pub fn with_command<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Command {
+pub fn with_command(path: impl AsRef<OsStr>, app: impl Into<String>) -> Command {
     let mut cmd = Command::new("uiopen");
     cmd.arg("--url")
         .arg(path.as_ref())
diff --git a/lektor_utils/src/open/macos.rs b/lektor_utils/src/open/macos.rs
index 6033d64c..7c9a7aea 100644
--- a/lektor_utils/src/open/macos.rs
+++ b/lektor_utils/src/open/macos.rs
@@ -1,12 +1,12 @@
 use std::{ffi::OsStr, process::Command};
 
-pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
+pub fn commands(path: impl AsRef<OsStr>) -> Vec<Command> {
     let mut cmd = Command::new("/usr/bin/open");
     cmd.arg(path.as_ref());
     vec![cmd]
 }
 
-pub fn with_command<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Command {
+pub fn with_command(path: impl AsRef<OsStr>, app: impl Into<String>) -> Command {
     let mut cmd = Command::new("/usr/bin/open");
     cmd.arg(path.as_ref()).arg("-a").arg(app.into());
     cmd
diff --git a/lektor_utils/src/open/mod.rs b/lektor_utils/src/open/mod.rs
index f21897b8..20b9bdc0 100644
--- a/lektor_utils/src/open/mod.rs
+++ b/lektor_utils/src/open/mod.rs
@@ -66,20 +66,25 @@
 //! }
 //! ```
 
-#[cfg(target_os = "windows")]
-use windows as os;
+#[cfg(windows)]
+#[path = "windows.rs"]
+mod windows;
 
 #[cfg(target_os = "macos")]
-use macos as os;
+#[path = "macos.rs"]
+mod os;
 
 #[cfg(target_os = "ios")]
-use ios as os;
+#[path = "ios.rs"]
+mod os;
 
 #[cfg(target_os = "haiku")]
-use haiku as os;
+#[path = "haiku.rs"]
+mod os;
 
 #[cfg(target_os = "redox")]
-use redox as os;
+#[path = "redox.rs"]
+mod os;
 
 #[cfg(any(
     target_os = "linux",
@@ -91,7 +96,8 @@ use redox as os;
     target_os = "illumos",
     target_os = "solaris"
 ))]
-use unix as os;
+#[path = "unix.rs"]
+mod os;
 
 #[cfg(not(any(
     target_os = "linux",
@@ -108,7 +114,7 @@ use unix as os;
     target_os = "haiku",
     target_os = "redox"
 )))]
-compile_error!("open is not supported on this platform");
+compile_error!("unsupported on this platform");
 
 use std::{
     ffi::OsStr,
@@ -223,8 +229,8 @@ pub fn that_in_background(path: impl AsRef<OsStr>) -> thread::JoinHandle<io::Res
 /// straightforward error handling.
 ///
 /// See documentation of [`with()`] for more details.
-pub fn with_in_background<T: AsRef<OsStr>>(
-    path: T,
+pub fn with_in_background(
+    path: impl AsRef<OsStr>,
     app: impl Into<String>,
 ) -> thread::JoinHandle<io::Result<()>> {
     let path = path.as_ref().to_os_string();
@@ -240,9 +246,7 @@ pub fn that_detached(path: impl AsRef<OsStr>) -> io::Result<()> {
     let mut last_err = None;
     for mut cmd in commands(path) {
         match cmd.spawn_detached() {
-            Ok(_) => {
-                return Ok(());
-            }
+            Ok(_) => return Ok(()),
             Err(err) => last_err = Some(err),
         }
     }
@@ -254,9 +258,8 @@ pub fn that_detached(path: impl AsRef<OsStr>) -> io::Result<()> {
 /// straightforward error handling.
 ///
 /// See documentation of [`with()`] for more details.
-pub fn with_detached<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
-    let mut cmd = with_command(path, app);
-    cmd.spawn_detached()
+pub fn with_detached(path: impl AsRef<OsStr>, app: impl Into<String>) -> io::Result<()> {
+    with_command(path, app).spawn_detached()
 }
 
 trait IntoResult<T> {
@@ -292,26 +295,21 @@ impl CommandExt for Command {
     fn spawn_detached(&mut self) -> io::Result<()> {
         // This is pretty much lifted from the implementation in Alacritty:
         // https://github.com/alacritty/alacritty/blob/b9c886872d1202fc9302f68a0bedbb17daa35335/alacritty/src/daemon.rs
-
         self.stdin(Stdio::null())
             .stdout(Stdio::null())
             .stderr(Stdio::null());
-
         #[cfg(unix)]
         unsafe {
             use std::os::unix::process::CommandExt as _;
-
             self.pre_exec(move || {
                 match libc::fork() {
-                    -1 => return Err(io::Error::last_os_error()),
+                    x if x < 0 => return Err(io::Error::last_os_error()),
                     0 => (),
                     _ => libc::_exit(0),
                 }
-
-                if libc::setsid() == -1 {
+                if libc::setsid() < 0 {
                     return Err(io::Error::last_os_error());
                 }
-
                 Ok(())
             });
         }
@@ -322,34 +320,6 @@ impl CommandExt for Command {
             const CREATE_NO_WINDOW: u32 = 0x08000000;
             self.creation_flags(CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW);
         }
-
         self.spawn().map(|_| ())
     }
 }
-
-#[cfg(windows)]
-mod windows;
-
-#[cfg(target_os = "macos")]
-mod macos;
-
-#[cfg(target_os = "ios")]
-mod ios;
-
-#[cfg(target_os = "haiku")]
-mod haiku;
-
-#[cfg(target_os = "redox")]
-mod redox;
-
-#[cfg(any(
-    target_os = "linux",
-    target_os = "android",
-    target_os = "freebsd",
-    target_os = "dragonfly",
-    target_os = "netbsd",
-    target_os = "openbsd",
-    target_os = "illumos",
-    target_os = "solaris"
-))]
-mod unix;
diff --git a/lektor_utils/src/open/redox.rs b/lektor_utils/src/open/redox.rs
index 8fab3434..84609e5a 100644
--- a/lektor_utils/src/open/redox.rs
+++ b/lektor_utils/src/open/redox.rs
@@ -1,12 +1,12 @@
 use std::{ffi::OsStr, process::Command};
 
-pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
+pub fn commands(path: impl AsRef<OsStr>) -> Vec<Command> {
     let mut cmd = Command::new("/ui/bin/launcher");
     cmd.arg(path.as_ref());
     vec![cmd]
 }
 
-pub fn with_command<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Command {
+pub fn with_command(path: impl AsRef<OsStr>, app: impl Into<String>) -> Command {
     let mut cmd = Command::new(app.into());
     cmd.arg(path.as_ref());
     cmd
diff --git a/lektor_utils/src/open/unix.rs b/lektor_utils/src/open/unix.rs
index 7cb23eeb..3df5e3eb 100644
--- a/lektor_utils/src/open/unix.rs
+++ b/lektor_utils/src/open/unix.rs
@@ -1,11 +1,11 @@
 use std::{
     env,
     ffi::{OsStr, OsString},
-    path::{Path, PathBuf},
+    path::Path,
     process::Command,
 };
 
-pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
+pub fn commands(path: impl AsRef<OsStr>) -> Vec<Command> {
     let path = path.as_ref();
     let mut commands: Vec<(&str, Vec<&OsStr>)> = vec![];
 
@@ -37,23 +37,20 @@ pub fn with_command<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Command
     cmd
 }
 
-// Polyfill to workaround absolute path bug in wslu(wslview). In versions before
-// v3.1.1, wslview is unable to find absolute paths. `wsl_path` converts an
-// absolute path into a relative path starting from the current directory. If
-// the path is already a relative path or the conversion fails the original path
-// is returned.
-fn wsl_path<T: AsRef<OsStr>>(path: T) -> OsString {
-    fn path_relative_to_current_dir<T: AsRef<OsStr>>(path: T) -> Option<PathBuf> {
-        let path = Path::new(&path);
-        if path.is_relative() {
-            return None;
-        }
-
-        pathdiff::diff_paths(path, env::current_dir().ok()?)
-    }
-
-    match path_relative_to_current_dir(&path) {
-        None => OsString::from(&path),
-        Some(relative) => OsString::from(relative),
+/// Polyfill to workaround absolute path bug in wslu(wslview). In versions before v3.1.1, wslview
+/// is unable to find absolute paths. `wsl_path` converts an absolute path into a relative path
+/// starting from the current directory. If the path is already a relative path or the conversion
+/// fails the original path is returned.
+fn wsl_path(path_str: impl AsRef<OsStr>) -> OsString {
+    let path = Path::new(path_str.as_ref());
+    match env::current_dir().ok() {
+        Some(current) => match path
+            .is_relative()
+            .then(|| crate::pathdiff::diff_paths(path, current))
+        {
+            Some(Some(relative)) => OsString::from(relative),
+            _ => path_str.as_ref().to_owned(),
+        },
+        None => path_str.as_ref().to_owned(),
     }
 }
diff --git a/lektor_utils/src/open/windows.rs b/lektor_utils/src/open/windows.rs
index e44b1610..202aefbe 100644
--- a/lektor_utils/src/open/windows.rs
+++ b/lektor_utils/src/open/windows.rs
@@ -6,7 +6,7 @@ use std::{
 
 const CREATE_NO_WINDOW: u32 = 0x08000000;
 
-pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
+pub fn commands(path: impl AsRef<OsStr>) -> Vec<Command> {
     let mut cmd = Command::new("cmd");
     cmd.arg("/c")
         .arg("start")
@@ -16,7 +16,7 @@ pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
     vec![cmd]
 }
 
-pub fn with_command<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Command {
+pub fn with_command(path: impl AsRef<OsStr>, app: impl Into<String>) -> Command {
     let mut cmd = Command::new("cmd");
     cmd.arg("/c")
         .arg("start")
@@ -27,7 +27,7 @@ pub fn with_command<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Command
     cmd
 }
 
-fn wrap_in_quotes<T: AsRef<OsStr>>(path: T) -> OsString {
+fn wrap_in_quotes(path: impl AsRef<OsStr>) -> OsString {
     let mut result = OsString::from("\"");
     result.push(path);
     result.push("\"");
diff --git a/lektor_utils/src/pathdiff.rs b/lektor_utils/src/pathdiff.rs
new file mode 100644
index 00000000..b9a72acb
--- /dev/null
+++ b/lektor_utils/src/pathdiff.rs
@@ -0,0 +1,144 @@
+use std::path::*;
+
+/// Construct a relative path from a provided base directory path to the provided path.
+///
+/// ```rust
+/// use pathdiff::diff_paths;
+/// use std::path::*;
+///
+/// let baz = "/foo/bar/baz";
+/// let bar = "/foo/bar";
+/// let quux = "/foo/bar/quux";
+/// assert_eq!(diff_paths(bar, baz), Some("../".into()));
+/// assert_eq!(diff_paths(baz, bar), Some("baz".into()));
+/// assert_eq!(diff_paths(quux, baz), Some("../quux".into()));
+/// assert_eq!(diff_paths(baz, quux), Some("../baz".into()));
+/// assert_eq!(diff_paths(bar, quux), Some("../".into()));
+///
+/// assert_eq!(diff_paths(&baz, &bar.to_string()), Some("baz".into()));
+/// assert_eq!(diff_paths(Path::new(baz), Path::new(bar).to_path_buf()), Some("baz".into()));
+/// ```
+pub fn diff_paths<P, B>(path: P, base: B) -> Option<PathBuf>
+where
+    P: AsRef<Path>,
+    B: AsRef<Path>,
+{
+    let path = path.as_ref();
+    let base = base.as_ref();
+
+    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())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_absolute() {
+        #[cfg(windows)]
+        fn abs(path: &str) -> String {
+            format!("C:\\{}", path)
+        }
+
+        #[cfg(not(windows))]
+        fn abs(path: &str) -> String {
+            format!("/{}", path)
+        }
+
+        assert_diff_paths(&abs("foo"), &abs("bar"), Some("../foo"));
+        assert_diff_paths(&abs("foo"), "bar", Some(&abs("foo")));
+        assert_diff_paths("foo", &abs("bar"), None);
+        assert_diff_paths("foo", "bar", Some("../foo"));
+    }
+
+    #[test]
+    fn test_identity() {
+        assert_diff_paths(".", ".", Some(""));
+        assert_diff_paths("../foo", "../foo", Some(""));
+        assert_diff_paths("./foo", "./foo", Some(""));
+        assert_diff_paths("/foo", "/foo", Some(""));
+        assert_diff_paths("foo", "foo", Some(""));
+
+        assert_diff_paths("../foo/bar/baz", "../foo/bar/baz", Some("".into()));
+        assert_diff_paths("foo/bar/baz", "foo/bar/baz", Some(""));
+    }
+
+    #[test]
+    fn test_subset() {
+        assert_diff_paths("foo", "fo", Some("../foo"));
+        assert_diff_paths("fo", "foo", Some("../fo"));
+    }
+
+    #[test]
+    fn test_empty() {
+        assert_diff_paths("", "", Some(""));
+        assert_diff_paths("foo", "", Some("foo"));
+        assert_diff_paths("", "foo", Some(".."));
+    }
+
+    #[test]
+    fn test_relative() {
+        assert_diff_paths("../foo", "../bar", Some("../foo"));
+        assert_diff_paths("../foo", "../foo/bar/baz", Some("../.."));
+        assert_diff_paths("../foo/bar/baz", "../foo", Some("bar/baz"));
+
+        assert_diff_paths("foo/bar/baz", "foo", Some("bar/baz"));
+        assert_diff_paths("foo/bar/baz", "foo/bar", Some("baz"));
+        assert_diff_paths("foo/bar/baz", "foo/bar/baz", Some(""));
+        assert_diff_paths("foo/bar/baz", "foo/bar/baz/", Some(""));
+
+        assert_diff_paths("foo/bar/baz/", "foo", Some("bar/baz"));
+        assert_diff_paths("foo/bar/baz/", "foo/bar", Some("baz"));
+        assert_diff_paths("foo/bar/baz/", "foo/bar/baz", Some(""));
+        assert_diff_paths("foo/bar/baz/", "foo/bar/baz/", Some(""));
+
+        assert_diff_paths("foo/bar/baz", "foo/", Some("bar/baz"));
+        assert_diff_paths("foo/bar/baz", "foo/bar/", Some("baz"));
+        assert_diff_paths("foo/bar/baz", "foo/bar/baz", Some(""));
+    }
+
+    #[test]
+    fn test_current_directory() {
+        assert_diff_paths(".", "foo", Some("../."));
+        assert_diff_paths("foo", ".", Some("foo"));
+        assert_diff_paths("/foo", "/.", Some("foo"));
+    }
+
+    fn assert_diff_paths(path: &str, base: &str, expected: Option<&str>) {
+        assert_eq!(diff_paths(path, base), expected.map(|s| s.into()));
+    }
+}
diff --git a/lektord/Cargo.toml b/lektord/Cargo.toml
index 45650932..b22fcff0 100644
--- a/lektord/Cargo.toml
+++ b/lektord/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
 description = "The lektord daemon"
 
 [dependencies]
diff --git a/lkt/Cargo.toml b/lkt/Cargo.toml
index e6caa3cf..88681dfe 100644
--- a/lkt/Cargo.toml
+++ b/lkt/Cargo.toml
@@ -4,6 +4,7 @@ version.workspace = true
 edition.workspace = true
 authors.workspace = true
 license.workspace = true
+rust-version.workspace = true
 description = "Simple command line utility to interact with the lektord daemon"
 
 [dependencies]
-- 
GitLab