diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..72eddf6
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,100 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.182"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
+
+[[package]]
+name = "linkdedup"
+version = "0.1.0"
+dependencies = [
+ "pathdiff",
+ "sha2",
+]
+
+[[package]]
+name = "pathdiff"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "typenum"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3d40618
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "linkdedup"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+sha2 = "0.10.9"
+pathdiff = "0.2.3"
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..53d505b
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,74 @@
+use pathdiff::diff_paths;
+use sha2::{Digest, Sha256};
+use std::fs::File;
+use std::io::Read;
+use std::path::{Path, PathBuf};
+
+fn get_hash(fname: &str) -> Vec<u8> {
+ let result1 = File::open(fname);
+ if result1.is_err() {
+ return vec![];
+ }
+ let mut result = result1.unwrap();
+ let mut hasher = Sha256::new();
+ loop {
+ let mut bif = [0; 1024];
+ let ur = result.read(&mut bif);
+ if ur.is_err() {
+ return vec![];
+ }
+ hasher.update(&bif);
+ if ur.unwrap() < 1024 {
+ break;
+ }
+ }
+
+ hasher.finalize().to_vec()
+}
+
+fn find_files(path: &Path) -> Vec<PathBuf> {
+ let mut ret = vec![];
+ let fss = std::fs::read_dir(path).unwrap();
+ for fs in fss {
+ let fs = fs.unwrap();
+ if fs.file_type().unwrap().is_file() {
+ ret.push(fs.path());
+ } else if fs.file_type().unwrap().is_dir() {
+ let mut p = PathBuf::from(path);
+ p.push(fs.file_name().to_str().unwrap());
+ ret.extend(find_files(p.as_path()));
+ }
+ }
+ ret
+}
+
+fn linkdup(path: &Path) {
+ let mut fhtable = vec![];
+ for z in find_files(path) {
+ let pth = z;
+ let hsh = get_hash(pth.to_str().unwrap());
+ fhtable.push((pth.to_str().unwrap().to_string(), hsh));
+ }
+ fhtable.sort_by(|x, y| Ord::cmp(&x.0, &y.0));
+ let mut fable: Vec<(String, Vec<u8>)> = vec![];
+ for f in fhtable {
+ let prstr = fable
+ .iter()
+ .filter_map(|x| if x.1 == f.1 { Some(x.0.clone()) } else { None })
+ .next();
+ if prstr.is_some() {
+ // let f2m = std::fs::metadata(f.0.clone()).unwrap();
+ std::fs::remove_file(f.0.clone()).unwrap();
+ let mut zz = PathBuf::from(f.0.clone());
+ zz.pop();
+ std::os::unix::fs::symlink(diff_paths(prstr.unwrap(), zz).unwrap(), f.0.clone())
+ .unwrap();
+ } else {
+ fable.push(f);
+ }
+ }
+}
+
+fn main() {
+ linkdup(PathBuf::from(".").as_path());
+}
|