diff --git a/README.md b/README.md
index fac6db9..c07a8e6 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ It tries to find a diff tool in the following order:
 - `meld`
 - `diff -r`
 
-It currently uses the `pip` executable found in path to download or build.
+It currently uses the `uv` or `pip` executable found in path to download or build.
 
 ## License
 
diff --git a/src/pip_wheel_diff/__main__.py b/src/pip_wheel_diff/__main__.py
index d6b9388..1245e65 100644
--- a/src/pip_wheel_diff/__main__.py
+++ b/src/pip_wheel_diff/__main__.py
@@ -2,55 +2,41 @@
 #
 # SPDX-License-Identifier: MIT
 
-import shutil
 import subprocess
 import sys
 import tempfile
-import zipfile
 from pathlib import Path
 
+from .clean import clean_unpacked_wheel
+from .diff_tool import diff_tool
+from .pip import has_pip, obtain_with_pip
+from .uv import has_uv, obtain_with_uv
 
-def _build_wheel(spec: str, path: Path) -> None:
-    with tempfile.TemporaryDirectory() as tmpdir:
-        cmd = ["pip", "wheel", "--use-pep517", "--no-deps", spec, "--wheel-dir", tmpdir]
-        subprocess.run(cmd, check=True)  # noqa: S603
-        wheelfile = next(Path(tmpdir).glob("*.whl"))
-        zipfile.ZipFile(wheelfile).extractall(path)
+if has_uv():
+    obtain = obtain_with_uv
+elif has_pip():
+    obtain = obtain_with_pip
+else:
+    msg = "Nor uv, nor pip were found, please install one of them."
+    raise SystemExit(msg)
 
 
-def _clean_unpacked_wheel(path: Path) -> None:
-    distinfo_path = next(path.glob("*.dist-info"))
-    # remove RECORD file, it's not useful for diffing
-    distinfo_path.joinpath("RECORD").unlink()
-    # rename dist-info to name without version for comparability
-    name = distinfo_path.stem.split("-")[0]
-    distinfo_path.rename(path / f"{name}.dist-info")
-
-
-def _diff_tool(v1path: Path, v2path: Path):
-    if shutil.which("meld"):
-        cmd = ["meld", v1path, v2path]
-    else:
-        cmd = ["diff", "-r", v1path, v2path]
-    subprocess.run(cmd, check=True)  # noqa: S603
-
-
-def main(spec1: str, spec2: str):
+def main(spec1: str, spec2: str) -> int:
     try:
         with tempfile.TemporaryDirectory() as tmpdir:
             tmppath = Path(tmpdir)
             # build 1
             v1path = tmppath / "v1"
             v1path.mkdir()
-            _build_wheel(spec1, v1path)
-            _clean_unpacked_wheel(v1path)
+            obtain(spec1, v1path)
+            clean_unpacked_wheel(v1path)
             # build 2
             v2path = tmppath / "v2"
             v2path.mkdir()
-            _build_wheel(spec2, v2path)
-            _clean_unpacked_wheel(v2path)
+            obtain(spec2, v2path)
+            clean_unpacked_wheel(v2path)
             # diff
-            _diff_tool(v1path, v2path)
+            diff_tool(v1path, v2path)
     except subprocess.CalledProcessError as e:
         return e.returncode
     return 0
diff --git a/src/pip_wheel_diff/clean.py b/src/pip_wheel_diff/clean.py
new file mode 100644
index 0000000..5e6a59b
--- /dev/null
+++ b/src/pip_wheel_diff/clean.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2023-present Stéphane Bidoul <stephane.bidoul@gmail.com>
+#
+# SPDX-License-Identifier: MIT
+
+from pathlib import Path
+
+
+def clean_unpacked_wheel(path: Path) -> None:
+    distinfo_path = next(path.glob("*.dist-info"))
+    # remove metadata files that are not useful for diffing
+    distinfo_path.joinpath("RECORD").unlink(missing_ok=True)
+    distinfo_path.joinpath("direct_url.json").unlink(missing_ok=True)
+    # rename dist-info to name without version for comparability
+    name = distinfo_path.stem.split("-")[0]
+    distinfo_path.rename(path / f"{name}.dist-info")
diff --git a/src/pip_wheel_diff/diff_tool.py b/src/pip_wheel_diff/diff_tool.py
new file mode 100644
index 0000000..4bafdc7
--- /dev/null
+++ b/src/pip_wheel_diff/diff_tool.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2023-present Stéphane Bidoul <stephane.bidoul@gmail.com>
+#
+# SPDX-License-Identifier: MIT
+
+import shutil
+import subprocess
+from pathlib import Path
+
+
+def diff_tool(v1path: Path, v2path: Path):
+    if shutil.which("meld"):
+        cmd = ["meld", v1path, v2path]
+    else:
+        cmd = ["diff", "-r", v1path, v2path]
+    subprocess.run(cmd, check=True)  # noqa: S603
diff --git a/src/pip_wheel_diff/pip.py b/src/pip_wheel_diff/pip.py
new file mode 100644
index 0000000..d503ace
--- /dev/null
+++ b/src/pip_wheel_diff/pip.py
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2023-present Stéphane Bidoul <stephane.bidoul@gmail.com>
+#
+# SPDX-License-Identifier: MIT
+
+import shutil
+import subprocess
+import tempfile
+import zipfile
+from pathlib import Path
+
+
+def has_pip() -> bool:
+    return bool(shutil.which("pip"))
+
+
+def obtain_with_pip(spec: str, path: Path) -> None:
+    with tempfile.TemporaryDirectory() as tmpdir:
+        cmd = ["pip", "wheel", "--use-pep517", "--no-deps", spec, "--wheel-dir", tmpdir]
+        subprocess.run(cmd, check=True)  # noqa: S603
+        wheelfile = next(Path(tmpdir).glob("*.whl"))
+        zipfile.ZipFile(wheelfile).extractall(path)
diff --git a/src/pip_wheel_diff/uv.py b/src/pip_wheel_diff/uv.py
new file mode 100644
index 0000000..5db18be
--- /dev/null
+++ b/src/pip_wheel_diff/uv.py
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: 2024-present Stéphane Bidoul <stephane.bidoul@gmail.com>
+#
+# SPDX-License-Identifier: MIT
+
+import shutil
+import subprocess
+import tempfile
+from pathlib import Path
+
+from .clean import clean_unpacked_wheel
+from .diff_tool import diff_tool
+
+
+def has_uv() -> bool:
+    return bool(shutil.which("uv"))
+
+
+def obtain_with_uv(spec: str, path: Path) -> None:
+    cmd = ["uv", "pip", "install", "--no-deps", "--target", path, spec]
+    subprocess.run(cmd, check=True)  # noqa: S603