From d6069a4ba507c62da868f9e88a22de2839738f46 Mon Sep 17 00:00:00 2001
From: Alexander Weiss <alex@weissfam.de>
Date: Sat, 23 Nov 2024 13:43:49 +0100
Subject: [PATCH] chore: move webdavfs from rustic_core to rustic

---
 Cargo.lock                      | 259 ++++++++++++++++++-----
 Cargo.toml                      |   2 +
 src/commands/webdav.rs          |   6 +-
 src/commands/webdav/webdavfs.rs | 353 ++++++++++++++++++++++++++++++++
 4 files changed, 569 insertions(+), 51 deletions(-)
 create mode 100644 src/commands/webdav/webdavfs.rs

diff --git a/Cargo.lock b/Cargo.lock
index 7ffd2d837..f7800dcc4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -359,7 +359,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -390,6 +390,33 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d5b3469636cdf8543cceab175efca534471f36eee12fb8374aba00eb5e7e7f8a"
 
+[[package]]
+name = "aws-lc-rs"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe7c2840b66236045acd2607d5866e274380afd87ef99d6226e961e2cb47df45"
+dependencies = [
+ "aws-lc-sys",
+ "mirai-annotations",
+ "paste",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad3a619a9de81e1d7de1f1186dcba4506ed661a0e483d84410fdef0ee87b2f96"
+dependencies = [
+ "bindgen",
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+ "libc",
+ "paste",
+]
+
 [[package]]
 name = "backoff"
 version = "0.4.0"
@@ -463,6 +490,29 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "bindgen"
+version = "0.69.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
+dependencies = [
+ "bitflags 2.6.0",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.12.1",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash 1.1.0",
+ "shlex",
+ "syn 2.0.89",
+ "which",
+]
+
 [[package]]
 name = "binrw"
 version = "0.14.0"
@@ -561,9 +611,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.7.2"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
+checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
 
 [[package]]
 name = "bytesize"
@@ -595,7 +645,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -654,6 +704,15 @@ dependencies = [
  "shlex",
 ]
 
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
 [[package]]
 name = "cfg-if"
 version = "0.1.10"
@@ -697,6 +756,17 @@ dependencies = [
  "inout",
 ]
 
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
 [[package]]
 name = "clap"
 version = "4.5.20"
@@ -738,7 +808,7 @@ dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -747,6 +817,15 @@ version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
 
+[[package]]
+name = "cmake"
+version = "0.1.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "color-eyre"
 version = "0.6.3"
@@ -831,7 +910,7 @@ dependencies = [
  "proc-macro-error2",
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1068,7 +1147,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1092,7 +1171,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1103,7 +1182,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1208,7 +1287,7 @@ checksum = "64b697ac90ff296f0fc031ee5a61c7ac31fb9fff50e3fb32873b09223613fc0c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1228,7 +1307,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
  "unicode-xid",
 ]
 
@@ -1241,7 +1320,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1331,7 +1410,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1418,7 +1497,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1440,7 +1519,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -1687,7 +1766,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -2279,7 +2358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c"
 dependencies = [
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -2331,6 +2410,15 @@ version = "1.70.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
 
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itertools"
 version = "0.13.0"
@@ -2439,7 +2527,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "regex",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -2451,12 +2539,28 @@ dependencies = [
  "spin",
 ]
 
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
 [[package]]
 name = "libc"
 version = "0.2.159"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
 
+[[package]]
+name = "libloading"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
+dependencies = [
+ "cfg-if 1.0.0",
+ "windows-targets 0.52.6",
+]
+
 [[package]]
 name = "libm"
 version = "0.2.8"
@@ -2574,6 +2678,12 @@ dependencies = [
  "unicase",
 ]
 
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
 [[package]]
 name = "miniz_oxide"
 version = "0.7.4"
@@ -2605,6 +2715,12 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "mirai-annotations"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1"
+
 [[package]]
 name = "moka"
 version = "0.12.8"
@@ -2665,6 +2781,16 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
 
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
 [[package]]
 name = "nonzero_ext"
 version = "0.3.0"
@@ -2820,9 +2946,9 @@ dependencies = [
 
 [[package]]
 name = "opendal"
-version = "0.50.0"
+version = "0.50.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36e44fc43be9ffe18dad3e3ef9d61c1ae01991ee6f1c8c026978c35777a711bf"
+checksum = "cb28bb6c64e116ceaf8dd4e87099d3cfea4a58e85e62b104fef74c91afba0f44"
 dependencies = [
  "anyhow",
  "async-tls",
@@ -3116,7 +3242,7 @@ checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -3273,6 +3399,16 @@ dependencies = [
  "yansi",
 ]
 
+[[package]]
+name = "prettyplease"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.89",
+]
+
 [[package]]
 name = "proc-macro-crate"
 version = "3.2.0"
@@ -3301,14 +3437,14 @@ dependencies = [
  "proc-macro-error-attr2",
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.87"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
 dependencies = [
  "unicode-ident",
 ]
@@ -3401,7 +3537,7 @@ dependencies = [
  "pin-project-lite",
  "quinn-proto",
  "quinn-udp",
- "rustc-hash",
+ "rustc-hash 2.0.0",
  "rustls 0.23.14",
  "socket2",
  "thiserror",
@@ -3418,7 +3554,7 @@ dependencies = [
  "bytes",
  "rand",
  "ring",
- "rustc-hash",
+ "rustc-hash 2.0.0",
  "rustls 0.23.14",
  "slab",
  "thiserror",
@@ -3490,7 +3626,7 @@ dependencies = [
  "crossterm 0.28.1",
  "indoc",
  "instability",
- "itertools",
+ "itertools 0.13.0",
  "lru",
  "paste",
  "strum",
@@ -3600,9 +3736,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
 
 [[package]]
 name = "reqsign"
-version = "0.16.0"
+version = "0.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03dd4ba7c3901dd43e6b8c7446a760d45bc1ea4301002e1a6fa48f97c3a796fa"
+checksum = "eb0075a66c8bfbf4cc8b70dca166e722e1f55a3ea9250ecbb85f4d92a5f64149"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3742,7 +3878,7 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -3818,7 +3954,7 @@ dependencies = [
  "regex",
  "relative-path",
  "rustc_version",
- "syn 2.0.79",
+ "syn 2.0.89",
  "unicode-ident",
 ]
 
@@ -3848,6 +3984,12 @@ version = "0.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
 [[package]]
 name = "rustc-hash"
 version = "2.0.0"
@@ -3871,6 +4013,7 @@ dependencies = [
  "aho-corasick",
  "anyhow",
  "assert_cmd",
+ "bytes",
  "bytesize",
  "cached",
  "chrono",
@@ -3888,13 +4031,14 @@ dependencies = [
  "directories",
  "displaydoc",
  "fuse_mt",
+ "futures",
  "gethostname",
  "globset",
  "human-panic",
  "humantime",
  "indicatif",
  "insta",
- "itertools",
+ "itertools 0.13.0",
  "jemallocator-global",
  "libc",
  "log",
@@ -3943,7 +4087,7 @@ dependencies = [
  "displaydoc",
  "hex",
  "humantime",
- "itertools",
+ "itertools 0.13.0",
  "log",
  "opendal",
  "rand",
@@ -3995,7 +4139,7 @@ dependencies = [
  "humantime",
  "ignore",
  "integer-sqrt",
- "itertools",
+ "itertools 0.13.0",
  "log",
  "nix",
  "pariter",
@@ -4064,6 +4208,8 @@ version = "0.23.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8"
 dependencies = [
+ "aws-lc-rs",
+ "log",
  "once_cell",
  "ring",
  "rustls-pki-types",
@@ -4125,6 +4271,7 @@ version = "0.102.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
 dependencies = [
+ "aws-lc-rs",
  "ring",
  "rustls-pki-types",
  "untrusted",
@@ -4306,7 +4453,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -4369,7 +4516,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -4622,7 +4769,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -4633,9 +4780,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "suppaftp"
-version = "6.0.1"
+version = "6.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d5c3d37ce3092d7148494a30a0f5036f8722792a626b25662a87434d3683c33"
+checksum = "75de72048c936880d6a4dd40048e0e656652e56ecdd28ca13096783592c0782f"
 dependencies = [
  "async-std",
  "async-tls",
@@ -4645,7 +4792,7 @@ dependencies = [
  "lazy-regex",
  "log",
  "pin-project",
- "rustls 0.21.12",
+ "rustls 0.23.14",
  "thiserror",
 ]
 
@@ -4662,9 +4809,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.79"
+version = "2.0.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
+checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -4800,7 +4947,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -4915,7 +5062,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -5024,7 +5171,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -5173,7 +5320,7 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
 dependencies = [
- "itertools",
+ "itertools 0.13.0",
  "unicode-segmentation",
  "unicode-width 0.1.14",
 ]
@@ -5364,7 +5511,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
  "wasm-bindgen-shared",
 ]
 
@@ -5398,7 +5545,7 @@ checksum = "4c74f6e152a76a2ad448e223b0fc0b6b5747649c3d769cc6bf45737bf97d0ed6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -5476,6 +5623,18 @@ dependencies = [
  "rustls-pki-types",
 ]
 
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+]
+
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -5773,7 +5932,7 @@ checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
@@ -5784,7 +5943,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.79",
+ "syn 2.0.89",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index c25a13884..3e4977556 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -108,6 +108,8 @@ open = "5.3.0"
 self_update = { version = "0.39.0", default-features = false, optional = true, features = ["rustls", "archive-tar", "compression-flate2"] } # FIXME: Downgraded to 0.39.0 due to https://github.com/jaemk/self_update/issues/136
 tar = "0.4.42"
 toml = "0.8"
+bytes = "1.8.0"
+futures = "0.3.31"
 
 [dev-dependencies]
 abscissa_core = { version = "0.8.1", default-features = false, features = ["testing"] }
diff --git a/src/commands/webdav.rs b/src/commands/webdav.rs
index 5dfdc4ccd..90b79072c 100644
--- a/src/commands/webdav.rs
+++ b/src/commands/webdav.rs
@@ -13,6 +13,9 @@ use dav_server::{warp::dav_handler, DavHandler};
 use serde::{Deserialize, Serialize};
 
 use rustic_core::vfs::{FilePolicy, IdenticalSnapshot, Latest, Vfs};
+use webdavfs::WebDavFS;
+
+mod webdavfs;
 
 #[derive(Clone, Command, Default, Debug, clap::Parser, Serialize, Deserialize, Merge)]
 #[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
@@ -127,8 +130,9 @@ impl WebDavCmd {
             |s| s.parse(),
         )?;
 
+        let webdavfs = WebDavFS::new(repo, vfs, file_access);
         let dav_server = DavHandler::builder()
-            .filesystem(vfs.into_webdav_fs(repo, file_access))
+            .filesystem(Box::new(webdavfs))
             .build_handler();
 
         tokio::runtime::Builder::new_current_thread()
diff --git a/src/commands/webdav/webdavfs.rs b/src/commands/webdav/webdavfs.rs
new file mode 100644
index 000000000..052089dbf
--- /dev/null
+++ b/src/commands/webdav/webdavfs.rs
@@ -0,0 +1,353 @@
+use std::fmt::{Debug, Formatter};
+use std::io::SeekFrom;
+#[cfg(not(windows))]
+use std::os::unix::ffi::OsStrExt;
+use std::sync::{Arc, OnceLock};
+use std::time::SystemTime;
+
+use bytes::{Buf, Bytes};
+use futures::FutureExt;
+use rustic_core::repofile::Node;
+use rustic_core::vfs::{FilePolicy, OpenFile, Vfs};
+use rustic_core::{IndexedFull, Repository};
+
+use dav_server::{
+    davpath::DavPath,
+    fs::{
+        DavDirEntry, DavFile, DavFileSystem, DavMetaData, FsError, FsFuture, FsResult, FsStream,
+        OpenOptions, ReadDirMeta,
+    },
+};
+
+fn now() -> SystemTime {
+    static NOW: OnceLock<SystemTime> = OnceLock::new();
+    *NOW.get_or_init(SystemTime::now)
+}
+
+/// The inner state of a [`WebDavFS`] instance.
+struct DavFsInner<P, S> {
+    /// The [`Repository`] to use
+    repo: Repository<P, S>,
+
+    /// The [`Vfs`] to use
+    vfs: Vfs,
+
+    /// The [`FilePolicy`] to use
+    file_policy: FilePolicy,
+}
+
+impl<P, S> Debug for DavFsInner<P, S> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
+        write!(f, "DavFS")
+    }
+}
+
+/// DAV Filesystem implementation.
+///
+/// This is the main entry point for the DAV filesystem.
+/// It implements [`DavFileSystem`] and can be used to serve a [`Repository`] via DAV.
+#[derive(Debug)]
+pub struct WebDavFS<P, S> {
+    inner: Arc<DavFsInner<P, S>>,
+}
+
+impl<P, S: IndexedFull> WebDavFS<P, S> {
+    /// Create a new [`WebDavFS`] instance.
+    ///
+    /// # Arguments
+    ///
+    /// * `repo` - The [`Repository`] to use
+    /// * `vfs` - The [`Vfs`] to use
+    /// * `file_policy` - The [`FilePolicy`] to use
+    ///
+    /// # Returns
+    ///
+    /// A new [`WebDavFS`] instance
+    pub(crate) fn new(repo: Repository<P, S>, vfs: Vfs, file_policy: FilePolicy) -> Self {
+        let inner = DavFsInner {
+            repo,
+            vfs,
+            file_policy,
+        };
+
+        Self {
+            inner: Arc::new(inner),
+        }
+    }
+
+    /// Get a [`Node`] from the specified [`DavPath`].
+    ///
+    /// # Arguments
+    ///
+    /// * `path` - The path to get the [`Tree`] at
+    ///
+    /// # Errors
+    ///
+    /// * If the [`Tree`] could not be found
+    ///
+    /// # Returns
+    ///
+    /// The [`Node`] at the specified path
+    ///
+    /// [`Tree`]: crate::repofile::Tree
+    fn node_from_path(&self, path: &DavPath) -> Result<Node, FsError> {
+        self.inner
+            .vfs
+            .node_from_path(&self.inner.repo, &path.as_pathbuf())
+            .map_err(|_| FsError::GeneralFailure)
+    }
+
+    /// Get a list of [`Node`]s from the specified directory path.
+    ///
+    /// # Arguments
+    ///
+    /// * `path` - The path to get the [`Tree`] at
+    ///
+    /// # Errors
+    ///
+    /// * If the [`Tree`] could not be found
+    ///
+    /// # Returns
+    ///
+    /// The list of [`Node`]s at the specified path
+    ///
+    /// [`Tree`]: crate::repofile::Tree
+    fn dir_entries_from_path(&self, path: &DavPath) -> Result<Vec<Node>, FsError> {
+        self.inner
+            .vfs
+            .dir_entries_from_path(&self.inner.repo, &path.as_pathbuf())
+            .map_err(|_| FsError::GeneralFailure)
+    }
+}
+
+impl<P, S: IndexedFull> Clone for WebDavFS<P, S> {
+    fn clone(&self) -> Self {
+        Self {
+            inner: self.inner.clone(),
+        }
+    }
+}
+
+impl<P: Debug + Send + Sync + 'static, S: IndexedFull + Debug + Send + Sync + 'static> DavFileSystem
+    for WebDavFS<P, S>
+{
+    fn metadata<'a>(&'a self, davpath: &'a DavPath) -> FsFuture<'_, Box<dyn DavMetaData>> {
+        self.symlink_metadata(davpath)
+    }
+
+    fn symlink_metadata<'a>(&'a self, davpath: &'a DavPath) -> FsFuture<'_, Box<dyn DavMetaData>> {
+        async move {
+            let node = self.node_from_path(davpath)?;
+            let meta: Box<dyn DavMetaData> = Box::new(DavFsMetaData(node));
+            Ok(meta)
+        }
+        .boxed()
+    }
+
+    fn read_dir<'a>(
+        &'a self,
+        davpath: &'a DavPath,
+        _meta: ReadDirMeta,
+    ) -> FsFuture<'_, FsStream<Box<dyn DavDirEntry>>> {
+        async move {
+            let entries = self.dir_entries_from_path(davpath)?;
+            let entry_iter = entries.into_iter().map(|e| {
+                let entry: Box<dyn DavDirEntry> = Box::new(DavFsDirEntry(e));
+                Ok(entry)
+            });
+            let strm: FsStream<Box<dyn DavDirEntry>> = Box::pin(futures::stream::iter(entry_iter));
+            Ok(strm)
+        }
+        .boxed()
+    }
+
+    fn open<'a>(
+        &'a self,
+        path: &'a DavPath,
+        options: OpenOptions,
+    ) -> FsFuture<'_, Box<dyn DavFile>> {
+        async move {
+            if options.write
+                || options.append
+                || options.truncate
+                || options.create
+                || options.create_new
+            {
+                return Err(FsError::Forbidden);
+            }
+
+            let node = self.node_from_path(path)?;
+            if matches!(self.inner.file_policy, FilePolicy::Forbidden) {
+                return Err(FsError::Forbidden);
+            }
+
+            let open = self
+                .inner
+                .repo
+                .open_file(&node)
+                .map_err(|_err| FsError::GeneralFailure)?;
+            let file: Box<dyn DavFile> = Box::new(DavFsFile {
+                node,
+                open,
+                fs: self.inner.clone(),
+                seek: 0,
+            });
+            Ok(file)
+        }
+        .boxed()
+    }
+}
+
+/// A [`DavDirEntry`] implementation for [`Node`]s.
+#[derive(Clone, Debug)]
+struct DavFsDirEntry(Node);
+
+impl DavDirEntry for DavFsDirEntry {
+    fn metadata(&self) -> FsFuture<'_, Box<dyn DavMetaData>> {
+        async move {
+            let meta: Box<dyn DavMetaData> = Box::new(DavFsMetaData(self.0.clone()));
+            Ok(meta)
+        }
+        .boxed()
+    }
+
+    #[cfg(not(windows))]
+    fn name(&self) -> Vec<u8> {
+        self.0.name().as_bytes().to_vec()
+    }
+
+    #[cfg(windows)]
+    fn name(&self) -> Vec<u8> {
+        self.0
+            .name()
+            .as_os_str()
+            .to_string_lossy()
+            .to_string()
+            .into_bytes()
+    }
+}
+
+/// A [`DavFile`] implementation for [`Node`]s.
+///
+/// This is a read-only file.
+struct DavFsFile<P, S> {
+    /// The [`Node`] this file is for
+    node: Node,
+
+    /// The [`OpenFile`] for this file
+    open: OpenFile,
+
+    /// The [`DavFsInner`] this file belongs to
+    fs: Arc<DavFsInner<P, S>>,
+
+    /// The current seek position
+    seek: usize,
+}
+
+impl<P, S> Debug for DavFsFile<P, S> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
+        write!(f, "DavFile")
+    }
+}
+
+impl<P: Debug + Send + Sync, S: IndexedFull + Debug + Send + Sync> DavFile for DavFsFile<P, S> {
+    fn metadata(&mut self) -> FsFuture<'_, Box<dyn DavMetaData>> {
+        async move {
+            let meta: Box<dyn DavMetaData> = Box::new(DavFsMetaData(self.node.clone()));
+            Ok(meta)
+        }
+        .boxed()
+    }
+
+    fn write_bytes(&mut self, _buf: Bytes) -> FsFuture<'_, ()> {
+        async move { Err(FsError::Forbidden) }.boxed()
+    }
+
+    fn write_buf(&mut self, _buf: Box<dyn Buf + Send>) -> FsFuture<'_, ()> {
+        async move { Err(FsError::Forbidden) }.boxed()
+    }
+
+    fn read_bytes(&mut self, count: usize) -> FsFuture<'_, Bytes> {
+        async move {
+            let data = self
+                .fs
+                .repo
+                .read_file_at(&self.open, self.seek, count)
+                .map_err(|_err| FsError::GeneralFailure)?;
+            self.seek += data.len();
+            Ok(data)
+        }
+        .boxed()
+    }
+
+    fn seek(&mut self, pos: SeekFrom) -> FsFuture<'_, u64> {
+        async move {
+            match pos {
+                SeekFrom::Start(start) => {
+                    self.seek = usize::try_from(start).expect("usize overflow should not happen");
+                }
+                SeekFrom::Current(delta) => {
+                    self.seek = usize::try_from(
+                        i64::try_from(self.seek).expect("i64 wrapped around") + delta,
+                    )
+                    .expect("usize overflow should not happen");
+                }
+                SeekFrom::End(end) => {
+                    self.seek = usize::try_from(
+                        i64::try_from(self.node.meta.size).expect("i64 wrapped around") + end,
+                    )
+                    .expect("usize overflow should not happen");
+                }
+            }
+
+            Ok(self.seek as u64)
+        }
+        .boxed()
+    }
+
+    fn flush(&mut self) -> FsFuture<'_, ()> {
+        async move { Ok(()) }.boxed()
+    }
+}
+
+/// A [`DavMetaData`] implementation for [`Node`]s.
+#[derive(Clone, Debug)]
+struct DavFsMetaData(Node);
+
+impl DavMetaData for DavFsMetaData {
+    fn len(&self) -> u64 {
+        self.0.meta.size
+    }
+    fn created(&self) -> FsResult<SystemTime> {
+        Ok(now())
+    }
+    fn modified(&self) -> FsResult<SystemTime> {
+        Ok(self.0.meta.mtime.map_or_else(now, SystemTime::from))
+    }
+    fn accessed(&self) -> FsResult<SystemTime> {
+        Ok(self.0.meta.atime.map_or_else(now, SystemTime::from))
+    }
+
+    fn status_changed(&self) -> FsResult<SystemTime> {
+        Ok(self.0.meta.ctime.map_or_else(now, SystemTime::from))
+    }
+
+    fn is_dir(&self) -> bool {
+        self.0.is_dir()
+    }
+    fn is_file(&self) -> bool {
+        self.0.is_file()
+    }
+    fn is_symlink(&self) -> bool {
+        self.0.is_symlink()
+    }
+    fn executable(&self) -> FsResult<bool> {
+        if self.0.is_file() {
+            let Some(mode) = self.0.meta.mode else {
+                return Ok(false);
+            };
+            return Ok((mode & 0o100) > 0);
+        }
+        Err(FsError::NotImplemented)
+    }
+}