From 3706a3271aba41763d3fcafd314d2a82e089a061 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sun, 29 Dec 2024 15:29:27 +0100 Subject: [PATCH 01/22] use typed_path --- Cargo.lock | 10 +- crates/core/Cargo.toml | 1 + .../proptest-regressions/backend/node.txt | 7 + crates/core/src/archiver.rs | 7 +- crates/core/src/archiver/parent.rs | 24 +- crates/core/src/archiver/tree.rs | 65 +- crates/core/src/archiver/tree_archiver.rs | 15 +- crates/core/src/backend.rs | 14 +- crates/core/src/backend/childstdout.rs | 9 +- crates/core/src/backend/ignore.rs | 22 +- crates/core/src/backend/local_destination.rs | 81 +- crates/core/src/backend/node.rs | 97 +- crates/core/src/backend/stdin.rs | 7 +- crates/core/src/blob/tree.rs | 160 +- crates/core/src/commands/backup.rs | 55 +- crates/core/src/commands/cat.rs | 5 +- crates/core/src/commands/merge.rs | 2 +- crates/core/src/commands/restore.rs | 42 +- crates/core/src/repofile/snapshotfile.rs | 15 +- crates/core/src/repository.rs | 21 +- crates/core/src/repository/command_input.rs | 2 +- crates/core/src/vfs.rs | 71 +- crates/core/tests/integration/backup.rs | 10 +- crates/core/tests/integration/find.rs | 35 +- crates/core/tests/integration/ls.rs | 12 +- ...nd__find-matching-nodes-not-found.snap.new | 12 + .../integration__integration__ls__ls.snap.new | 4672 +++++++++++++++++ crates/core/tests/integration/vfs.rs | 10 +- examples/find/Cargo.toml | 1 + examples/find/examples/find.rs | 8 +- 30 files changed, 5104 insertions(+), 388 deletions(-) create mode 100644 crates/core/proptest-regressions/backend/node.txt create mode 100644 crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap.new create mode 100644 crates/core/tests/integration/snapshots/integration__integration__ls__ls.snap.new diff --git a/Cargo.lock b/Cargo.lock index 94d8be84..a9c24557 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1177,6 +1177,7 @@ dependencies = [ "rustic_backend", "rustic_core", "simplelog", + "typed-path 0.10.0", ] [[package]] @@ -3330,7 +3331,7 @@ dependencies = [ "thiserror 2.0.16", "tokio", "toml 0.9.5", - "typed-path", + "typed-path 0.11.0", "url", "walkdir", ] @@ -3411,6 +3412,7 @@ dependencies = [ "tempfile", "thiserror 2.0.16", "toml 0.9.5", + "typed-path 0.11.0", "walkdir", "xattr", "zstd", @@ -4440,6 +4442,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-path" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41713888c5ccfd99979fcd1afd47b71652e331b3d4a0e19d30769e80fec76cce" + [[package]] name = "typed-path" version = "0.11.0" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index bafbe969..7fac30d0 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -104,6 +104,7 @@ itertools = "0.14.0" quick_cache = "0.6.16" shell-words = "1.1.0" strum = { version = "0.27.2", features = ["derive"] } +typed-path = "0.11.0" zstd = "0.13.3" [target.'cfg(not(windows))'.dependencies] diff --git a/crates/core/proptest-regressions/backend/node.txt b/crates/core/proptest-regressions/backend/node.txt new file mode 100644 index 00000000..bef5b709 --- /dev/null +++ b/crates/core/proptest-regressions/backend/node.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc e47bb7f9437f9eee284dd56b9aa3f1faaa2075c84c0011f8a1b8b155be13e135 # shrinks to bytes = [92, 77, 131, 179, 118, 40, 44, 129, 125, 56, 198, 195, 210, 147, 170, 195, 13, 105, 68, 76, 242, 167, 12, 215, 106, 131, 72, 26, 136, 172, 134, 100, 15, 8, 23, 1, 100, 56, 148, 107, 248, 161, 164, 53, 72, 58, 22, 237, 101, 58, 179, 246, 83, 125, 4, 155, 231, 150, 190, 13, 206, 82, 4, 216, 25, 31, 146, 218, 93, 50, 21, 175, 22, 76, 225, 42, 45, 70, 110, 129, 90, 101, 221, 112, 35, 63, 143, 127, 39, 210, 245, 81, 238, 139, 132, 216, 206, 248, 177, 20, 143, 104, 215, 9, 63, 23, 188, 90, 13, 2, 225, 195, 170, 69, 0, 46, 34, 185, 4, 16, 52, 120, 80, 98, 202, 105, 24, 75, 40, 97, 233, 141, 55, 85, 16, 215, 26, 27, 42, 101, 150, 4, 161, 54, 224, 218, 58, 185, 8, 92, 72, 135, 129, 142, 71, 234, 163, 80, 208, 239, 11, 39, 147, 162, 193, 2, 132, 221, 83, 85, 110, 114, 56, 45, 42, 45, 170, 83, 150, 210, 83, 84, 49, 149, 1, 99, 215, 204, 220, 20, 3, 122, 98, 218, 239, 64, 108, 157, 20, 233, 137, 59, 173, 195, 16, 162, 191, 13, 96, 187, 168, 6, 5, 35, 145, 5, 4, 45, 65, 165, 162, 73, 209, 34, 226, 196, 79, 111, 27, 171, 243, 1, 138, 180, 85, 142, 148, 241, 30, 40, 159, 147, 177, 19, 237, 48, 48, 254, 72, 146, 202, 101, 13, 253, 255, 102, 186, 156, 17, 211, 45, 158, 192, 209, 153, 222, 142, 3, 122, 243, 185, 98, 106, 239, 198, 33, 177, 214, 78, 125, 66, 116, 216, 158, 57, 7, 47, 242, 154, 149, 43, 255, 133, 227, 127, 39, 254, 173, 214, 235, 73, 142, 120, 69, 56, 162, 128, 252, 60, 155, 220, 75, 237, 103, 50, 155, 187, 2, 246, 148, 183, 157, 70, 88, 97, 231, 4, 220, 96, 76, 81, 253, 6, 199, 98, 149, 77, 1, 153, 36, 158, 13, 141, 59, 137, 207, 244, 68, 238, 81, 133, 200, 252, 95, 3, 29, 244, 146, 35, 69, 140, 135, 63, 178, 180, 87, 55, 151, 87, 2, 218, 72, 39, 156, 198, 97, 116, 154, 239, 133, 173, 212, 202, 167, 232, 254, 14, 165, 210, 11, 174, 118, 252, 214, 80, 59, 21, 82, 90, 71, 250, 34, 27, 184, 201, 54, 239, 2, 35, 46, 228, 168, 50, 109, 30, 56, 181, 58, 4, 99, 212, 72, 196, 105, 141, 224, 94, 109, 148, 210, 86, 146, 116, 19, 246, 69, 119, 199, 155, 136, 213, 146, 210, 252, 48, 166, 173, 179, 93, 241, 127, 104, 140, 81, 67, 122, 210, 243, 52, 119, 117, 250, 65, 238, 120, 39, 152, 21, 170, 73, 25, 161, 194, 101, 35, 247, 240, 12, 65, 245, 163, 87, 115, 171, 169, 131, 255, 44, 55, 8, 118, 13, 15, 249, 242, 26, 223, 24, 143, 240, 80, 244, 141, 224, 54, 149, 210, 131, 215, 216, 135, 255, 136, 116, 96, 99, 133, 145, 195, 179, 8, 239, 31, 208, 109, 157, 57, 197, 183, 33, 25, 112, 209, 103, 39, 81, 170, 63, 110, 107, 106, 115, 153, 236, 30, 122, 230, 220, 85, 22, 53, 161, 157, 13, 159, 85, 203, 194, 210, 95, 7, 83, 47, 228, 248, 77, 69, 245, 47, 254, 32, 96, 72, 50, 109, 202, 239, 193, 244, 100, 13, 225, 152, 116, 63, 239, 9, 82, 212, 202, 176, 212, 98, 48, 28, 207, 173, 196, 2, 0, 220, 207, 216, 109, 207, 57, 26, 188, 235, 254, 1, 141, 202, 72, 253, 131, 232, 39, 150, 18, 170, 138, 114, 44, 156, 237, 236, 221, 137, 101, 205, 201, 123, 62, 21, 8, 12, 34, 74, 254, 185, 28, 175, 64, 37, 137, 114, 185, 27, 248, 106, 26, 49, 72, 28, 234, 110, 103, 118, 139, 24, 23, 196, 196, 92, 243, 117, 57, 122, 66, 152, 32, 97, 192, 227, 210, 137, 104, 225, 105, 66, 166, 23, 209, 40, 181, 103, 113, 110, 42, 174, 199, 57, 220, 179, 32, 85, 108, 169, 47, 2, 252, 184, 8, 36, 187, 114, 204, 182, 137, 242, 248, 50, 50, 220, 42, 132, 237, 23, 192, 253, 16, 35, 46, 47, 251, 62, 204, 81, 74, 221, 182, 248, 187, 76, 49, 111, 16, 224, 22, 125, 218, 141, 100, 128, 80, 235, 64, 68, 186, 189, 146, 22, 128, 83, 251, 36, 67, 254, 62, 196, 44, 153, 248, 96, 214, 155, 20, 97, 236, 30, 249, 27, 151, 94, 21, 136, 226, 98, 21, 3, 149, 64, 73, 244, 105, 245, 160, 229, 90, 39, 197, 126, 102, 196, 205, 78, 108, 125, 41, 160, 0, 47, 26, 173, 101, 210, 88, 193, 109, 91, 170, 236, 166, 15, 246, 136, 96, 225, 153, 158, 253, 48, 103, 251, 94, 216, 205, 222, 40, 18, 158, 172, 119, 224, 203, 135, 155, 187, 76, 163, 105, 247, 133, 96, 45, 248, 50, 66, 45, 133, 124, 14, 227, 74, 16, 240, 136, 26, 196, 49, 208, 253, 38, 150, 101, 220, 69, 89, 56, 242, 21, 186, 85, 236, 93, 39, 225, 89, 105, 178, 86, 95, 200, 232, 178, 56, 251, 222, 212, 90, 150, 199, 47, 113, 187, 244, 106, 232, 180, 20, 102, 63, 190, 249, 118, 157, 207, 203, 207, 181, 48, 215, 198, 80, 4, 158, 248, 252, 46, 119, 76, 154, 38, 54, 44, 72, 110, 228, 152, 185, 243, 245, 126, 68, 104, 106, 149, 140, 124, 167, 216, 168, 130, 251, 85, 125, 155, 19, 147, 44, 228, 138, 84, 129, 107, 106, 47, 200, 78, 175, 3, 140, 194, 61, 219, 7, 205, 130, 144, 214, 171, 55, 203, 70, 155, 190, 77, 170, 136, 230, 224, 175, 171, 249, 136, 184, 232, 142, 97, 162, 175, 32, 138, 32, 27, 218, 81, 231, 70, 10, 13, 17, 103, 14, 76, 110, 23, 26, 181, 181, 177, 170, 12, 199, 36, 239, 34, 111, 30, 188, 100, 74, 246, 202, 210, 5, 15, 194, 221, 0, 63, 103, 182, 151, 79, 217, 228, 222, 163, 152, 40, 29, 153, 64, 214, 73, 158, 162, 157, 168, 49, 76, 88, 183, 189, 157, 211, 47, 58, 242, 106, 123, 62, 45, 156, 210, 93, 154, 174, 190, 225, 93, 53, 190, 10, 13, 226, 162, 35, 165, 255, 170, 128, 231, 152, 233, 219, 85, 104, 41, 209, 96, 63, 236, 205, 88, 36, 31, 224, 161, 246, 101, 228, 35, 178, 139, 229, 63, 42, 89, 208, 100, 239, 140, 204, 200, 117, 57, 174, 236, 224, 22, 238, 242, 223, 212, 217, 14, 169, 203, 80, 205, 145, 194, 133, 42, 144, 124, 249, 236, 157, 169, 109, 98, 174, 24, 246, 244, 18, 208, 222, 180, 62, 51, 236, 184, 70, 247, 239, 215, 181, 80, 163, 11, 193, 196, 85, 114, 77, 60, 157, 142, 100, 149, 153, 160, 102, 79, 34, 243, 17, 13, 28, 24, 121, 97, 40, 166, 101, 104, 89, 151, 135, 200, 175, 191, 175, 79, 121, 180, 132, 171, 121, 163, 4, 123, 63, 99, 186, 14, 163, 22, 24, 213, 62, 54, 255, 202, 33, 23, 50, 110, 195, 174, 247, 96, 67, 137, 29, 251, 254, 87, 41, 38, 14, 88, 34, 174, 152, 111, 193, 108, 142, 186, 120, 234, 221, 170, 11, 51, 11, 12, 211, 193, 208, 40, 129, 155, 27, 129, 219, 170, 140, 78, 55, 126, 255, 16, 235, 133, 225, 61, 185, 23, 200, 108, 183, 248, 20, 154, 189, 58, 56, 17, 183, 186, 133, 79, 49, 34, 179, 216, 38, 86, 57, 1, 205, 151, 211, 247, 23, 119, 206, 96, 217, 228, 102, 159, 148, 2, 154, 193, 120, 78, 76, 58, 59, 115, 117, 94, 57, 150, 123, 75, 103, 61, 76, 41, 128, 182, 105, 81, 154, 7, 95, 242, 78, 205, 5, 45, 12, 67, 130, 107, 100, 199, 242, 203, 94, 246, 39, 155, 43, 154, 14, 212, 204, 32, 194, 126, 225, 190, 199, 222, 150, 45, 145, 79, 133, 171, 199, 244, 122, 244, 183, 136, 38, 54, 74, 18, 78, 85, 107, 47, 199, 12, 234, 85, 147, 164, 236, 227, 217, 211, 6, 104, 155, 35, 242, 197, 85, 117, 59, 17, 251, 229, 223, 104, 20, 249, 129, 219, 57, 104, 169, 17, 7, 80, 63, 147, 185, 179, 58, 196, 93, 229, 66, 56, 159, 243, 42, 176, 52, 237, 232, 36, 236, 58, 227, 197, 66, 148, 214, 63, 109, 98, 95, 161, 21, 191, 104, 19, 80, 226, 254, 71, 118, 221, 248, 75, 58, 251, 39, 88, 110, 97, 39, 18, 148, 74, 134, 49, 248, 183, 106, 189, 104, 111, 243, 181, 49, 10, 69, 22, 143, 97, 107, 250, 186, 18, 240, 69, 169, 144, 178, 78, 177, 95, 165, 109, 246, 224, 74, 66, 223, 136, 248, 142, 92, 205, 143, 192, 190, 40, 196, 238, 131, 74, 160, 111, 156, 26, 122, 112, 99, 73, 204, 210, 155, 209, 250, 246, 48, 220, 230, 31, 247, 112, 168, 77, 2, 66, 122, 121, 87, 116, 209, 8, 112, 244, 25, 92, 58, 73, 116, 3, 89, 46, 105, 152, 175, 64, 251, 191, 53, 129, 93, 93, 178, 239, 44, 148, 206, 53, 39, 89, 210, 60, 178, 122, 201, 53, 5, 75, 208, 16, 151, 206, 67, 13, 233, 125, 116, 121, 174, 46, 59, 154, 16, 56, 69, 232, 147, 113, 141, 167, 180, 56, 158, 83, 139, 123, 33, 129, 85, 156, 162, 98, 202, 205, 79, 202, 211, 27, 149, 61, 98, 57, 61, 8, 226, 235, 107, 183, 32, 53, 226, 156, 4, 232, 192, 136, 117, 223, 129, 251, 48, 191, 2, 24, 8, 205, 14, 139, 56, 130, 49, 27, 68, 65, 97, 247, 160, 121, 92, 164, 246, 86, 6, 121, 159, 159, 231, 107, 186, 87, 226, 1, 58, 230, 135, 227, 0, 39, 251, 76, 141, 11, 253, 72, 20, 51, 204, 42, 55, 68, 208, 60, 53, 96, 212, 20, 130, 10, 137, 182, 174, 59, 38, 98, 194, 150, 99, 92, 165, 220, 146, 174, 235, 254, 164, 255, 168, 25, 170, 150, 162, 178, 101, 126, 49, 136, 202, 141, 207, 102, 139, 89, 11, 198, 130, 244, 177, 191, 50, 208, 91, 6, 122, 172, 250, 44, 192, 242, 206, 3, 155, 97, 255, 155, 134, 196, 233, 27, 125, 26, 16, 166, 236, 209, 230, 3, 180, 190, 172, 83, 118, 44, 15, 39, 133, 32, 2, 7, 40, 39, 99, 247, 116, 102, 75, 119, 36, 213, 216, 94, 124, 218, 179, 19, 163, 71, 181, 234, 178, 81, 114, 162, 174, 167, 219, 239, 4, 110, 188, 115, 62, 224, 140, 104, 105, 137, 166, 55, 178, 96, 237, 132, 228, 140, 57, 182, 239, 100, 89, 15, 3, 189, 48, 53, 68, 81, 130, 173, 118, 107, 223, 61, 162, 239, 130, 246, 86, 224, 210, 106, 28, 254, 57, 57, 169, 14, 31, 234, 8, 224, 162, 220, 182, 202, 5, 47, 2, 227, 120, 143, 197, 81, 203, 209, 171, 206, 61, 229, 134, 120, 163, 187, 26, 229, 148, 29, 33, 81, 86, 239, 82, 86, 77, 2, 254, 194, 219, 64, 102, 126, 192, 227, 161, 244, 145, 145, 83, 152, 134, 114, 117, 50, 248, 169, 47, 74, 216, 65, 27, 174, 77, 40, 69, 67, 250, 48, 224, 66, 246, 51, 183, 227, 57, 17, 1, 210, 38, 48, 55, 173, 67, 83, 40, 73, 138, 153, 251, 121, 152, 250, 209, 15, 71, 5, 48, 210, 190, 36, 52, 44, 129, 166, 159, 30, 144, 90, 155, 41, 207, 151, 10, 248, 102, 126, 79, 93, 230, 222, 143, 158, 238, 45, 175, 160, 54, 85, 93, 203, 122, 21, 92, 153, 174, 159, 61, 106, 114, 240, 107, 69, 195, 237, 43, 47, 251, 75, 114, 27, 191, 180, 202, 98, 18, 10, 197, 224, 43, 52, 90, 167, 128, 56, 17, 198, 131, 7, 185, 245, 179, 138, 230, 136, 64, 125, 49, 189, 48, 162, 50, 108, 114, 45, 126, 225, 52, 156, 233, 178, 233, 167, 83, 11, 57, 164, 226, 82, 40, 110, 14, 235, 135, 60, 124, 119, 141, 204, 211, 19, 176, 248, 217, 236, 89, 52, 87, 132, 175, 206, 0, 168, 108, 149, 113, 217, 5, 169, 174, 128, 80, 136, 232, 193, 154, 178, 62, 86, 255, 121, 132, 108, 172, 11, 231, 151, 204, 150, 216, 142, 11, 226, 196, 175, 136, 6, 31, 242, 89, 197, 130, 15, 75, 99, 13, 246, 77, 252, 157, 137, 133, 205, 4, 131, 17, 119, 112, 104, 153, 91, 55, 195, 16, 201, 86, 69, 205, 129, 130, 255, 205, 239, 94, 197, 56, 205, 4, 16, 15, 8, 72, 76, 254, 120, 107, 184, 189, 21, 253, 180, 252, 209, 15, 75, 101, 116, 229, 90, 94, 131, 123, 136, 226, 14, 186, 237, 30, 192, 204, 64, 148, 230, 250, 183, 152, 39, 90, 22, 59, 254, 229, 56, 223, 133, 196, 112, 75, 170, 142, 44, 144, 166, 82, 49, 130, 147, 42, 253, 83, 255, 38, 106, 151, 234, 14, 165, 200, 87, 82, 94, 21, 242, 36, 161, 38, 81, 5, 180, 95, 134, 223, 98, 227, 196, 123, 54, 73, 129, 194, 4, 44, 167, 219, 170, 3, 247, 80, 21, 151, 14, 74, 6, 222, 236, 27, 146, 126, 101, 39, 91, 133, 108, 34, 95, 61, 37, 107, 181, 148, 253, 83, 254, 145, 142, 136, 67, 242, 252, 57, 65, 141, 37, 185, 233, 167, 130, 52, 176, 79, 194, 54, 227, 94, 166, 225, 144, 132, 237, 29, 138, 111, 224, 43, 199, 192, 1, 42, 36, 26, 237, 103, 193, 241, 64, 166, 2, 145, 161, 158, 226, 110, 226, 226, 152, 206, 59, 164, 86, 205, 83, 244, 241, 9, 75, 176, 82, 206, 38, 68, 11, 196, 147, 20, 85, 27, 210, 189, 240, 8, 178, 239, 150, 144, 31, 50, 108, 54, 81, 45, 6, 25, 194, 9, 102, 56, 113, 198, 33, 231, 2, 221, 72, 254, 234, 246, 16, 132, 105, 240, 176, 132, 104, 197, 127, 104, 17, 204, 189, 203, 129, 93, 206, 6, 238, 109, 24, 72, 206, 145, 100, 220, 215, 207, 0, 20, 131, 137, 178, 79, 196, 145, 197, 227, 49, 13, 111, 170, 51, 110, 242, 34, 142, 102, 50, 219, 215, 135, 73, 109, 116, 234, 36, 92, 43, 76, 136, 68, 87, 9, 212, 158, 228, 103, 77, 37, 225, 104, 242, 179, 246, 188, 85, 6, 130, 213, 101, 197, 144, 247, 189, 245, 51, 45, 247, 158, 146, 90, 173, 10, 75, 233, 213, 133, 255, 58, 218, 144, 54, 47, 121, 123, 68, 229, 96, 113, 122, 143, 147, 183, 210, 89, 213, 20, 183, 126, 137, 242, 166, 171, 63, 208, 140, 147, 94, 53, 97, 59, 140, 150, 85, 43, 118, 0, 1, 78, 156, 214, 193, 242, 190, 186, 247, 71, 247, 62, 41, 45, 217, 0, 125, 115, 83, 245, 57, 171, 86, 178, 39, 128, 65, 109, 59, 239, 41, 137, 68, 214, 162, 242, 64, 94, 32, 44, 252, 238, 85, 227, 165, 134, 217, 93, 231, 35, 130, 139, 4, 163, 58, 5, 6, 37, 196, 126, 210, 61, 37, 69, 28, 105, 86, 3, 188, 91, 135, 37, 144, 167, 211, 106, 12, 36, 62, 119, 6, 63, 38, 20, 114, 194, 81, 72, 94, 116, 132, 127, 128, 252, 147, 195, 249, 157, 19, 219, 76, 128, 164, 237, 64, 127, 0, 42, 252, 40, 111, 17, 195, 224, 190, 35, 126, 17, 165, 41, 72, 167, 77, 194, 94, 159, 159, 243, 79, 115, 222, 171, 211, 240, 139, 182, 152, 195, 171, 32, 215, 179, 88, 207, 160, 164, 208, 11, 223, 70, 185, 44, 233, 150, 3, 180, 50, 92, 148, 32, 10, 190, 124, 3, 14, 188, 181, 92, 39, 191, 131, 116, 183, 123, 147, 175, 78, 117, 72, 238, 149, 107, 253, 6, 235, 48, 64, 60, 83, 0, 31, 221, 30, 104, 233, 196, 188, 189, 138, 109, 167, 15, 250, 247, 47, 33, 235, 6, 31, 45, 96, 33, 113, 197, 196, 195, 26, 29, 162, 47, 66, 39, 213, 148, 147, 15, 216, 148, 161, 172, 3, 47, 225, 22, 250, 203, 254, 76, 71, 252, 1, 198, 53, 122, 238, 149, 216, 17, 36, 50, 239, 63, 98, 235, 182, 85, 98, 111, 96, 17, 182, 17, 129, 181, 81, 49, 127, 106, 133, 250, 254, 183, 58, 83, 148, 60, 105, 232, 46, 206, 61, 2, 198, 219, 12, 21, 248, 211, 192, 183, 229, 175, 20, 123, 90, 60, 39, 237, 20, 86, 198, 44, 181, 6, 164, 177, 98, 225, 79, 238, 132, 13, 36, 234, 31, 7, 51, 76, 27, 33, 172, 104, 213, 4, 206, 145, 7, 189, 187, 62, 97, 3, 78, 29, 203, 97, 76, 161, 72, 220, 116, 19, 98, 183, 238, 240, 57, 253, 255, 228, 173, 138, 89, 101, 223, 245, 119, 133, 123, 194, 168, 129, 79, 238, 245, 145, 238, 150, 219, 43, 84, 14, 30, 167, 186, 55, 165, 74, 152, 16, 219, 115, 230, 227, 255, 3, 127, 29, 188, 159, 235, 171, 173, 29, 47, 231, 111, 17, 80, 106, 146, 99, 140, 207, 233, 248, 112, 167, 166, 64, 34, 230, 245, 198, 41, 97, 19, 8, 36, 252, 236, 116, 40, 128, 89, 251, 128, 123, 250, 202, 164, 28, 34, 102, 218, 138, 236, 191, 53, 242, 10, 117, 18, 187, 106, 186, 17, 115, 40, 69, 61, 243, 189, 89, 60, 247, 189, 83, 164, 116, 254, 234, 121, 1, 79, 238, 0, 229, 93, 214, 177, 51, 121, 89, 251, 75, 66, 154, 36, 153, 103, 227, 3, 115, 21, 148, 175, 132, 5, 239, 197, 4, 147, 84, 184, 187, 38, 80, 99, 140, 175, 100, 164, 8, 122, 37, 112, 192, 103, 238, 151, 136, 182, 208, 121, 191, 18, 6, 92, 233, 138, 109, 6, 8, 231, 7, 231, 246, 68, 117, 153, 6, 96, 249, 52, 0, 49, 147, 98, 230, 222, 209, 70, 214, 39, 111, 34, 130, 62, 5, 202, 98, 69, 156, 132, 235, 218, 248, 105, 170, 140, 184, 140, 103, 119, 57, 221, 236, 229, 198, 89, 181, 164, 92, 19, 154, 86, 22, 239, 236, 131, 237, 175, 189, 249, 97, 20, 248, 174, 197, 183, 191, 114, 240, 1, 126, 98, 215, 186, 13, 49, 241, 194, 24, 49, 135, 45, 237, 49, 229, 220, 59, 225, 248, 220, 133, 184, 250, 127, 162, 9, 77, 245, 66, 128, 132, 91, 174, 68, 28, 237, 245, 156, 242, 235, 60, 227, 84, 7, 115, 139, 129, 223, 105, 209, 47, 88, 65, 210, 45, 117, 116, 233, 111, 222, 98, 253, 126, 163, 251, 175, 173, 182, 98, 114, 28, 17, 174, 97, 71, 240, 112, 240, 254, 161, 41, 154, 201, 106, 184, 164, 79, 97, 164, 124, 30, 93, 205, 217, 156, 143, 156, 32, 34, 29, 34, 43, 189, 122, 69, 171, 7, 179, 37, 162, 191, 212, 108, 245, 137, 18, 196, 219, 46, 101, 108, 200, 100, 218, 222, 251, 199, 120, 51, 58, 127, 35, 225, 87, 39, 127, 137, 119, 157, 42, 14, 216, 33, 15, 207, 11, 21, 152, 30, 44, 72, 251, 204, 225, 25, 93, 202, 206, 225, 211, 79, 220, 67, 114, 85, 20, 10, 118, 129, 107, 124, 193, 113, 176, 219, 140, 151, 229, 81, 171, 70, 9, 80, 164, 88, 163, 182, 159, 184, 180, 102, 161, 220, 116, 218, 189, 48, 183, 13, 15, 114, 144, 37, 18, 117, 11, 97, 210, 26, 199, 102, 194, 22, 189, 169, 240, 218, 209, 14, 102, 246, 150, 78, 33, 209, 56, 127, 213, 124, 116, 22, 1, 217, 249, 25, 84, 60, 17, 123, 44, 141, 50, 160, 150, 197, 242, 16, 82, 116, 236, 151, 218, 40, 13, 222, 171, 2, 77, 234, 102, 31, 145, 14, 216, 141, 210, 175, 253, 189, 145, 70, 47, 233, 141, 43, 213, 233, 176, 199, 240, 30, 88, 82, 236, 41, 208, 115, 198, 172, 123, 221, 25, 216, 137, 79, 147, 228, 177, 180, 243, 26, 17, 86, 19, 8, 73, 47, 133, 2, 123, 74, 16, 146, 2, 42, 178, 192, 237, 2, 126, 200, 14, 164, 171, 166, 234, 122, 71, 187, 123, 60, 193, 77, 102, 76, 22, 58, 107, 212, 240, 194, 229, 145, 172, 0, 206, 138, 179, 134, 212, 233, 38, 141, 49, 119, 234, 132, 162, 61, 120, 39, 83, 225, 230, 211, 176, 119, 191, 88, 73, 198, 164, 13, 121, 175, 82, 205, 97, 165, 111, 157, 108, 70, 75, 134, 224, 143, 8, 23, 192, 168, 72, 135, 120, 80, 223, 82, 96, 101, 170, 142, 77, 209, 88, 232, 13, 222, 126, 211, 147, 240, 124, 139, 24, 128, 133, 91, 36, 98, 219, 250, 136, 245, 75, 58, 254, 92, 193, 190, 181, 90, 187, 123, 194, 86, 122, 246, 139, 91, 164, 59, 8, 192, 78, 156, 163, 78, 11, 75, 6, 241, 19, 86, 240, 2, 188, 98, 218, 244, 75, 41, 169, 140, 159, 37, 218, 90, 184, 205, 99, 194, 25, 222, 253, 96, 171, 154, 117, 74, 245, 178, 146, 255, 69, 153, 101, 223, 128, 248, 195, 21, 146, 123, 105, 56, 194, 231, 37, 213, 237, 169, 30, 171, 31, 137, 98, 16, 22, 117, 105, 97, 95, 27, 208, 117, 51, 235, 187, 219, 228, 178, 67, 3, 23, 225, 185, 44, 96, 39, 219, 216, 252, 186, 147, 183, 194, 73, 243, 65, 110, 177, 48, 15, 3, 46, 60, 99, 53, 20, 209, 50, 248, 168, 231, 91, 235, 169, 59, 172, 48, 65, 90, 237, 206, 106, 96, 92, 208, 147, 253, 118, 15, 74, 41, 147, 130, 101, 154, 32, 126, 191, 7, 30, 149, 241, 80, 126, 31, 169, 189, 218, 227, 73, 185, 11, 196, 65, 254, 229, 242, 253, 5, 32, 50, 203, 5, 209, 8, 41, 201, 170, 7, 21, 122, 21, 138, 181, 221, 84, 128, 70, 241, 188, 138, 118, 221, 12, 44, 138, 123, 25, 0, 206, 82, 27, 68, 4, 29, 86, 204, 106, 177, 179, 179, 51, 32, 155, 147, 136, 76, 242, 171, 66, 26, 234, 145, 115, 156, 184, 218, 219, 204, 118, 173, 216, 174, 228, 240, 24, 27, 154, 111, 71, 136, 189, 76, 162, 120, 76, 122, 80, 229, 109, 32, 116, 213, 230, 119, 29, 182, 66, 156, 168, 92, 244, 102, 30, 187, 145, 104, 118, 251, 193, 233, 216, 122, 51, 227, 144, 143, 167, 38, 65, 165, 175, 137, 120, 15, 132, 202, 228, 194, 187, 29, 191, 145, 23, 169, 200, 40, 167, 120, 124, 204, 223, 210, 11, 232, 42, 127, 186, 229, 113, 242, 14, 112, 67, 160, 26, 170, 0, 192, 62, 113, 65, 106, 152, 37, 190, 160, 44, 79, 154, 52, 138, 118, 133, 136, 215, 238, 185, 143, 98, 185, 154, 239, 21, 72, 216, 224, 102, 210, 220, 86, 71, 176, 19, 68, 23, 255, 133, 34, 167, 134, 148, 168, 195, 21, 75, 178, 32, 43, 239, 155, 237, 224, 20, 63, 143, 174, 166, 156, 83, 118, 197, 154, 118, 143, 145, 248, 161, 214, 242, 200, 87, 198, 107, 32, 150, 233, 21, 29, 226, 200, 29, 233, 224, 128, 146, 160, 128, 119, 3, 23, 15, 188, 192, 21, 251, 197, 143, 95, 78, 104, 7, 213, 77, 61, 74, 206, 215, 63, 31, 42, 64, 98, 215, 254, 181, 240, 206, 79, 152, 102, 189, 217, 155, 123, 36, 100, 198, 49, 3, 73, 153, 1, 223, 27, 24, 219, 33, 223, 202, 2, 187, 36, 208, 36, 154, 11, 101, 191, 79, 225, 177, 28, 100, 139, 222, 44, 196, 167, 80, 0, 96, 130, 120, 63, 36, 129, 15, 226, 54, 12, 184, 183, 193, 49, 167, 217, 109, 180, 125, 138, 211, 80, 207, 28, 243, 53, 116, 216, 149, 188, 119, 31, 237, 146, 82, 46, 246, 38, 102, 222, 101, 13, 237, 229, 115, 200, 44, 118, 254, 171, 212, 218, 220, 243, 123, 122, 169, 171, 35, 225, 218, 21, 118, 121, 235, 17, 71, 86, 152, 71, 20, 235, 85, 246, 39, 53, 109, 132, 236, 44, 24, 232, 216, 12, 165, 60, 22, 69, 62, 96, 27, 63, 208, 212, 75, 215, 95, 109, 167, 6, 193, 80, 244, 26, 214, 147, 94, 65, 187, 123, 207, 8, 67, 247, 177, 137, 215, 43, 240, 188, 165, 0, 110, 31, 154, 193, 221, 96, 16, 200, 229, 253, 234, 159, 0, 67, 127, 155, 201, 42, 91, 211, 144, 96, 216, 131, 218, 213, 163, 43, 197, 204, 85, 119, 132, 168, 72, 101, 204, 101, 2, 37, 44, 122, 235, 100, 19, 108, 57, 134, 66, 110, 18, 211, 162, 225, 127, 53, 97, 133, 179, 1, 83, 18, 111, 112, 158, 105, 11, 75, 5, 199, 236, 6, 81, 208, 187, 44, 151, 160, 38, 239, 173, 235, 53, 74, 34, 60, 179, 59, 106, 27, 231, 251, 125, 148, 214, 101, 78, 113, 253, 251, 241, 117, 38, 112, 173, 230, 173, 60, 112, 136, 236, 186, 99, 113, 240, 13, 97, 101, 133, 146, 196, 188, 107, 236, 99, 117, 248, 102, 58, 73, 228, 77, 125, 69, 178, 36, 87, 50, 61, 139, 75, 89, 104, 171, 120, 75, 42, 234, 0, 40, 166, 107, 0, 70, 249, 43, 44, 9, 61, 52, 169, 242, 146, 108, 24, 202, 25, 233, 152, 147, 98, 53, 204, 111, 232, 206, 155, 42, 147, 77, 158, 122, 7, 253, 52, 28, 186, 113, 188, 114, 113, 109, 1, 67, 78, 207, 25, 78, 80, 44, 221, 239, 86, 232, 255, 93, 94, 162, 171, 199, 3, 121, 212, 56, 84, 122, 4, 111, 52, 208, 131, 100, 93, 1, 29, 56, 70, 59, 48, 221, 222, 21, 17, 216, 43, 113, 228, 94, 45, 218, 225, 193, 37, 82, 27, 169, 113, 229, 104, 18, 38, 227, 222, 148, 146, 230, 67, 14, 250, 184, 163, 113, 197, 186, 16, 220, 235, 28, 231, 146, 179, 106, 229, 228, 163, 218, 37, 176, 66, 184, 235, 112, 148, 253, 143, 186, 52, 16, 208, 73, 130, 85, 194, 54, 42, 109, 131, 88, 64, 164, 106, 46, 86, 168, 165, 77, 29, 219, 246, 203, 23, 171, 71, 243, 43, 187, 51, 36, 92, 151, 3, 129, 154, 249, 107, 37, 72, 94, 60, 97, 156, 218, 97, 58, 166, 126, 209, 136, 1, 39, 203, 136, 153, 150, 44, 52, 140, 151, 184, 169, 134, 109, 153, 214, 95, 25, 98, 106, 138, 213, 100, 251, 200, 54, 123, 243, 74, 197, 224, 219, 195, 212, 72, 50, 29, 45, 21, 205, 190, 22, 66, 13, 201, 171, 121, 44, 185, 16, 41, 110, 166, 144, 169, 125, 201, 129, 10, 13, 3, 58, 247, 205, 222, 187, 203, 195, 58, 44, 58, 107, 30, 175, 100, 169, 189, 131, 30, 236, 85, 131, 15, 211, 184, 6, 41, 36, 74, 251, 53, 107, 65, 106, 221, 220, 112, 12, 64, 231, 22, 128, 248, 95, 213, 165, 62, 136, 79, 142, 204, 143, 189, 34, 18, 18, 172, 131, 82, 118, 234, 98, 189, 132, 44, 151, 249, 110, 55, 182, 126, 100, 122, 131, 170, 180, 230, 75, 66, 167, 135, 124, 118, 68, 143, 11, 150, 163, 56, 75, 90, 254, 93, 205, 252, 212, 160, 230, 1, 230, 42, 61, 86, 169, 225, 5, 229, 237, 202, 148, 13, 220, 179, 99, 240, 10, 69, 51, 10, 60, 215, 121, 177, 165, 4, 163, 59, 206, 151, 222, 30, 178, 178, 242, 144, 38, 109, 217, 150, 28, 170, 240, 38, 221, 145, 165, 187, 36, 113, 98, 189, 123, 222, 254, 15, 218, 161, 59, 217, 183, 113, 190, 113, 176, 59, 89, 194, 67, 10, 3, 31, 249, 74, 62, 203, 241, 124, 78, 10, 104, 171, 244, 104, 156, 165, 83, 26, 189, 42, 102, 23, 58, 3, 39, 98, 239, 2, 104, 203, 157, 248, 177, 61, 168, 149, 146, 206, 12, 81, 196, 162, 1, 213, 191, 245, 194, 33, 156, 167, 175, 71, 60, 83, 196, 152, 5, 170, 165, 196, 131, 207, 70, 57, 24, 23, 61, 249, 179, 109, 249, 113, 22, 254, 104, 216, 226, 18, 247, 187, 228, 126, 151, 245, 238, 127, 143, 241, 107, 184, 4, 92, 55, 226, 228, 194, 207, 37, 71, 186, 49, 110, 11, 176, 238, 26, 53, 156, 182, 41, 139, 207, 18, 83, 246, 175, 210, 221, 142, 82, 16, 87, 71, 244, 170, 160, 88, 39, 24, 249, 28, 131, 170, 118, 110, 210, 17, 142, 163, 183, 190, 85, 69, 16, 241, 31, 192, 202, 210, 255, 89, 62, 169, 238, 40, 245, 16, 174, 202, 5, 233, 251, 188, 225, 64, 16, 47, 227, 174, 204, 253, 219, 99, 242, 81, 30, 217, 11, 174, 187, 176, 242, 72, 21, 31, 194, 131, 40, 43, 146, 120, 108, 53, 81, 175, 152, 6, 254, 176, 168, 28, 134, 229, 10, 101, 122, 33, 221, 123, 208, 140, 158, 221, 141, 186, 114, 166, 49, 112, 174, 106, 215, 7, 206, 91, 157, 68, 180, 190, 138, 125, 185, 254, 25, 168, 42, 6, 127, 186, 39, 171, 188, 135, 8, 35, 63, 12, 165, 54, 236, 83, 250, 213, 152, 200, 200, 179, 154, 169, 66, 138, 120, 97, 198, 206, 23, 33, 113, 107, 65, 206, 5, 157, 110, 42, 151, 80, 172, 170, 46, 212, 53, 6, 136, 62, 182, 8, 188, 172, 124, 18, 63, 198, 207, 190, 224, 21, 115, 70, 67, 15, 107, 21, 103, 72, 125, 69, 222, 27, 186, 15, 239, 31, 36, 178, 252, 83, 129, 14, 14, 85, 229, 80, 176, 13, 170, 72, 3, 159, 238, 244, 168, 185, 67, 13, 175, 203, 254, 200, 195, 37, 160, 137, 181, 158, 224, 196, 34, 82, 79, 170, 133, 235, 124, 33, 155, 87, 106, 231, 30, 224, 204, 4, 151, 98, 102, 53, 111, 173, 84, 96, 101, 9, 209, 9, 183, 216, 18, 47, 32, 191, 180, 179, 153, 254, 24, 225, 13, 156, 100, 105, 73, 44, 206, 140, 194, 247, 173, 63, 169, 110, 108, 224, 125, 37, 30, 10, 17, 219, 159, 81, 83, 227, 58, 165, 152, 100, 188, 177, 125, 197, 103, 156, 72, 114, 169, 69, 190, 236, 24, 206, 33, 178, 81, 27, 235, 129, 145, 250, 71, 102, 225, 232, 70, 68, 209, 244, 216, 67, 238, 161, 197, 166, 255, 161, 145, 156, 182, 238, 166, 23, 86, 13, 210, 58, 242, 141, 27, 78, 44, 56, 19, 115, 226, 239, 228, 22, 29, 110, 148, 122, 61, 10, 232, 221, 88, 11, 17, 195, 70, 202, 141, 44, 24, 46, 191, 68, 162, 226, 92, 94, 98, 86, 102, 247, 137, 124, 117, 118, 121, 41, 7, 194, 181, 201, 227, 204, 29, 115, 105, 181, 165, 110, 134, 15, 129, 195, 124, 158, 109, 192, 11, 147, 140, 172, 156, 163, 204, 237, 10, 234, 10, 49, 207, 62, 248, 184, 35, 194, 205, 13, 237, 167, 48, 161, 159, 82, 149, 150, 38, 27, 169, 115, 27, 80, 62, 5, 0, 78, 84, 13, 192, 110, 112, 13, 1, 222, 146, 233, 250, 195, 153, 47, 219, 189, 109, 226, 128, 144, 133, 78, 155, 85, 225, 80, 243, 29, 200, 116, 78, 253, 246, 24, 90, 19, 254, 49, 235, 161, 174, 57, 75, 108, 221, 74, 25, 49, 91, 65, 112, 94, 211, 30, 130, 64, 204, 23, 150, 240, 42, 1, 23, 23, 201, 83, 2, 13, 117, 164, 70, 135, 65, 200, 150, 42, 232, 10, 5, 191, 244, 227, 143, 56, 113, 233, 11, 241, 147, 110, 142, 44, 196, 117, 254, 126, 119, 49, 84, 233, 1, 20, 43, 11, 108, 179, 197, 59, 177, 108, 71, 93, 36, 102, 236, 201, 14, 122, 225, 158, 113, 250, 57, 120, 79, 39, 221, 161, 113, 197, 210, 175, 233, 37, 108, 39, 184, 129, 132, 3, 211, 105, 213, 202, 15, 170, 61, 246, 233, 131, 1, 170, 220, 125, 169, 84, 196, 146, 174, 131, 3, 8, 172, 114, 189, 214, 238, 171, 153, 165, 9, 22, 53, 123, 214, 146, 185, 188, 178, 14, 236, 24, 103, 143, 205, 155, 123, 82, 71, 211, 27, 80, 177, 182, 233, 198, 3, 150, 154, 77, 195, 21, 21, 25, 151, 188, 191, 34, 112, 140, 56, 170, 240, 154, 61, 188, 247, 77, 206, 119, 254, 15, 118, 37, 199, 6, 107, 145, 135, 192, 197, 134, 56, 230, 16, 95, 212, 158, 98, 29, 185, 30, 184, 189, 96, 223, 117, 93, 211, 23, 135, 213, 14, 72, 131, 137, 235, 22, 128, 192, 56, 230, 64, 5, 202, 74, 57, 110, 113, 241, 43, 135, 192, 81, 190, 19, 222, 117, 134, 205, 89, 165, 224, 96, 207, 228, 154, 131, 57, 62, 88, 83, 63, 188, 253, 27, 107, 0, 110, 188, 214, 128, 199, 107, 250, 180, 41, 65, 122, 217, 64, 55, 43, 97, 8, 243, 152, 139, 243, 249, 75, 37, 237, 151, 224, 156, 214, 48, 241, 252, 213, 117, 225, 14, 98, 26, 200, 152, 200, 198, 30, 119, 124, 31, 141, 244, 13, 135, 133, 8, 45, 224, 10, 52, 67, 203, 33, 4, 185, 136, 220, 143, 186, 108, 68, 209, 157, 76, 40, 20, 23, 184, 36, 140, 133, 207, 122, 32, 81, 138, 63, 81, 191, 198, 228, 211, 249, 15, 130, 86, 212, 17, 75, 22, 143, 238, 162, 93, 17, 225, 202, 118, 176, 39, 37, 83, 96, 171, 201, 252, 184, 60, 244, 144, 110, 7, 119, 191, 204, 149, 183, 144, 49, 238, 178, 126, 246, 3, 182, 165, 64, 88, 145, 126, 100, 0, 105, 227, 127, 195, 193, 149, 132, 189, 48, 16, 31, 28, 57, 246, 60, 248, 196, 229, 24, 21, 159, 211, 239, 214, 1, 128, 97, 138, 136, 143, 161, 27, 44, 65, 130, 203, 94, 87, 134, 32, 134, 126, 224, 193, 120, 35, 120, 209, 234, 18, 18, 18, 254, 215, 64, 64, 124, 146, 159, 177, 50, 226, 125, 191, 184, 147, 187, 113, 141, 178, 40, 24, 79, 137, 189, 178, 94, 142, 14, 179, 2, 239, 23, 151, 10, 165, 34, 117, 165, 243, 73, 254, 175, 226, 14, 215, 190, 88, 61, 76, 68, 85, 46, 160, 254, 56, 23, 77, 103, 69, 125, 156, 165, 126, 180, 42, 89, 172, 178, 107, 3, 51, 4, 53, 117, 55, 53, 150, 135, 246, 65, 19, 57, 100, 93, 243, 31, 53, 224, 64, 167, 33, 130, 201, 17, 138, 85, 5, 33, 179, 95, 26, 73, 111, 234, 103, 255, 136, 13, 18, 116, 150, 162, 96, 113, 117, 79, 46, 30, 248, 233, 42, 182, 99, 253, 178, 177, 170, 62, 103, 197, 20, 7, 117, 59, 216, 1, 213, 167, 115, 134, 239, 229, 160, 156, 22, 94, 133, 99, 61, 100, 18, 62, 74, 36, 151, 65, 157, 110, 28, 46, 19, 169, 173, 222, 118, 234, 86, 21, 199, 219, 33, 218, 255, 232, 237, 172, 176, 163, 110, 60, 214, 24, 243, 27, 147, 123, 90, 148, 50, 15, 249, 109, 229, 60, 114, 99, 157, 7, 54, 94, 77, 16, 220, 126, 129, 44, 82, 131, 253, 80, 153, 194, 58, 229, 138, 147, 233, 116, 55, 0, 6, 250, 198, 226, 203, 29, 89, 145, 177, 63, 134, 204, 254, 55, 147, 213, 193, 192, 86, 55, 161, 49, 120, 239, 200, 250, 136, 33, 251, 254, 61, 241, 241, 180, 27, 142, 239, 112, 219, 109, 25, 15, 31, 247, 36, 74, 11, 68, 149, 17, 135, 20, 4, 253, 111, 76, 53, 94, 212, 47, 163, 97, 6, 161, 127, 126, 150, 136, 189, 179, 157, 39, 31, 69, 249, 114, 149, 229, 160, 121, 7, 0, 229, 69, 51, 229, 242, 74, 128, 201, 22, 170, 154, 215, 72, 20, 137, 85, 149, 186, 176, 4, 255, 106, 214, 18, 89, 222, 60, 247, 221, 153, 178, 252, 156, 51, 132, 207, 154, 156, 154, 131, 21, 105, 224, 212, 72, 168, 253, 104, 6, 196, 118, 50, 186, 146, 244, 119, 229, 3, 203, 85, 12, 113, 124, 229, 253, 47, 172, 166, 222, 67, 163, 111, 166, 167, 27, 46, 35, 129, 32, 109, 31, 24, 233, 198, 102, 16, 93, 162, 227, 14, 145, 14, 122, 104, 32, 181, 225, 254, 187, 232, 199, 96, 84, 213, 49, 74, 114, 217, 121, 160, 74, 41, 62, 2, 38, 109, 23, 250, 176, 25, 245, 102, 122, 71, 45, 62, 110, 143, 25, 141, 12, 47, 160, 27, 237, 218, 195, 50, 88, 205, 206, 107, 245, 193, 197, 145, 135, 217, 202, 243, 178, 4, 204, 118, 201, 102, 75, 182, 126, 94, 154, 202, 5, 225, 18, 22, 85, 71, 139, 167, 52, 155, 68, 5, 7, 231, 95, 238, 56, 244, 247, 134, 1, 29, 202, 201, 175, 159, 147, 173, 191, 133, 126, 182, 153, 88, 102, 128, 100, 5, 65, 234, 156, 229, 155, 125, 123, 38, 215, 36, 150, 191, 99, 140, 145, 254, 201, 246, 28, 228, 34, 30, 177, 70, 14, 125, 108, 51, 236, 33, 151, 146, 250, 214, 228, 212, 13, 91, 5, 143, 36, 254, 137, 159, 202, 199, 111, 139, 18, 87, 248, 46, 101, 245, 38, 131, 135, 128, 224, 114, 137, 7, 29, 193, 42, 231, 245, 11, 59, 68, 242, 18, 205, 50, 207, 65, 164, 63, 190, 30, 4, 64, 13, 50, 39, 71, 237, 162, 148, 114, 133, 19, 58, 22, 139, 12, 49, 14, 155, 141, 16, 234, 196, 195, 27, 174, 233, 64, 212, 241, 148, 145, 113, 232, 200, 13, 83, 75, 134, 206, 224, 103, 127, 110, 96, 1, 215, 114, 87, 153, 44, 113, 67, 37, 146, 203, 110, 224, 100, 188, 22, 200, 51, 122, 170, 177, 196, 35, 215, 49, 205, 212, 143, 70, 7, 57, 101, 220, 200, 101, 131, 182, 132, 145, 104, 248, 150, 205, 209, 22, 181, 8, 247, 180, 21, 18, 227, 221, 195, 37, 235, 53, 30, 97, 24, 51, 212, 78, 249, 125, 28, 16, 23, 44, 133, 162, 70, 208, 164, 59, 185, 242, 155, 185, 94, 205, 193, 230, 118, 210, 193, 123, 66, 130, 122, 108, 69, 178, 210, 109, 233, 17, 188, 179, 105, 197, 87, 31, 150, 104, 24, 6, 171, 6, 6, 36, 104, 67, 154, 98, 145, 192, 10, 218, 44, 9, 14, 5, 50, 255, 115, 55, 214, 57, 168, 91, 179, 174, 133, 27, 50, 224, 155, 37, 38, 138, 130, 57, 228, 134, 193, 193, 17, 219, 56, 206, 64, 177, 139, 38, 219, 111, 114, 216, 171, 151, 94, 208, 70, 251, 30, 42, 207, 129, 23, 80, 246, 133, 86, 230, 226, 204, 182, 89, 31, 115, 66, 28, 104, 181, 46, 173, 14, 128, 46, 149, 244, 126, 187, 195, 14, 46, 32, 25, 116, 189, 238, 31, 41, 117, 100, 200, 4, 124, 169, 157, 4, 101, 53, 249, 89, 254, 196, 30, 28, 79, 81, 180, 89, 210, 64, 35, 124, 77, 81, 202, 115, 116, 121, 95, 94, 40, 18, 94, 224, 35, 9, 255, 177, 117, 172, 101, 183, 27, 14, 29, 1, 158, 110, 0, 129, 30, 11, 175, 31, 114, 234, 119, 166, 153, 111, 61, 162, 69, 228, 171, 143, 1, 43, 203, 72, 235, 245, 178, 240, 230, 1, 111, 233, 232, 146, 144, 174, 175, 110, 214, 35, 88, 116, 177, 95, 188, 104, 66, 115, 253, 93, 151, 107, 141, 138, 2, 20, 96, 83, 29, 180, 120, 207, 204, 84, 132, 135, 100, 163, 129, 142, 100, 133, 241, 82, 210, 33, 34, 190, 44, 163, 60, 232, 204, 40, 110, 149, 177, 233, 58, 136, 136, 169, 254, 223, 32, 44, 100, 34, 139, 196, 159, 189, 255, 76, 201, 164, 217, 52, 78, 243, 105, 19, 77, 81, 172, 243, 70, 242, 186, 85, 221, 46, 247, 149, 141, 0, 0, 38, 227, 27, 110, 187, 137, 83, 158, 179, 65, 90, 164, 18, 187, 118, 192, 39, 65, 47, 110, 148, 202, 3, 88, 183, 111, 21, 248, 4, 47, 112, 169, 130, 24, 85, 147, 189, 111, 85, 236, 161, 49, 167, 219, 102, 96, 141, 195, 136, 90, 176, 221, 137, 240, 80, 107, 21, 5, 38, 44, 245, 42, 19, 198, 104, 46, 200, 207, 1, 114, 216, 102, 240, 62, 149, 28, 37, 238, 236, 56, 189, 43, 93, 204, 67, 15, 143, 153, 197, 92, 10, 192, 224, 77, 64, 154, 53, 230, 148, 221, 76, 113, 67, 86, 138, 251, 27, 12, 71, 64, 238, 242, 57, 171, 67, 201, 49, 164, 226, 196, 155, 44, 34, 200, 252, 119, 85, 202, 210, 124, 250, 216, 27, 207, 189, 123, 161, 152, 53, 217, 160, 97, 55, 91, 225, 227, 91, 129, 43, 223, 220, 88, 223, 219, 171, 192, 43, 164, 201, 162, 239, 117, 252, 57, 143, 196, 69, 86, 8, 133, 42, 108, 140, 194, 12, 246, 112, 169, 93, 193, 116, 224, 70, 216, 159, 16, 14, 194, 176, 148, 223, 109, 124, 44, 181, 170, 251, 8, 32, 165, 12, 0, 68, 147, 85, 151, 253, 156, 251, 52, 191, 117, 124, 230, 44, 147, 148, 26, 158, 89, 73, 114, 45, 32, 23, 154, 36, 105, 99, 141, 3, 11, 119, 99, 5, 138, 193, 42, 112, 20, 129, 56, 81, 215, 85, 62, 115, 67, 223, 113, 168, 225, 253, 64, 160, 218, 68, 213, 116, 186, 15, 57, 47, 21, 52, 21, 246, 110, 169, 179, 169, 84, 124, 83, 105, 224, 96, 52, 160, 161, 168, 87, 233, 233, 230, 64, 6, 61, 247, 64, 112, 191, 176, 159, 158, 194, 214, 61, 5, 226, 39, 236, 134, 178, 217, 192, 65, 15, 240, 246, 113, 235, 92, 5, 45, 162, 145, 53, 178, 30, 113, 58, 252, 213, 66, 96, 235, 253, 205, 185, 62, 205, 75, 191, 220, 101, 149, 245, 167, 150, 79, 140, 219, 241, 154, 84, 146, 242, 87, 91, 233, 246, 78, 250, 233, 224, 235, 94, 100, 208, 194, 222, 61, 147, 68, 51, 18, 162, 17, 204, 100, 212, 251, 19, 217, 157, 134, 151, 116, 19, 54, 128, 123, 200, 59, 27, 109, 199, 213, 149, 97, 129, 96, 115, 6, 92, 114, 251, 20, 53, 104, 64, 16, 110, 202, 11, 7, 231, 241, 30, 2, 141, 203, 167, 133, 78, 159, 67, 29, 97, 44, 193, 45, 202, 8, 240, 76, 112, 115, 160, 97, 178, 217, 54, 41, 21, 161, 80, 42, 12, 218, 153, 220, 71, 240, 19, 197, 162, 81, 193, 21, 237, 233, 53, 16, 105, 172, 17, 63, 45, 247, 86, 247, 4, 16, 69, 212, 123, 61, 161, 223, 247, 20, 234, 53, 4, 24, 174, 63, 80, 204, 22, 108, 160, 242, 105, 203, 182, 235, 64, 48, 60, 43, 61, 104, 8, 146, 24, 86, 166, 183, 140, 28, 210, 213, 31, 165, 252, 8, 231, 58, 234, 145, 170, 104, 246, 195, 118, 173, 245, 78, 214, 44, 112, 129, 239, 241, 210, 203, 58, 23, 175, 68, 68, 52, 67, 150, 166, 128, 121, 236, 48, 92, 145, 154, 214, 154, 82, 80, 47, 36, 8, 224, 71, 187, 78, 91, 146, 74, 245, 219, 101, 185, 151, 0, 9, 89, 54, 69, 199, 72, 11, 110, 85, 57, 194, 211, 105, 214, 167, 54, 192, 90, 201, 79, 89, 202, 149, 238, 65, 16, 204, 156, 39, 54, 240, 28, 116, 115, 182, 205, 68, 97, 57, 246, 126, 198, 63, 239, 39, 228, 221, 42, 244, 182, 148, 24, 187, 194, 98, 37, 135, 109, 51, 247, 63, 38, 16, 197, 155, 169, 216, 221, 163, 83, 224, 217, 84, 233, 124, 192, 11, 99, 21, 178, 89, 104, 226, 195, 205, 191, 81, 19, 110, 59, 236, 161, 244, 122, 40, 143, 190, 221, 85, 144, 118, 17, 210, 112, 100, 119, 127, 167, 196, 126, 204, 107, 175, 204, 147, 16, 74, 93, 173, 49, 223, 193, 132, 201, 37, 216, 23, 176, 61, 107, 50, 150, 106, 140, 201, 211, 35, 44, 120, 158, 250, 85, 26, 152, 213, 156, 158, 112, 163, 11, 216, 178, 190, 223, 47, 174, 190, 183, 68, 136, 31, 72, 82, 220, 144, 112, 46, 80, 29, 219, 107, 77, 107, 65, 11, 84, 15, 33, 157, 195, 59, 250, 202, 17, 102, 203, 196, 136, 28, 86, 58, 26, 47, 231, 119, 167, 126, 203, 58, 96, 215, 213, 173, 71, 198, 130, 234, 43, 36, 180, 58, 247, 168, 24, 52, 17, 30, 105, 232, 103, 154, 249, 194, 52, 129, 136, 99, 217, 133, 223, 16, 68, 129, 123, 192, 163, 165, 156, 19, 77, 182, 1, 121, 195, 239, 40, 72, 249, 143, 152, 30, 82, 16, 205, 167, 234, 29, 68, 162, 18, 68, 58, 253, 90, 222, 197, 56, 32, 222, 226, 214, 203, 67, 62, 68, 239, 254, 88, 60, 1, 41, 102, 42, 98, 211, 17, 138, 109, 150, 58, 55, 173, 142, 49, 135, 31, 59, 64, 42, 97, 20, 30, 54, 255, 108, 86, 87, 111, 239, 192, 98, 0, 67, 28, 251, 114, 4, 80, 93, 183, 188, 89, 68, 85, 8, 142, 120, 8, 167, 213, 143, 53, 229, 93, 164, 51, 152, 208, 161, 64, 38, 91, 103, 86, 66, 254, 164, 240, 106, 143, 36, 116, 100, 64, 4, 107, 9, 156, 78, 45, 123, 74, 51, 13, 19, 80, 31, 75, 183, 166, 68, 111, 59, 231, 85, 178, 125, 164, 157, 92, 9, 236, 102, 172, 134, 152, 195, 242, 98, 238, 31, 102, 53, 244, 25, 128, 71, 46, 206, 123, 216, 180, 100, 42, 85, 155, 61, 13, 24, 193, 245, 207, 131, 134, 224, 201, 79, 99, 60, 234, 79, 228, 34, 66, 85, 73, 183, 71, 198, 40, 138, 116, 175, 64, 209, 211, 7, 23, 106, 1, 69, 150, 72, 96, 206, 244, 228, 241, 32, 103, 41, 236, 163, 7, 95, 60, 131, 40, 240, 217, 116, 109, 201, 21, 194, 102, 96, 103, 61, 92, 109, 93, 249, 23, 104, 254, 46, 206, 203, 136, 129, 70, 149, 77, 150, 64, 137, 129, 160, 103, 183, 0, 44, 93, 158, 126, 16, 109, 27, 88, 139, 247, 38, 45, 166, 225, 96, 183, 114, 70, 232, 237, 176, 29, 51, 207, 81, 201, 169, 72, 124, 10, 110, 175, 96, 62, 143, 5, 173, 225, 138, 247, 202, 98, 72, 125, 198, 63, 206, 19, 208, 121, 95, 109, 66, 231, 174, 208, 20, 220, 242, 192, 11, 42, 217, 86, 229, 201, 64, 103, 127, 52, 20, 161, 200, 252, 97, 165, 116, 16, 238, 182, 30, 240, 178, 1, 131, 217, 235, 217, 152, 96, 216, 193, 153, 197, 245, 71, 79, 225, 58, 26, 151, 107, 107, 110, 202, 42, 110, 253, 62, 222, 211, 57, 49, 81, 227, 213, 194, 84, 15, 86, 175, 126, 56, 180, 252, 193, 183, 89, 45, 242, 143, 152, 1, 204, 97, 251, 67, 196, 103, 167, 3, 80, 185, 216, 25, 52, 42, 32, 86, 242, 165, 144, 231, 130, 166, 137, 120, 219, 26, 34, 61, 221, 31, 229, 244, 214, 65, 221, 203, 147, 192, 22, 71, 193, 173, 132, 80, 232, 244, 248, 43, 226, 32, 24, 22, 207, 252, 18, 89, 167, 191, 219, 190, 183, 252, 198, 127, 93, 59, 148, 136, 33, 195, 204, 56, 74, 25, 65, 34, 111, 7, 174, 168, 78, 100, 169, 107, 207, 204, 1, 68, 67, 216, 87, 15, 198, 155, 163, 56, 244, 105, 10, 37, 97, 16, 31, 30, 250, 128, 112, 228, 52, 224, 183, 174, 80, 73, 204, 43, 143, 95, 233, 126, 8, 227, 76, 124, 115, 243, 166, 139, 107, 48, 247, 146, 166, 37, 53, 214, 174, 162, 102, 38, 54, 58, 44, 140, 60, 115, 148, 109, 105, 183, 25, 205, 12, 55, 220, 100, 89, 222, 150, 113, 82, 159, 48, 140, 22, 81, 49, 92, 140, 67, 225, 249, 20, 233, 147, 231, 54, 27, 85, 168, 108, 229, 156, 6, 71, 175, 158, 221, 153, 61, 126, 104, 128, 199, 224, 110, 203, 134, 203, 138, 54, 129, 89, 11, 111, 185, 216, 255, 165, 40, 169, 98, 77, 38, 7, 121, 236, 251, 99, 117, 254, 89, 2, 27, 193, 156, 131, 169, 85, 96, 79, 73, 161, 254, 102, 17, 146, 170, 164, 246, 30, 207, 235, 44, 81, 40, 48, 10, 201, 37, 229, 32, 73, 51, 144, 56, 149, 6, 45, 27, 6, 89, 13, 244, 86, 64, 50, 174, 80, 108, 24, 222, 91, 149, 168, 103, 52, 171, 86, 239, 248, 108, 52, 43, 211, 72, 94, 166, 133, 227, 133, 143, 64, 29, 21, 160, 72, 199, 104, 236, 112, 132, 188, 108, 89, 230, 190, 129, 226, 64, 39, 67, 35, 25, 235, 48, 62, 131, 80, 8, 203, 35, 159, 174, 246, 55, 110, 38, 81, 22, 240, 80, 128, 1, 113, 37, 239, 48, 135, 147, 139, 42, 37, 72, 95, 184, 71, 148, 212, 98, 12, 191, 75, 84, 245, 229, 127, 187, 187, 102, 169, 217, 190, 32, 136, 138, 71, 129, 244, 67, 74, 13, 95, 40, 178, 112, 137, 15, 96, 175, 24, 70, 129, 205, 181, 252, 46, 25, 215, 248, 123, 148, 220, 66, 249, 46, 171, 99, 106, 193, 143, 63, 66, 169, 92, 163, 89, 41, 194, 41, 15, 104, 240, 102, 12, 109, 180, 183, 232, 53, 246, 123, 139, 79, 164, 246, 182, 72, 128, 123, 196, 108, 57, 103, 242, 146, 52, 202, 71, 106, 90, 65, 48, 158, 17, 12, 1, 50, 138, 182, 59, 7, 11, 157, 16, 123, 144, 184, 183, 79, 223, 249, 109, 134, 33, 23, 24, 129, 218, 149, 89, 68, 83, 24, 25, 60, 160, 124, 203, 234, 240, 139, 253, 119, 166, 182, 50, 17, 151, 118, 193, 64, 199, 101, 66, 251, 169, 157, 11, 111, 135, 38, 200, 211, 202, 95, 37, 11, 126, 220, 67, 46, 103, 152, 194, 54, 98, 205, 219, 100, 30, 151, 162, 50, 52, 95, 126, 161, 229, 139, 179, 91, 155, 26, 228, 87, 192, 242, 162, 30, 2, 165, 155, 26, 15, 239, 50, 219, 250, 117, 224, 213, 132, 213, 41, 49, 177, 43, 58, 148, 137, 10, 144, 155, 191, 192, 137, 30, 232, 164, 222, 255, 60, 216, 65, 225, 153, 172, 114, 28, 108, 129, 25, 165, 76, 241, 201, 234, 51, 183, 174, 250, 98, 18, 240, 211, 53, 169, 190, 61, 191, 8, 237, 123, 74, 43, 175, 58, 240, 17, 36, 183, 133, 7, 20, 218, 155, 237, 10, 157, 86, 120, 35, 101, 154, 180, 71, 66, 23, 201, 151, 221, 207, 183, 206, 124, 112, 155, 226, 142, 172, 17, 25, 27, 69, 110, 143, 233, 180, 162, 165, 158, 151, 93, 220, 102, 36, 160, 164, 192, 239, 177, 250, 66, 161, 235, 13, 99, 245, 67, 158, 221, 142, 99, 243, 52, 170, 165, 193, 225, 30, 69, 35, 121, 239, 127, 185, 129, 244, 192, 103, 87, 16, 166, 119, 95, 185, 95, 92, 142, 87, 76, 23, 4, 135, 55, 194, 3, 54, 230, 146, 172, 37, 215, 79, 50, 65, 73, 228, 44, 10, 208, 237, 0, 118, 43, 12, 237, 59, 35, 41, 50, 244, 207, 122, 120, 66, 212, 190, 202, 201, 10, 180, 30, 228, 69, 121, 44, 155, 22, 102, 15, 53, 247, 197, 236, 21, 171, 133, 71, 129, 33, 127, 130, 170, 25, 91, 161, 39, 137, 157, 167, 133, 212, 235, 241, 229, 150, 199, 200, 166, 105, 205, 102, 94, 16, 223, 33, 79, 33, 244, 200, 249, 208, 80, 100, 98, 143, 208, 108, 152, 106, 229, 81, 25, 191, 155, 25, 73, 230, 137, 42, 93, 231, 157, 115, 120, 251, 209, 211, 166, 177, 105, 6, 111, 178, 208, 101, 25, 66, 177, 81, 113, 36, 135, 63, 34, 111, 140, 73, 112, 162, 196, 170, 78, 182, 43, 160, 97, 11, 130, 150, 98, 127, 40, 223, 77, 41, 14, 104, 97, 95, 37, 229, 22, 157, 211, 94, 23, 58, 7, 163, 142, 95, 52, 172, 122, 69, 233, 19, 43, 69, 114, 200, 20, 208, 144, 171, 0, 81, 123, 119, 29, 6, 178, 19, 226, 220, 92, 27, 110, 7, 2, 102, 214, 20, 200, 226, 126, 10, 23, 97, 207, 182, 60, 125, 211, 82, 176, 89, 120, 189, 109, 135, 116, 26, 164, 61, 18, 246, 33, 85, 109, 84, 29, 19, 172, 92, 136, 174, 54, 51, 26, 214, 215, 107, 249, 108, 183, 225, 234, 100, 126, 242, 59, 76, 208, 2, 107, 78, 251, 178, 119, 79, 146, 83, 198, 70, 147, 218, 95, 3, 22, 240, 66, 108, 121, 88, 242, 6, 80, 38, 133, 220, 14, 28, 166, 13, 167, 209, 3, 110, 28, 248, 156, 55, 168, 7, 219, 24, 216, 112, 88, 224, 196, 208, 70, 236, 211, 213, 40, 20, 72, 191, 120, 134, 86, 229, 165, 23, 191, 232, 213, 191, 174, 60, 12, 175, 247, 101, 121, 183, 152, 233, 73, 179, 91, 190, 116, 243, 69, 122, 215, 9, 227, 58, 98, 46, 68, 96, 145, 129, 154, 38, 199, 164, 240, 146, 91, 93, 65, 88, 149, 117, 170, 240, 191, 206, 114, 129, 115, 78, 243, 226, 210, 199, 193, 70, 50, 179, 5, 84, 217, 199, 157, 163, 95, 14, 73, 148, 101, 68, 90, 145, 246, 246, 51, 164, 51, 16, 126, 10, 46, 211, 115, 210, 110, 4, 212, 248, 18, 2, 71, 89, 25, 163, 170, 157, 91, 211, 7, 219, 226, 94, 135, 105, 65, 98, 156, 37, 47, 80, 218, 149, 181, 86, 141, 26, 61, 91, 217, 36, 67, 223, 232, 109, 43, 18, 154, 10, 231, 28, 6, 105, 209, 59, 149, 83, 3, 67, 186, 112, 200, 255, 251, 245, 77, 195, 53, 230, 138, 215, 245, 20, 68, 113, 177, 47, 124, 9, 29, 73, 151, 105, 191, 34, 244, 217, 48, 225, 121, 134, 254, 161, 59, 187, 120, 163, 208, 141, 94, 216, 12, 2, 20, 217, 79, 153, 78, 171, 243, 18, 8, 51, 213, 165, 81, 3, 224, 103, 126, 126, 244, 138, 183, 1, 211, 177, 236, 112, 50, 148, 165, 235, 171, 109, 14, 175, 241, 189, 125, 126, 73, 3, 85, 54, 53, 137, 59, 4, 58, 168, 248, 210, 63, 173, 193, 199, 175, 32, 254, 57, 42, 189, 57, 205, 95, 122, 11, 142, 108, 132, 108, 255, 52, 27, 151, 100, 245, 115, 161, 7, 119, 180, 162, 190, 2, 45, 237, 150, 55, 147, 1, 215, 240, 111, 170, 145, 29, 47, 205, 201, 220, 7, 173, 252, 1, 215, 38, 218, 162, 78, 242, 213, 179, 23, 110, 158, 91, 150, 214, 37, 18, 232, 245, 219, 86, 88, 178, 202, 54, 96, 126, 55, 129, 115, 170, 46, 98, 165, 219, 11, 176, 173, 253, 236, 168, 7, 245, 207, 130, 68, 68, 116, 188, 85, 39, 69, 114, 109, 207, 232, 158, 194, 250, 154, 249, 77, 214, 34, 45, 229, 128, 22, 34, 241, 127, 173, 108, 126, 39, 143, 125, 66, 39, 170, 221, 170, 149, 58, 183, 51, 79, 179, 199, 56, 145, 233, 143, 86, 178, 110, 20, 14, 101, 206, 125, 123, 238, 110, 27, 169, 63, 139, 237, 192, 61, 236, 226, 15, 50, 241, 199, 116, 110, 41, 157, 198, 217, 62, 75, 126, 166, 108, 47, 131, 203, 216, 166, 56, 80, 113, 61, 144, 2, 128, 94, 59, 148, 136, 123, 56, 178, 201, 138, 118, 237, 243, 181, 189, 130, 115, 219, 239, 125, 152, 40, 213, 126, 61, 107, 1, 58, 84, 175, 185, 131, 87, 175, 137, 38, 164, 254, 2, 48, 228, 197, 137, 117, 251, 13, 44, 59, 11, 77, 36, 187, 84, 52, 65, 217, 79, 174, 63, 66, 111, 228, 42, 115, 51, 89, 24, 235, 16, 242, 230, 86, 115, 140, 122, 12, 253, 1, 122, 5, 242, 86, 153, 192, 135, 190, 132, 103, 100, 184, 178, 234, 255, 57, 23, 80, 238, 89, 20, 67, 173, 75, 48, 164, 7, 119, 215, 217, 46, 237, 182, 65, 146, 85, 195, 130, 153, 25, 202, 123, 99, 118, 81, 89, 174, 248, 24, 222, 34, 208, 107, 240, 211, 0, 130, 150, 81, 145, 34, 192, 171, 155, 4, 204, 77, 128, 101, 73, 242, 247, 145, 16, 204, 104, 249, 22, 17, 250, 61, 139, 38, 249, 219, 156, 242, 90, 216, 247, 10, 223, 95, 247, 58, 69, 148, 137, 151, 181, 98, 178, 128, 160, 68, 145, 23, 34, 117, 116, 21, 212, 97, 226, 24, 166, 210, 98, 44, 129, 96, 73, 19, 116, 49, 66, 233, 214, 78, 44, 56, 203, 183, 104, 86, 216, 185, 180, 93, 242, 228, 184, 171, 139, 218, 209, 138, 179, 152, 74, 66, 71, 149, 79, 235, 227, 213, 168, 65, 72, 194, 103, 148, 230, 18, 41, 72, 77, 185, 201, 85, 203, 141, 99, 5, 193, 194, 218, 81, 48, 203, 1, 252, 78, 177, 138, 59, 190, 94, 210, 188, 45, 4, 236, 130, 102, 89, 81, 215, 188, 68, 57, 35, 250, 3, 246, 206, 71, 124, 193, 34, 232, 40, 67, 10, 117, 170, 124, 208, 211, 214, 35, 14, 99, 210, 10, 98, 167, 255, 179, 180, 7, 184, 171, 132, 212, 145, 12, 14, 123, 166, 55, 205, 150, 163, 247, 231, 59, 169, 197, 206, 32, 83, 63, 228, 222, 5, 238, 116, 226, 109, 201, 181, 47, 252, 234, 47, 47, 226, 62, 185, 235, 122, 187, 94, 28, 135, 24, 66, 30, 67, 90, 25, 197, 205, 235, 11, 41, 239, 182, 98, 137, 236, 215, 244, 71, 25, 189, 15, 103, 92, 109, 66, 169, 141, 103, 110, 101, 73, 108, 189, 208, 225, 24, 140, 68, 242, 253, 197, 131, 71, 156, 129, 7, 42, 75, 204, 222, 205, 113, 244, 246, 135, 22, 171, 15, 255, 79, 162, 34, 118, 132, 244, 170, 221, 38, 148, 241, 169, 51, 88, 198, 153, 194, 14, 209, 20, 95, 178, 201, 74, 237, 122, 235, 2, 25, 133, 29, 202, 215, 97, 116, 215, 186, 141, 19, 130, 162, 52, 243, 36, 169, 191, 217, 135, 211, 110, 230, 255, 175, 175, 3, 98, 222, 222, 18, 222, 61, 59, 17, 119, 232, 28, 61, 250, 90, 152, 10, 36, 37, 60, 107, 102, 1, 59, 7, 184, 169, 92, 120, 18, 229, 109, 187, 29, 223, 181, 49, 94, 151, 24, 180, 135, 172, 34, 158, 180, 128, 62, 81, 145, 58, 1, 214, 240, 157, 140, 238, 219, 123, 185, 51, 56, 104, 44, 167, 146, 205, 14, 196, 72, 147, 160, 80, 190, 190, 166, 242, 237, 10, 163, 111, 248, 182, 8, 36, 77, 160, 5, 248, 242, 94, 231, 124, 152, 78, 125, 90, 202, 214, 220, 161, 23, 161, 182, 145, 150, 204, 61, 8, 25, 229, 10, 70, 158, 151, 235, 61, 25, 83, 89, 31, 95, 190, 37, 141, 236, 171, 40, 185, 106, 122, 55, 79, 134, 248, 64, 42, 237, 222, 91, 136, 86, 179, 156, 183, 253, 87, 120, 140, 248, 23, 1, 230, 83, 194, 131, 129, 11, 66, 41, 187, 79, 190, 99, 114, 183, 45, 148, 108, 188, 15, 160, 141, 232, 236, 39, 88, 224, 159, 31, 135, 204, 200, 92, 52, 105, 82, 113, 231, 115, 76, 60, 66, 153, 99, 101, 122, 249, 132, 65, 33, 222, 30, 162, 38, 73, 99, 139, 97, 100, 11, 133, 159, 67, 113, 102, 242, 62, 109, 201, 18, 101, 104, 225, 72, 61, 53, 147, 63, 93, 51, 0, 200, 214, 89, 50, 120, 47, 60, 225, 152, 199, 111, 164, 129, 192, 211, 38, 99, 232, 151, 86, 109, 184, 202, 159, 240, 222, 159, 210, 204, 181, 89, 29, 42, 169, 22, 199, 95, 166, 209, 112, 114, 96, 4, 250, 211, 70, 105, 166, 106, 87, 228, 104, 35, 83, 180, 111, 72, 46, 40, 116, 244, 189, 240, 110, 56, 243, 47, 229, 181, 146, 57, 220, 189, 92, 128, 246, 23, 7, 118, 234, 254, 88, 61, 247, 100, 242, 71, 66, 83, 227, 243, 57, 170, 104, 73, 208, 188, 149, 46, 71, 96, 9, 13, 142, 147, 253, 84, 49, 92, 55, 164, 153, 67, 80, 142, 74, 188, 58, 175, 124, 231, 84, 35, 154, 153, 26, 220, 196, 105, 211, 238, 16, 47, 8, 40, 236, 97, 80, 197, 201, 165, 121, 65, 41, 60, 172, 187, 98, 246, 62, 190, 207, 185, 229, 106, 184, 108, 148, 194, 8, 239, 112, 217, 144, 2, 158, 244, 138, 99, 79, 91, 73, 120, 11, 20, 250, 149, 78, 231, 218, 112, 209, 186, 149, 178, 204, 29, 9, 12, 95, 189, 141, 13, 129, 26, 4, 82, 215, 51, 63, 15, 165, 167, 205, 113, 129, 201, 107, 152, 101, 201, 185, 152, 200, 170, 220, 223, 220, 54, 72, 225, 71, 98, 155, 123, 184, 148, 244, 57, 241, 122, 223, 236, 50, 126, 245, 100, 183, 230, 138, 122, 171, 247, 164, 46, 131, 134, 250, 3, 139, 8, 53, 81, 136, 76, 68, 133, 203, 0, 199, 34, 188, 133, 136, 161, 223, 71, 144, 115, 170, 103, 133, 27, 91, 48, 122, 148, 84, 58, 255, 137, 30, 211, 89, 132, 36, 60, 56, 24, 75, 225, 73, 166, 131, 85, 105, 30, 162, 134, 216, 44, 208, 42, 19, 228, 119, 75, 86, 179, 231, 103, 28, 156, 216, 80, 48, 142, 1, 156, 172, 29, 97, 183, 106, 27, 53, 32, 239, 78, 143, 2, 3, 221, 250, 163, 46, 151, 234, 75, 42, 2, 235, 199, 243, 48, 58, 98, 92, 129, 212, 93, 56, 156, 80, 243, 200, 68, 227, 119, 51, 176, 151, 159, 175, 161, 20, 178, 183, 250, 54, 128, 89, 81, 168, 220, 104, 200, 230, 255, 157, 69, 163, 148, 18, 245, 147, 17, 15, 36, 70, 210, 254, 117, 33, 134, 115, 55, 211, 3, 187, 47, 148, 194, 154, 231, 135, 210, 178, 92, 176, 253, 103, 26, 149, 56, 224, 206, 30, 98, 254, 191, 171, 120, 106, 1, 235, 136, 134, 59, 208, 162, 46, 133, 223, 138, 71, 133, 94, 234, 90, 209, 166, 168, 141, 232, 155, 98, 116, 64, 131, 103, 61, 89, 117, 155, 172, 174, 152, 10, 52, 206, 204, 208, 136, 192, 145, 229, 73, 121, 128, 237, 153, 72, 252, 246, 52, 224, 19, 65, 18, 234, 213, 158, 138, 158, 182, 57, 81, 143, 217, 246, 25, 20, 40, 204, 147, 6, 113, 39, 129, 197, 150, 24, 150, 109, 31, 0, 13, 110, 114, 4, 128, 164, 13, 107, 160, 163, 216, 6, 111, 106, 246, 19, 61, 207, 208, 72, 191, 46, 212, 44, 33, 191, 222, 120, 116, 196, 92, 129, 205, 176, 243, 143, 9, 32, 220, 52, 3, 155, 80, 174, 89, 108, 165, 176, 123, 133, 127, 181, 116, 199, 19, 176, 209, 10, 149, 130, 135, 142, 9, 248, 182, 229, 190, 195, 198, 155, 46, 205, 162, 42, 27, 211, 10, 160, 91, 106, 108, 229, 71, 211, 33, 168, 15, 145, 243, 23, 127, 103, 120, 212, 138, 27, 251, 22, 94, 180, 168, 14, 206, 56, 170, 17, 59, 210, 181, 77, 103, 12, 177, 47, 9, 135, 120, 15, 171, 119, 79, 24, 46, 19, 178, 143, 60, 90, 54, 168, 227, 44, 134, 144, 88, 44, 203, 36, 59, 102, 5, 172, 84, 232, 252, 2, 13, 39, 206, 186, 236, 111, 93, 106, 6, 244, 84, 253, 211, 7, 79, 17, 168, 219, 120, 95, 1, 93, 62, 163, 203, 26, 244, 123, 142, 31, 100, 4, 43, 250, 222, 180, 2, 47, 133, 123, 106, 40, 20, 196, 6, 175, 91, 163, 153, 53, 78, 45, 9, 221, 163, 64, 153, 110, 214, 57, 65, 104, 142, 243, 187, 86, 160, 45, 11, 237, 125, 116, 245, 136, 163, 35, 33, 229, 254, 64, 253, 249, 247, 131, 139, 40, 80, 100, 71, 164, 198, 23, 74, 34, 247, 152, 47, 202, 33, 1, 26, 50, 54, 182, 158, 95, 127, 245, 250, 84, 78, 147, 32, 193, 247, 170, 49, 0, 224, 75, 94, 73, 214, 196, 3, 75, 48, 196, 127, 43, 100, 242, 21, 3, 195, 63, 218, 201, 95, 202, 195, 75, 96, 107, 53, 250, 15, 127, 195, 76, 164, 7, 244, 153, 209, 140, 49, 30, 113, 17, 13, 181, 187, 203, 18, 209, 104, 81, 53, 2, 152, 138, 116, 228, 228, 128, 235, 16, 108, 225, 222, 7, 84, 128, 245, 151, 168, 189] diff --git a/crates/core/src/archiver.rs b/crates/core/src/archiver.rs index 33e0b1e3..4df183b5 100644 --- a/crates/core/src/archiver.rs +++ b/crates/core/src/archiver.rs @@ -3,11 +3,10 @@ pub(crate) mod parent; pub(crate) mod tree; pub(crate) mod tree_archiver; -use std::path::{Path, PathBuf}; - use chrono::Local; use log::warn; use pariter::{IteratorExt, scope}; +use typed_path::{UnixPath, UnixPathBuf}; use crate::{ Progress, @@ -126,8 +125,8 @@ impl<'a, BE: DecryptFullBackend, I: ReadGlobalIndex> Archiver<'a, BE, I> { pub fn archive( mut self, src: &R, - backup_path: &Path, - as_path: Option<&PathBuf>, + backup_path: &UnixPath, + as_path: Option<&UnixPathBuf>, skip_identical_parent: bool, no_scan: bool, p: &impl Progress, diff --git a/crates/core/src/archiver/parent.rs b/crates/core/src/archiver/parent.rs index e6d2d44e..99857722 100644 --- a/crates/core/src/archiver/parent.rs +++ b/crates/core/src/archiver/parent.rs @@ -1,7 +1,4 @@ -use std::{ - cmp::Ordering, - ffi::{OsStr, OsString}, -}; +use std::cmp::Ordering; use log::warn; @@ -124,7 +121,7 @@ impl Parent { /// # Returns /// /// The parent node with the given name, or `None` if the parent node is not found. - fn p_node(&mut self, name: &OsStr) -> Option<&Node> { + fn p_node(&mut self, name: &[u8]) -> Option<&Node> { match &self.tree { None => None, Some(tree) => { @@ -132,12 +129,12 @@ impl Parent { loop { match p_nodes.get(self.node_idx) { None => break None, - Some(p_node) => match p_node.name().as_os_str().cmp(name) { - Ordering::Less => self.node_idx += 1, + Some(p_node) => match name.cmp(&p_node.name()) { + Ordering::Greater => self.node_idx += 1, Ordering::Equal => { break Some(p_node); } - Ordering::Greater => { + Ordering::Less => { break None; } }, @@ -161,7 +158,7 @@ impl Parent { /// # Note /// /// TODO: This function does not check whether the given node is a directory. - fn is_parent(&mut self, node: &Node, name: &OsStr) -> ParentResult<&Node> { + fn is_parent(&mut self, node: &Node, name: &[u8]) -> ParentResult<&Node> { // use new variables as the mutable borrow is used later let ignore_ctime = self.ignore_ctime; let ignore_inode = self.ignore_inode; @@ -190,12 +187,7 @@ impl Parent { /// /// * `be` - The backend to read from. /// * `name` - The name of the parent node. - fn set_dir( - &mut self, - be: &impl DecryptReadBackend, - index: &impl ReadGlobalIndex, - name: &OsStr, - ) { + fn set_dir(&mut self, be: &impl DecryptReadBackend, index: &impl ReadGlobalIndex, name: &[u8]) { let tree = self.p_node(name).and_then(|p_node| { p_node.subtree.map_or_else( || { @@ -257,7 +249,7 @@ impl Parent { &mut self, be: &impl DecryptReadBackend, index: &impl ReadGlobalIndex, - item: TreeType, + item: TreeType>, ) -> Result, TreeStackEmptyError> { let result = match item { TreeType::NewTree((path, node, tree)) => { diff --git a/crates/core/src/archiver/tree.rs b/crates/core/src/archiver/tree.rs index cc5c4297..87cf9254 100644 --- a/crates/core/src/archiver/tree.rs +++ b/crates/core/src/archiver/tree.rs @@ -1,9 +1,6 @@ -use std::{ffi::OsString, path::PathBuf}; +use crate::backend::node::{Metadata, Node, NodeType}; -use crate::{ - backend::node::{Metadata, Node, NodeType}, - blob::tree::comp_to_osstr, -}; +use typed_path::{Component, UnixPathBuf}; /// `TreeIterator` turns an Iterator yielding items with paths and Nodes into an /// Iterator which ensures that all subdirectories are visited and closed. @@ -18,7 +15,7 @@ pub(crate) struct TreeIterator { /// The original Iterator. iter: I, /// The current path. - path: PathBuf, + path: UnixPathBuf, /// The current item. item: Option, } @@ -31,7 +28,7 @@ where let item = iter.next(); Self { iter, - path: PathBuf::new(), + path: UnixPathBuf::new(), item, } } @@ -49,32 +46,25 @@ where #[derive(Debug)] pub(crate) enum TreeType { /// New tree to be inserted. - NewTree((PathBuf, Node, U)), + NewTree((UnixPathBuf, Node, U)), /// A pseudo item which indicates that a tree is finished. EndTree, /// Original item. - Other((PathBuf, Node, T)), + Other((UnixPathBuf, Node, T)), } -impl Iterator for TreeIterator<(PathBuf, Node, O), I> +impl Iterator for TreeIterator<(UnixPathBuf, Node, O), I> where - I: Iterator, + I: Iterator, { - type Item = TreeType; + type Item = TreeType>; fn next(&mut self) -> Option { match &self.item { None => { if self.path.pop() { Some(TreeType::EndTree) } else { - // Check if we still have a path prefix open... - match self.path.components().next() { - Some(std::path::Component::Prefix(..)) => { - self.path = PathBuf::new(); - Some(TreeType::EndTree) - } - _ => None, - } + None } } Some((path, node, _)) => { @@ -84,24 +74,25 @@ where Some(TreeType::EndTree) } Ok(missing_dirs) => { - for comp in missing_dirs.components() { - self.path.push(comp); - // process next normal path component - other components are simply ignored - if let Some(p) = comp_to_osstr(comp).ok().flatten() { - if node.is_dir() && path == &self.path { - let (path, node, _) = self.item.take().unwrap(); - self.item = self.iter.next(); - let name = node.name(); - return Some(TreeType::NewTree((path, node, name))); - } - // Use mode 755 for missing dirs, so they can be accessed - let meta = Metadata { - mode: Some(0o755), - ..Default::default() - }; - let node = Node::new_node(&p, NodeType::Dir, meta); - return Some(TreeType::NewTree((self.path.clone(), node, p))); + if let Some(p) = missing_dirs.components().next() { + self.path.push(p); + if node.is_dir() && path == &self.path { + let (path, node, _) = self.item.take().unwrap(); + self.item = self.iter.next(); + let name = node.name().to_vec(); + return Some(TreeType::NewTree((path, node, name))); } + // Use mode 755 for missing dirs, so they can be accessed + let meta = Metadata { + mode: Some(0o755), + ..Default::default() + }; + let node = Node::new_node(p.as_bytes(), NodeType::Dir, meta); + return Some(TreeType::NewTree(( + self.path.clone(), + node, + p.as_bytes().to_vec(), + ))); } // there wasn't any normal path component to process - return current item let item = self.item.take().unwrap(); diff --git a/crates/core/src/archiver/tree_archiver.rs b/crates/core/src/archiver/tree_archiver.rs index a5edbc72..17132492 100644 --- a/crates/core/src/archiver/tree_archiver.rs +++ b/crates/core/src/archiver/tree_archiver.rs @@ -1,7 +1,6 @@ -use std::path::{Path, PathBuf}; - use bytesize::ByteSize; use log::{debug, trace}; +use typed_path::{UnixPath, UnixPathBuf}; use crate::{ archiver::{parent::ParentResult, tree::TreeType}, @@ -30,7 +29,7 @@ pub(crate) struct TreeArchiver<'a, BE: DecryptWriteBackend, I: ReadGlobalIndex> /// The current tree. tree: Tree, /// The stack of trees. - stack: Vec<(PathBuf, Node, ParentResult, Tree)>, + stack: Vec<(UnixPathBuf, Node, ParentResult, Tree)>, /// The index to read from. index: &'a I, /// The packer to write to. @@ -129,7 +128,7 @@ impl<'a, BE: DecryptWriteBackend, I: ReadGlobalIndex> TreeArchiver<'a, BE, I> { /// * `path` - The path of the file. /// * `node` - The node of the file. /// * `parent` - The parent result of the file. - fn add_file(&mut self, path: &Path, node: Node, parent: &ParentResult<()>, size: u64) { + fn add_file(&mut self, path: &UnixPath, node: Node, parent: &ParentResult<()>, size: u64) { let filename = path.join(node.name()); match parent { ParentResult::Matched(()) => { @@ -164,7 +163,11 @@ impl<'a, BE: DecryptWriteBackend, I: ReadGlobalIndex> TreeArchiver<'a, BE, I> { /// # Returns /// /// The id of the tree. - fn backup_tree(&mut self, path: &Path, parent: &ParentResult) -> RusticResult { + fn backup_tree( + &mut self, + path: &UnixPath, + parent: &ParentResult, + ) -> RusticResult { let (chunk, id) = self.tree.serialize().map_err(|err| { RusticError::with_source( ErrorKind::Internal, @@ -224,7 +227,7 @@ impl<'a, BE: DecryptWriteBackend, I: ReadGlobalIndex> TreeArchiver<'a, BE, I> { parent_tree: Option, ) -> RusticResult<(TreeId, SnapshotSummary)> { let parent = parent_tree.map_or(ParentResult::NotFound, ParentResult::Matched); - let id = self.backup_tree(&PathBuf::new(), &parent)?; + let id = self.backup_tree(UnixPath::new(&[]), &parent)?; let stats = self.tree_packer.finalize()?; stats.apply(&mut self.summary, BlobType::Tree); diff --git a/crates/core/src/backend.rs b/crates/core/src/backend.rs index 0be14872..0573ee20 100644 --- a/crates/core/src/backend.rs +++ b/crates/core/src/backend.rs @@ -20,6 +20,7 @@ use log::trace; use mockall::mock; use serde_derive::{Deserialize, Serialize}; +use typed_path::UnixPathBuf; use crate::{ backend::node::{Metadata, Node, NodeType}, @@ -31,8 +32,8 @@ use crate::{ #[derive(thiserror::Error, Debug, displaydoc::Display)] #[non_exhaustive] pub enum BackendErrorKind { - /// Path is not allowed: `{0:?}` - PathNotAllowed(PathBuf), + /// Path is not allowed: `{0}` + PathNotAllowed(String), } pub(crate) type BackendResult = Result; @@ -380,7 +381,7 @@ impl std::fmt::Debug for dyn WriteBackend { #[derive(Debug, Clone)] pub struct ReadSourceEntry { /// The path of the entry. - pub path: PathBuf, + pub path: UnixPathBuf, /// The node information of the entry. pub node: Node, @@ -390,10 +391,11 @@ pub struct ReadSourceEntry { } impl ReadSourceEntry { - fn from_path(path: PathBuf, open: Option) -> BackendResult { + fn from_path(path: UnixPathBuf, open: Option) -> BackendResult { let node = Node::new_node( - path.file_name() - .ok_or_else(|| BackendErrorKind::PathNotAllowed(path.clone()))?, + path.file_name().ok_or_else(|| { + BackendErrorKind::PathNotAllowed(path.to_string_lossy().to_string()) + })?, NodeType::File, Metadata::default(), ); diff --git a/crates/core/src/backend/childstdout.rs b/crates/core/src/backend/childstdout.rs index ef38b587..ca6bd855 100644 --- a/crates/core/src/backend/childstdout.rs +++ b/crates/core/src/backend/childstdout.rs @@ -1,10 +1,11 @@ use std::{ iter::{Once, once}, - path::PathBuf, process::{Child, ChildStdout, Command, Stdio}, sync::Mutex, }; +use typed_path::UnixPathBuf; + use crate::{ backend::{ReadSource, ReadSourceEntry}, error::{ErrorKind, RusticError, RusticResult}, @@ -15,7 +16,7 @@ use crate::{ #[derive(Debug)] pub struct ChildStdoutSource { /// The path of the stdin entry. - path: PathBuf, + path: UnixPathBuf, /// The child process /// /// # Note @@ -30,14 +31,14 @@ pub struct ChildStdoutSource { impl ChildStdoutSource { /// Creates a new `ChildSource`. - pub fn new(cmd: &CommandInput, path: PathBuf) -> RusticResult { + pub fn new(cmd: &CommandInput, path: UnixPathBuf) -> RusticResult { let process = Command::new(cmd.command()) .args(cmd.args()) .stdout(Stdio::piped()) .spawn() .map_err(|err| CommandInputErrorKind::ProcessExecutionFailed { command: cmd.clone(), - path: path.clone(), + path: path.to_string_lossy().to_string(), source: err, }); diff --git a/crates/core/src/backend/ignore.rs b/crates/core/src/backend/ignore.rs index acca3a89..a3b7d44a 100644 --- a/crates/core/src/backend/ignore.rs +++ b/crates/core/src/backend/ignore.rs @@ -610,11 +610,15 @@ fn map_entry( with_atime: bool, ignore_devid: bool, ) -> IgnoreResult> { - let name = entry.file_name(); + use std::os::unix::ffi::OsStrExt; + + use typed_path::{UnixPath, UnixPathBuf}; + + let name = entry.file_name().as_bytes(); let m = entry .metadata() .map_err(|err| IgnoreErrorKind::AcquiringMetadataFailed { - name: name.to_string_lossy().to_string(), + name: String::from_utf8_lossy(name).to_string(), source: err, })?; @@ -684,11 +688,14 @@ fn map_entry( Node::new_node(name, NodeType::Dir, meta) } else if m.is_symlink() { let path = entry.path(); - let target = read_link(path).map_err(|err| IgnoreErrorKind::ErrorLink { - path: path.to_path_buf(), - source: err, - })?; - let node_type = NodeType::from_link(&target); + let target = read_link(path) + .map_err(|err| IgnoreErrorKind::ErrorLink { + path: path.to_path_buf(), + source: err, + })? + .into_os_string(); + let target = target.as_encoded_bytes(); + let node_type = NodeType::from_link(&UnixPath::new(target).to_typed_path()); Node::new_node(name, node_type, meta) } else if filetype.is_block_device() { let node_type = NodeType::Dev { device: m.rdev() }; @@ -705,6 +712,7 @@ fn map_entry( }; let path = entry.into_path(); let open = Some(OpenFile(path.clone())); + let path: UnixPathBuf = path.try_into().unwrap(); // TODO: Error handling Ok(ReadSourceEntry { path, node, open }) } diff --git a/crates/core/src/backend/local_destination.rs b/crates/core/src/backend/local_destination.rs index 5ddbbc5d..68f6b72b 100644 --- a/crates/core/src/backend/local_destination.rs +++ b/crates/core/src/backend/local_destination.rs @@ -23,6 +23,7 @@ use nix::{ fcntl::{AT_FDCWD, AtFlags}, unistd::{Gid, Group, Uid, User, fchownat}, }; +use typed_path::UnixPath; #[cfg(not(windows))] use crate::backend::ignore::mapper::map_mode_from_go; @@ -189,11 +190,20 @@ impl LocalDestination { /// /// * If the destination is a file, this will return the base path. /// * If the destination is a directory, this will return the base path joined with the item. - pub(crate) fn path(&self, item: impl AsRef) -> PathBuf { + #[allow(clippy::unnecessary_wraps)] + pub(crate) fn path(&self, item: impl AsRef) -> LocalDestinationResult { if self.is_file { - self.path.clone() - } else { - self.path.join(item) + return Ok(self.path.clone()); + } + #[cfg(not(windows))] + { + let item = PathBuf::from(item.as_ref()); + Ok(self.path.join(item)) + } + #[cfg(windows)] + { + let item = PathBuf::try_from(item.as_ref())?; + Ok(self.path.join(item)) } } @@ -249,8 +259,8 @@ impl LocalDestination { /// # Notes /// /// This will create the directory structure recursively. - pub(crate) fn create_dir(&self, item: impl AsRef) -> LocalDestinationResult<()> { - let dirname = self.path.join(item); + pub(crate) fn create_dir(&self, item: impl AsRef) -> LocalDestinationResult<()> { + let dirname = self.path(item)?; fs::create_dir_all(dirname).map_err(LocalDestinationErrorKind::DirectoryCreationFailed)?; Ok(()) } @@ -267,10 +277,10 @@ impl LocalDestination { /// * If the times could not be set pub(crate) fn set_times( &self, - item: impl AsRef, + item: impl AsRef, meta: &Metadata, ) -> LocalDestinationResult<()> { - let filename = self.path(item); + let filename = self.path(item)?; if let Some(mtime) = meta.mtime { let atime = meta.atime.unwrap_or(mtime); set_symlink_file_times( @@ -322,10 +332,10 @@ impl LocalDestination { #[allow(clippy::similar_names)] pub(crate) fn set_user_group( &self, - item: impl AsRef, + item: impl AsRef, meta: &Metadata, ) -> LocalDestinationResult<()> { - let filename = self.path(item); + let filename = self.path(item)?; let user = meta.user.clone().and_then(uid_from_name); // use uid from user if valid, else from saved uid (if saved) @@ -375,10 +385,10 @@ impl LocalDestination { #[allow(clippy::similar_names)] pub(crate) fn set_uid_gid( &self, - item: impl AsRef, + item: impl AsRef, meta: &Metadata, ) -> LocalDestinationResult<()> { - let filename = self.path(item); + let filename = self.path(item)?; let uid = meta.uid.map(Uid::from_raw); let gid = meta.gid.map(Gid::from_raw); @@ -423,14 +433,14 @@ impl LocalDestination { #[allow(clippy::similar_names)] pub(crate) fn set_permission( &self, - item: impl AsRef, + item: impl AsRef, node: &Node, ) -> LocalDestinationResult<()> { if node.is_symlink() { return Ok(()); } - let filename = self.path(item); + let filename = self.path(item)?; if let Some(mode) = node.meta.mode { let mode = map_mode_from_go(mode); @@ -456,7 +466,7 @@ impl LocalDestination { #[allow(clippy::unused_self, clippy::unnecessary_wraps)] pub(crate) fn set_extended_attributes( &self, - _item: impl AsRef, + _item: impl AsRef, _extended_attributes: &[ExtendedAttribute], ) -> LocalDestinationResult<()> { Ok(()) @@ -485,10 +495,10 @@ impl LocalDestination { /// * If the extended attributes could not be set. pub(crate) fn set_extended_attributes( &self, - item: impl AsRef, + item: impl AsRef, extended_attributes: &[ExtendedAttribute], ) -> LocalDestinationResult<()> { - let filename = self.path(item); + let filename = self.path(item)?; let mut done = vec![false; extended_attributes.len()]; for curr_name in xattr::list(&filename).map_err(|err| { @@ -497,9 +507,11 @@ impl LocalDestination { path: filename.clone(), } })? { - match extended_attributes.iter().enumerate().find( - |(_, ExtendedAttribute { name, .. })| name == curr_name.to_string_lossy().as_ref(), - ) { + match extended_attributes + .iter() + .enumerate() + .find(|(_, ExtendedAttribute { name, .. })| curr_name.to_string_lossy() == *name) + { Some((index, ExtendedAttribute { name, value })) => { let curr_value = xattr::get(&filename, name).map_err(|err| { LocalDestinationErrorKind::GettingXattrFailed { @@ -564,10 +576,10 @@ impl LocalDestination { /// If it doesn't exist, create a new (empty) one with given length. pub(crate) fn set_length( &self, - item: impl AsRef, + item: impl AsRef, size: u64, ) -> LocalDestinationResult<()> { - let filename = self.path(item); + let filename = self.path(item)?; let dir = filename .parent() .ok_or_else(|| LocalDestinationErrorKind::FileDoesNotHaveParent(filename.clone()))?; @@ -603,7 +615,7 @@ impl LocalDestination { #[allow(clippy::unused_self, clippy::unnecessary_wraps)] pub(crate) fn create_special( &self, - _item: impl AsRef, + _item: impl AsRef, _node: &Node, ) -> LocalDestinationResult<()> { Ok(()) @@ -624,17 +636,18 @@ impl LocalDestination { /// * If the device could not be created. pub(crate) fn create_special( &self, - item: impl AsRef, + item: impl AsRef, node: &Node, ) -> LocalDestinationResult<()> { - let filename = self.path(item); + let filename = self.path(item)?; match &node.node_type { NodeType::Symlink { .. } => { - let linktarget = node.node_type.to_link(); - symlink(linktarget, &filename).map_err(|err| { + let linktarget: PathBuf = + node.node_type.to_link().to_path_buf().try_into().unwrap(); // TODO: Error handling + symlink(linktarget.clone(), &filename).map_err(|err| { LocalDestinationErrorKind::SymlinkingFailed { - linktarget: linktarget.to_path_buf(), + linktarget, filename, source: err, } @@ -721,11 +734,11 @@ impl LocalDestination { /// * If the length of the file could not be read. pub(crate) fn read_at( &self, - item: impl AsRef, + item: impl AsRef, offset: u64, length: u64, ) -> LocalDestinationResult { - let filename = self.path(item); + let filename = self.path(item)?; let mut file = File::open(filename).map_err(LocalDestinationErrorKind::OpeningFileFailed)?; _ = file @@ -757,8 +770,8 @@ impl LocalDestination { /// /// If a file exists and size matches, this returns a `File` open for reading. /// In all other cases, returns `None` - pub fn get_matching_file(&self, item: impl AsRef, size: u64) -> Option { - let filename = self.path(item); + pub fn get_matching_file(&self, item: impl AsRef, size: u64) -> Option { + let filename = self.path(item).ok()?; fs::symlink_metadata(&filename).map_or_else( |_| None, |meta| { @@ -790,11 +803,11 @@ impl LocalDestination { /// This will create the file if it doesn't exist. pub(crate) fn write_at( &self, - item: impl AsRef, + item: impl AsRef, offset: u64, data: &[u8], ) -> LocalDestinationResult<()> { - let filename = self.path(item); + let filename = self.path(item)?; let mut file = OpenOptions::new() .create(true) .truncate(false) diff --git a/crates/core/src/backend/node.rs b/crates/core/src/backend/node.rs index edd3e202..6c16ff3c 100644 --- a/crates/core/src/backend/node.rs +++ b/crates/core/src/backend/node.rs @@ -1,17 +1,9 @@ -use std::{ - cmp::Ordering, - ffi::{OsStr, OsString}, - fmt::Debug, - path::Path, - str::FromStr, -}; +use std::{borrow::Cow, cmp::Ordering, fmt::Debug}; #[cfg(not(windows))] use std::fmt::Write; #[cfg(not(windows))] use std::num::ParseIntError; -#[cfg(not(windows))] -use std::os::unix::ffi::OsStrExt; use chrono::{DateTime, Local}; use derive_more::Constructor; @@ -23,6 +15,7 @@ use serde_with::{ formats::Padded, serde_as, skip_serializing_none, }; +use typed_path::TypedPath; use crate::blob::{DataId, tree::TreeId}; @@ -171,15 +164,14 @@ pub enum NodeType { } impl NodeType { - #[cfg(not(windows))] /// Get a [`NodeType`] from a linktarget path #[must_use] - pub fn from_link(target: &Path) -> Self { + pub fn from_link(target: &TypedPath<'_>) -> Self { let (linktarget, linktarget_raw) = target.to_str().map_or_else( || { ( - target.as_os_str().to_string_lossy().to_string(), - Some(target.as_os_str().as_bytes().to_vec()), + target.to_string_lossy().to_string(), + Some(target.as_bytes().to_vec()), ) }, |t| (t.to_string(), None), @@ -190,57 +182,23 @@ impl NodeType { } } - #[cfg(windows)] - // Windows doesn't support non-unicode link targets, so we assume unicode here. - // TODO: Test and check this! - /// Get a [`NodeType`] from a linktarget path - #[must_use] - pub fn from_link(target: &Path) -> Self { - Self::Symlink { - linktarget: target.as_os_str().to_string_lossy().to_string(), - linktarget_raw: None, - } - } - // Must be only called on NodeType::Symlink! /// Get the link path from a `NodeType::Symlink`. /// /// # Panics /// /// * If called on a non-symlink node - #[cfg(not(windows))] #[must_use] - pub fn to_link(&self) -> &Path { - match self { + pub fn to_link(&self) -> TypedPath<'_> { + TypedPath::derive(match self { Self::Symlink { linktarget, linktarget_raw, - } => linktarget_raw.as_ref().map_or_else( - || Path::new(linktarget), - |t| Path::new(OsStr::from_bytes(t)), - ), - _ => panic!("called method to_link on non-symlink!"), - } - } - - /// Convert a `NodeType::Symlink` to a `Path`. - /// - /// # Warning - /// - /// * Must be only called on `NodeType::Symlink`! - /// - /// # Panics - /// - /// * If called on a non-symlink node - /// * If the link target is not valid unicode - // TODO: Implement non-unicode link targets correctly for windows - #[cfg(windows)] - #[must_use] - pub fn to_link(&self) -> &Path { - match self { - Self::Symlink { linktarget, .. } => Path::new(linktarget), + } => linktarget_raw + .as_ref() + .map_or_else(|| linktarget.as_bytes(), |t| t), _ => panic!("called method to_link on non-symlink!"), - } + }) } } @@ -314,7 +272,7 @@ impl Node { /// /// The created [`Node`] #[must_use] - pub fn new_node(name: &OsStr, node_type: NodeType, meta: Metadata) -> Self { + pub fn new_node(name: &[u8], node_type: NodeType, meta: Metadata) -> Self { Self { name: escape_filename(name), node_type, @@ -360,8 +318,8 @@ impl Node { /// # Panics /// /// * If the name is not valid unicode - pub fn name(&self) -> OsString { - unescape_filename(&self.name).unwrap_or_else(|_| OsString::from_str(&self.name).unwrap()) + pub fn name(&self) -> Cow<'_, [u8]> { + unescape_filename(&self.name).map_or(Cow::Borrowed(self.name.as_bytes()), Cow::Owned) } } @@ -408,8 +366,8 @@ fn unescape_filename(s: &str) -> Result { // stconv.Quote, see https://pkg.go.dev/strconv#Quote // However, so far there was no specification what Quote really does, so this // is some kind of try-and-error and maybe does not cover every case. -fn escape_filename(name: &OsStr) -> String { - let mut input = name.as_bytes(); +fn escape_filename(name: &[u8]) -> String { + let mut input = name; let mut s = String::with_capacity(name.len()); let push = |s: &mut String, p: &str| { @@ -463,7 +421,7 @@ fn escape_filename(name: &OsStr) -> String { /// /// * `s` - The escaped filename // inspired by the enquote crate -fn unescape_filename(s: &str) -> NodeResult<'_, OsString> { +fn unescape_filename(s: &str) -> NodeResult<'_, Vec> { let mut chars = s.chars(); let mut u = Vec::new(); loop { @@ -560,7 +518,7 @@ fn unescape_filename(s: &str) -> NodeResult<'_, OsString> { } } - Ok(OsStr::from_bytes(&u).to_os_string()) + Ok(u) } #[cfg(not(windows))] @@ -581,12 +539,12 @@ mod tests { use proptest::prelude::*; use rstest::rstest; + use typed_path::UnixPath; proptest! { #[test] - fn escape_unescape_is_identity(bytes in prop::collection::vec(prop::num::u8::ANY, 0..65536)) { - let name = OsStr::from_bytes(&bytes); - prop_assert_eq!(name, unescape_filename(&escape_filename(name)).unwrap()); + fn escape_unescape_is_identity(name in prop::collection::vec(prop::num::u8::ANY, 0..65536)) { + prop_assert_eq!(unescape_filename(&escape_filename(&name)).unwrap(), name); } } @@ -608,8 +566,7 @@ mod tests { #[case(b"\xc3\x9f", "\u{00df}")] #[case(b"\xe2\x9d\xa4", "\u{2764}")] #[case(b"\xf0\x9f\x92\xaf", "\u{01f4af}")] - fn escape_cases(#[case] input: &[u8], #[case] expected: &str) { - let name = OsStr::from_bytes(input); + fn escape_cases(#[case] name: &[u8], #[case] expected: &str) { assert_eq!(expected, escape_filename(name)); } @@ -633,16 +590,16 @@ mod tests { #[case(r#"\u2764"#, b"\xe2\x9d\xa4")] #[case(r#"\U0001f4af"#, b"\xf0\x9f\x92\xaf")] fn unescape_cases(#[case] input: &str, #[case] expected: &[u8]) { - let expected = OsStr::from_bytes(expected); assert_eq!(expected, unescape_filename(input).unwrap()); } proptest! { #[test] fn from_link_to_link_is_identity(bytes in prop::collection::vec(prop::num::u8::ANY, 0..65536)) { - let path = Path::new(OsStr::from_bytes(&bytes)); - let node = NodeType::from_link(path); - prop_assert_eq!(path, node.to_link()); - } + let path = TypedPath::Unix(UnixPath::new(&bytes)); + let node = NodeType::from_link(&path); + let link = node.to_link(); + prop_assert_eq!(bytes, link.as_bytes()); + } } } diff --git a/crates/core/src/backend/stdin.rs b/crates/core/src/backend/stdin.rs index 3027bfe9..407d320b 100644 --- a/crates/core/src/backend/stdin.rs +++ b/crates/core/src/backend/stdin.rs @@ -1,9 +1,10 @@ use std::{ io::{Stdin, stdin}, iter::{Once, once}, - path::PathBuf, }; +use typed_path::UnixPathBuf; + use crate::{ backend::{ReadSource, ReadSourceEntry}, error::{ErrorKind, RusticError, RusticResult}, @@ -13,12 +14,12 @@ use crate::{ #[derive(Debug, Clone)] pub struct StdinSource { /// The path of the stdin entry. - path: PathBuf, + path: UnixPathBuf, } impl StdinSource { /// Creates a new `StdinSource`. - pub const fn new(path: PathBuf) -> Self { + pub fn new(path: UnixPathBuf) -> Self { Self { path } } } diff --git a/crates/core/src/blob/tree.rs b/crates/core/src/blob/tree.rs index 2bbda4ab..575c631a 100644 --- a/crates/core/src/blob/tree.rs +++ b/crates/core/src/blob/tree.rs @@ -1,9 +1,9 @@ use std::{ + borrow::Cow, cmp::Ordering, collections::{BTreeMap, BTreeSet, BinaryHeap}, - ffi::{OsStr, OsString}, mem, - path::{Component, Path, PathBuf, Prefix}, + path::PathBuf, str::{self, Utf8Error}, }; @@ -13,6 +13,7 @@ use ignore::Match; use ignore::overrides::{Override, OverrideBuilder}; use serde::{Deserialize, Deserializer}; use serde_derive::Serialize; +use typed_path::{Component, TypedPath, UnixPath, UnixPathBuf, WindowsComponent, WindowsPrefix}; use crate::{ backend::{ @@ -52,8 +53,8 @@ pub(super) mod constants { pub(super) const MAX_TREE_LOADER: usize = 4; } -pub(crate) type TreeStreamItem = RusticResult<(PathBuf, Tree)>; -type NodeStreamItem = RusticResult<(PathBuf, Node)>; +pub(crate) type TreeStreamItem = RusticResult<(UnixPathBuf, Tree)>; +type NodeStreamItem = RusticResult<(UnixPathBuf, Node)>; impl_blobid!(TreeId, BlobType::Tree); #[derive(Default, Serialize, Deserialize, Clone, Debug)] @@ -174,37 +175,27 @@ impl Tree { be: &impl DecryptReadBackend, index: &impl ReadGlobalIndex, id: TreeId, - path: &Path, + path: &UnixPath, ) -> RusticResult { - let mut node = Node::new_node(OsStr::new(""), NodeType::Dir, Metadata::default()); + let mut node = Node::new_node(&[], NodeType::Dir, Metadata::default()); node.subtree = Some(id); for p in path.components() { - if let Some(p) = comp_to_osstr(p).map_err(|err| { - RusticError::with_source( - ErrorKind::Internal, - "Failed to convert Path component `{path}` to OsString.", - err, - ) - .attach_context("path", path.display().to_string()) - .ask_report() - })? { - let id = node.subtree.ok_or_else(|| { - RusticError::new(ErrorKind::Internal, "Node `{node}` is not a directory.") - .attach_context("node", p.to_string_lossy()) + let id = node.subtree.ok_or_else(|| { + RusticError::new(ErrorKind::Internal, "Node `{node}` is not a directory.") + .attach_context("node", String::from_utf8_lossy(p.as_bytes())) + .ask_report() + })?; + let tree = Self::from_backend(be, index, id)?; + node = tree + .nodes + .into_iter() + .find(|node| node.name() == p.as_bytes()) + .ok_or_else(|| { + RusticError::new(ErrorKind::Internal, "Node `{node}` not found in tree.") + .attach_context("node", String::from_utf8_lossy(p.as_bytes())) .ask_report() })?; - let tree = Self::from_backend(be, index, id)?; - node = tree - .nodes - .into_iter() - .find(|node| node.name() == p) - .ok_or_else(|| { - RusticError::new(ErrorKind::Internal, "Node `{node}` not found in tree.") - .attach_context("node", p.to_string_lossy()) - .ask_report() - })?; - } } Ok(node) @@ -214,14 +205,14 @@ impl Tree { be: &impl DecryptReadBackend, index: &impl ReadGlobalIndex, ids: impl IntoIterator, - path: &Path, + path: &UnixPath, ) -> RusticResult { // helper function which is recursively called fn find_node_from_component( be: &impl DecryptReadBackend, index: &impl ReadGlobalIndex, tree_id: TreeId, - path_comp: &[OsString], + path_comp: &[&[u8]], results_cache: &mut [BTreeMap>], nodes: &mut BTreeMap, idx: usize, @@ -246,7 +237,7 @@ impl Tree { ErrorKind::Internal, "Subtree ID not found for node `{node}`", ) - .attach_context("node", path_comp[idx].to_string_lossy()) + .attach_context("node", String::from_utf8_lossy(path_comp[idx])) .ask_report() })?; @@ -267,19 +258,7 @@ impl Tree { Ok(result) } - let path_comp: Vec<_> = path - .components() - .filter_map(|p| comp_to_osstr(p).transpose()) - .collect::>() - .map_err(|err| { - RusticError::with_source( - ErrorKind::Internal, - "Failed to convert Path component `{path}` to OsString.", - err, - ) - .attach_context("path", path.display().to_string()) - .ask_report() - })?; + let path_comp: Vec<_> = path.components().map(|p| p.as_bytes()).collect(); // caching all results let mut results_cache = vec![BTreeMap::new(); path_comp.len()]; @@ -312,19 +291,19 @@ impl Tree { be: &impl DecryptReadBackend, index: &impl ReadGlobalIndex, ids: impl IntoIterator, - matches: &impl Fn(&Path, &Node) -> bool, + matches: &impl Fn(&UnixPath, &Node) -> bool, ) -> RusticResult { // internal state used to save match information in find_matching_nodes #[derive(Default)] struct MatchInternalState { // we cache all results - cache: BTreeMap<(TreeId, PathBuf), Vec<(usize, usize)>>, + cache: BTreeMap<(TreeId, UnixPathBuf), Vec<(usize, usize)>>, nodes: BTreeMap, - paths: BTreeMap, + paths: BTreeMap, } impl MatchInternalState { - fn insert_result(&mut self, path: PathBuf, node: Node) -> (usize, usize) { + fn insert_result(&mut self, path: UnixPathBuf, node: Node) -> (usize, usize) { let new_idx = self.nodes.len(); let node_idx = self.nodes.entry(node).or_insert(new_idx); let new_idx = self.paths.len(); @@ -338,9 +317,9 @@ impl Tree { be: &impl DecryptReadBackend, index: &impl ReadGlobalIndex, tree_id: TreeId, - path: &Path, + path: &UnixPath, state: &mut MatchInternalState, - matches: &impl Fn(&Path, &Node) -> bool, + matches: &impl Fn(&UnixPath, &Node) -> bool, ) -> RusticResult> { let mut result = Vec::new(); if let Some(result) = state.cache.get(&(tree_id, path.to_path_buf())) { @@ -356,7 +335,7 @@ impl Tree { ErrorKind::Internal, "Subtree ID not found for node `{node}`", ) - .attach_context("node", node.name().to_string_lossy()) + .attach_context("node", String::from_utf8_lossy(&node.name())) .ask_report() })?; @@ -376,7 +355,7 @@ impl Tree { let mut state = MatchInternalState::default(); - let initial_path = PathBuf::new(); + let initial_path = UnixPathBuf::new(); let matches: Vec<_> = ids .into_iter() .map(|id| { @@ -411,17 +390,17 @@ pub struct FindNode { } /// Results from `find_matching_nodes` -#[derive(Debug, Serialize)] +#[derive(Debug)] pub struct FindMatches { /// found matching paths - pub paths: Vec, + pub paths: Vec, /// found matching nodes pub nodes: Vec, /// found paths/nodes for all given snapshots. (usize,usize) is the path / node index pub matches: Vec>, } -/// Converts a [`Component`] to an [`OsString`]. +/// Converts a [`TypedPath`] to an [`Cow`]. /// /// # Arguments /// @@ -431,21 +410,33 @@ pub struct FindMatches { /// /// * If the component is a current or parent directory. /// * If the component is not UTF-8 conform. -pub(crate) fn comp_to_osstr(p: Component<'_>) -> TreeResult> { - let s = match p { - Component::RootDir => None, - Component::Prefix(p) => match p.kind() { - Prefix::Verbatim(p) | Prefix::DeviceNS(p) => Some(p.to_os_string()), - Prefix::VerbatimUNC(_, q) | Prefix::UNC(_, q) => Some(q.to_os_string()), - Prefix::VerbatimDisk(p) | Prefix::Disk(p) => Some( - OsStr::new(str::from_utf8(&[p]).map_err(TreeErrorKind::PathIsNotUtf8Conform)?) - .to_os_string(), - ), - }, - Component::Normal(p) => Some(p.to_os_string()), - _ => return Err(TreeErrorKind::ContainsCurrentOrParentDirectory), - }; - Ok(s) +pub(crate) fn typed_path_to_unix_path<'a>(p: &'a TypedPath<'_>) -> Cow<'a, UnixPath> { + match p { + TypedPath::Unix(path) => Cow::Borrowed(path), + TypedPath::Windows(path) => { + let mut unix_path = UnixPathBuf::new(); + for c in path.with_windows_encoding().components() { + match c { + WindowsComponent::Prefix(p) => match p.kind() { + WindowsPrefix::Verbatim(p) | WindowsPrefix::DeviceNS(p) => { + unix_path.push(p); + } + WindowsPrefix::VerbatimUNC(_, q) | WindowsPrefix::UNC(_, q) => { + unix_path.push(q); + } + WindowsPrefix::VerbatimDisk(p) | WindowsPrefix::Disk(p) => { + let c = vec![p]; + unix_path.push(&c); + } + }, + c => { + unix_path.push(c); + } + } + } + Cow::Owned(unix_path) + } + } } impl IntoIterator for Tree { @@ -517,7 +508,7 @@ where /// Inner iterator for the current subtree nodes inner: std::vec::IntoIter, /// The current path - path: PathBuf, + path: UnixPathBuf, /// The backend to read from be: BE, /// index @@ -578,7 +569,7 @@ where Ok(Self { inner, open_iterators: Vec::new(), - path: PathBuf::new(), + path: UnixPathBuf::new(), be, index, overrides, @@ -730,7 +721,18 @@ where } if let Some(overrides) = &self.overrides { - if let Match::Ignore(_) = overrides.matched(&path, false) { + // TODO: use globset directly with UnixPath instead of converting to std::Path, + // see https://github.com/BurntSushi/ripgrep/issues/2954 + #[cfg(windows)] + if let Match::Ignore(_) = overrides.matched( + // using a lossy UTF-8 string from the underlying bytes is a best effort to get "correct" matching results + PathBuf::from(String::from_utf8_lossy(path.as_bytes()).to_string()), + false, + ) { + continue; + } + #[cfg(not(windows))] + if let Match::Ignore(_) = overrides.matched(PathBuf::from(&path), false) { continue; } } @@ -759,9 +761,9 @@ pub struct TreeStreamerOnce

{ /// The visited tree IDs visited: BTreeSet, /// The queue to send tree IDs to - queue_in: Option>, + queue_in: Option>, /// The queue to receive trees from - queue_out: Receiver>, + queue_out: Receiver>, /// The progress indicator p: P, /// The number of trees that are not yet finished @@ -824,7 +826,7 @@ impl TreeStreamerOnce

{ for (count, id) in ids.into_iter().enumerate() { if !streamer - .add_pending(PathBuf::new(), id, count) + .add_pending(UnixPathBuf::new(), id, count) .map_err(|err| { RusticError::with_source( ErrorKind::Internal, @@ -859,7 +861,7 @@ impl TreeStreamerOnce

{ /// # Errors /// /// * If sending the message fails. - fn add_pending(&mut self, path: PathBuf, id: TreeId, count: usize) -> TreeResult { + fn add_pending(&mut self, path: UnixPathBuf, id: TreeId, count: usize) -> TreeResult { if self.visited.insert(id) { self.queue_in .as_ref() @@ -915,7 +917,7 @@ impl Iterator for TreeStreamerOnce

{ "Failed to add tree ID `{tree_id}` to pending queue (`{count}`).", err, ) - .attach_context("path", path.display().to_string()) + .attach_context("path", path.to_string_lossy().to_string()) .attach_context("tree_id", id.to_string()) .attach_context("count", count.to_string()) .ask_report() diff --git a/crates/core/src/commands/backup.rs b/crates/core/src/commands/backup.rs index fe108604..bc885833 100644 --- a/crates/core/src/commands/backup.rs +++ b/crates/core/src/commands/backup.rs @@ -1,6 +1,7 @@ //! `backup` subcommand use derive_setters::Setters; use log::info; +use typed_path::UnixPathBuf; use std::path::PathBuf; @@ -221,7 +222,7 @@ pub(crate) fn backup( source.paths() }; - let as_path = opts + let as_path: Option = opts .as_path .as_ref() .map(|p| -> RusticResult<_> { @@ -234,7 +235,9 @@ pub(crate) fn backup( ) .attach_context("path", p.display().to_string()) })? - .to_path_buf()) + .to_path_buf() + .try_into() + .unwrap()) // TODO: error handling }) .transpose()?; @@ -250,22 +253,31 @@ pub(crate) fn backup( ) .attach_context("paths", p.display().to_string()) })?, - None => snap.paths.set_paths(&backup_path).map_err(|err| { - RusticError::with_source( - ErrorKind::Internal, - "Failed to set paths `{paths}` in snapshot.", - err, - ) - .attach_context( - "paths", - backup_path + None => snap + .paths + .set_paths( + &backup_path .iter() - .map(|p| p.display().to_string()) - .collect::>() - .join(","), + .map(|p| UnixPathBuf::try_from(p.clone())) + .collect::, _>>() + .unwrap(), ) - })?, - } + .map_err(|err| { + RusticError::with_source( + ErrorKind::Internal, + "Failed to set paths `{paths}` in snapshot.", + err, + ) + .attach_context( + "paths", + backup_path + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join(","), + ) + })?, + }; let (parent_id, parent) = opts.parent_opts.get_parent(repo, &snap, backup_stdin); match parent_id { @@ -286,10 +298,11 @@ pub(crate) fn backup( let snap = if backup_stdin { let path = &backup_path[0]; if let Some(command) = &opts.stdin_command { - let src = ChildStdoutSource::new(command, path.clone())?; + let unix_path: UnixPathBuf = path.clone().try_into().unwrap(); // todo: error handling + let src = ChildStdoutSource::new(command, unix_path.clone())?; let res = archiver.archive( &src, - path, + &unix_path, as_path.as_ref(), opts.parent_opts.skip_if_unchanged, opts.no_scan, @@ -298,10 +311,11 @@ pub(crate) fn backup( src.finish()?; res } else { + let path: UnixPathBuf = path.clone().try_into().unwrap(); // TODO: error handling let src = StdinSource::new(path.clone()); archiver.archive( &src, - path, + &path, as_path.as_ref(), opts.parent_opts.skip_if_unchanged, opts.no_scan, @@ -314,9 +328,10 @@ pub(crate) fn backup( &opts.ignore_filter_opts, &backup_path, )?; + let backup_path: UnixPathBuf = backup_path[0].clone().try_into().unwrap(); archiver.archive( &src, - &backup_path[0], + &backup_path, as_path.as_ref(), opts.parent_opts.skip_if_unchanged, opts.no_scan, diff --git a/crates/core/src/commands/cat.rs b/crates/core/src/commands/cat.rs index fdf78142..975690b6 100644 --- a/crates/core/src/commands/cat.rs +++ b/crates/core/src/commands/cat.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use bytes::Bytes; +use typed_path::UnixPath; use crate::{ backend::{FileType, FindInBackend, decrypt::DecryptReadBackend}, @@ -103,7 +102,7 @@ pub(crate) fn cat_tree( sn_filter, &repo.pb.progress_counter("getting snapshot..."), )?; - let node = Tree::node_from_path(repo.dbe(), repo.index(), snap.tree, Path::new(path))?; + let node = Tree::node_from_path(repo.dbe(), repo.index(), snap.tree, UnixPath::new(path))?; let id = node.subtree.ok_or_else(|| { RusticError::new( ErrorKind::InvalidInput, diff --git a/crates/core/src/commands/merge.rs b/crates/core/src/commands/merge.rs index 5533f968..81c0379c 100644 --- a/crates/core/src/commands/merge.rs +++ b/crates/core/src/commands/merge.rs @@ -44,7 +44,7 @@ pub(crate) fn merge_snapshots( .collect::() .merge(); - snap.paths.set_paths(&paths.paths()).map_err(|err| { + snap.paths.set_paths(&paths.unix_paths()).map_err(|err| { RusticError::with_source( ErrorKind::Internal, "Failed to set paths `{paths}` in snapshot.", diff --git a/crates/core/src/commands/restore.rs b/crates/core/src/commands/restore.rs index 279bcc45..47242d01 100644 --- a/crates/core/src/commands/restore.rs +++ b/crates/core/src/commands/restore.rs @@ -2,14 +2,9 @@ use derive_setters::Setters; use log::{debug, error, info, trace, warn}; +use typed_path::{UnixPath, UnixPathBuf}; -use std::{ - cmp::Ordering, - collections::BTreeMap, - num::NonZeroU32, - path::{Path, PathBuf}, - sync::Mutex, -}; +use std::{cmp::Ordering, collections::BTreeMap, num::NonZeroU32, path::Path, sync::Mutex}; use chrono::{DateTime, Local, Utc}; use ignore::{DirEntry, WalkBuilder}; @@ -38,7 +33,7 @@ pub(crate) mod constants { } type RestoreInfo = BTreeMap<(PackId, BlobLocation), Vec>; -type Filenames = Vec; +type Filenames = Vec; #[allow(clippy::struct_excessive_bools)] #[cfg_attr(feature = "clap", derive(clap::Parser))] @@ -116,7 +111,7 @@ pub(crate) fn restore_repository( file_infos: RestorePlan, repo: &Repository, opts: RestoreOptions, - node_streamer: impl Iterator>, + node_streamer: impl Iterator>, dest: &LocalDestination, ) -> RusticResult<()> { repo.warm_up_wait(file_infos.to_packs().into_iter())?; @@ -151,12 +146,18 @@ pub(crate) fn restore_repository( pub(crate) fn collect_and_prepare( repo: &Repository, opts: RestoreOptions, - mut node_streamer: impl Iterator>, + mut node_streamer: impl Iterator>, dest: &LocalDestination, dry_run: bool, ) -> RusticResult { let p = repo.pb.progress_spinner("collecting file information..."); - let dest_path = dest.path(""); + let dest_path = dest.path("").map_err(|err| { + RusticError::with_source( + ErrorKind::InputOutput, + "Failed to use the destination directory.", + err, + ) + })?; let mut stats = RestoreStats::default(); let mut restore_infos = RestorePlan::default(); @@ -215,7 +216,7 @@ pub(crate) fn collect_and_prepare( Ok(()) }; - let mut process_node = |path: &PathBuf, node: &Node, exists: bool| -> RusticResult<_> { + let mut process_node = |path: &UnixPathBuf, node: &Node, exists: bool| -> RusticResult<_> { match node.node_type { NodeType::Dir => { if exists { @@ -296,7 +297,15 @@ pub(crate) fn collect_and_prepare( next_dst = dst_iter.next(); } (Some(destination), Some((path, node))) => { - match destination.path().cmp(&dest.path(path)) { + let dest_path = dest.path(path).map_err(|err| { + RusticError::with_source( + ErrorKind::InputOutput, + "Failed to create the directory `{path}`. Please check the path and try again.", + err + ) + .attach_context("path", path.display().to_string()) + })?; + match destination.path().cmp(&dest_path) { Ordering::Less => { process_existing(destination)?; next_dst = dst_iter.next(); @@ -349,7 +358,7 @@ pub(crate) fn collect_and_prepare( /// /// * If the restore failed. fn restore_metadata( - mut node_streamer: impl Iterator>, + mut node_streamer: impl Iterator>, opts: RestoreOptions, dest: &LocalDestination, ) -> RusticResult<()> { @@ -396,9 +405,10 @@ fn restore_metadata( pub(crate) fn set_metadata( dest: &LocalDestination, opts: RestoreOptions, - path: &PathBuf, + path: impl AsRef, node: &Node, ) { + let path = path.as_ref(); debug!("setting metadata for {}", path.display()); dest.create_special(path, node) .unwrap_or_else(|_| warn!("restore {}: creating special file failed.", path.display())); @@ -675,7 +685,7 @@ impl RestorePlan { &mut self, dest: &LocalDestination, file: &Node, - name: PathBuf, + name: UnixPathBuf, repo: &Repository, ignore_mtime: bool, ) -> RusticResult { diff --git a/crates/core/src/repofile/snapshotfile.rs b/crates/core/src/repofile/snapshotfile.rs index 194eeb23..ce268ee2 100644 --- a/crates/core/src/repofile/snapshotfile.rs +++ b/crates/core/src/repofile/snapshotfile.rs @@ -17,6 +17,7 @@ use log::{info, warn}; use path_dedot::ParseDot; use serde_derive::{Deserialize, Serialize}; use serde_with::{DisplayFromStr, serde_as, skip_serializing_none}; +use typed_path::{UnixPath, UnixPathBuf}; use crate::{ Id, @@ -34,7 +35,7 @@ use crate::{ #[non_exhaustive] pub enum SnapshotFileErrorKind { /// non-unicode path `{0:?}` - NonUnicodePath(PathBuf), + NonUnicodePath(UnixPathBuf), /// value `{0:?}` not allowed ValueNotAllowed(String), /// datetime out of range: `{0:?}` @@ -1402,7 +1403,7 @@ impl StringList { /// # Errors /// /// * If a path is not valid unicode - pub(crate) fn set_paths>(&mut self, paths: &[T]) -> SnapshotFileResult<()> { + pub(crate) fn set_paths>(&mut self, paths: &[T]) -> SnapshotFileResult<()> { self.0 = paths .iter() .map(|p| { @@ -1506,6 +1507,16 @@ impl PathList { self.0.clone() } + /// Clone the internal `Vec`. + #[must_use] + pub(crate) fn unix_paths(&self) -> Vec { + self.0 + .iter() + .map(|p| p.clone().try_into()) + .collect::>() + .unwrap() + } + /// Sanitize paths: Parse dots, absolutize if needed and merge paths. /// /// # Errors diff --git a/crates/core/src/repository.rs b/crates/core/src/repository.rs index 7ecddf08..8393e5a8 100644 --- a/crates/core/src/repository.rs +++ b/crates/core/src/repository.rs @@ -5,7 +5,7 @@ use std::{ cmp::Ordering, fs::File, io::{BufRead, BufReader, Write}, - path::{Path, PathBuf}, + path::PathBuf, process::{Command, Stdio}, sync::Arc, }; @@ -14,6 +14,7 @@ use bytes::Bytes; use derive_setters::Setters; use log::{debug, error, info}; use serde_with::{DisplayFromStr, serde_as}; +use typed_path::{UnixPath, UnixPathBuf}; use crate::{ RepositoryBackends, RusticError, @@ -1745,8 +1746,8 @@ impl Repository { /// * If the path is not a directory. /// * If the path is not found. /// * If the path is not UTF-8 conform. - pub fn node_from_path(&self, root_tree: TreeId, path: &Path) -> RusticResult { - Tree::node_from_path(self.dbe(), self.index(), root_tree, Path::new(path)) + pub fn node_from_path(&self, root_tree: TreeId, path: &UnixPath) -> RusticResult { + Tree::node_from_path(self.dbe(), self.index(), root_tree, path) } /// Get all [`Node`]s from given root trees and a path @@ -1762,7 +1763,7 @@ impl Repository { pub fn find_nodes_from_path( &self, ids: impl IntoIterator, - path: &Path, + path: &UnixPath, ) -> RusticResult { Tree::find_nodes_from_path(self.dbe(), self.index(), ids, path) } @@ -1780,7 +1781,7 @@ impl Repository { pub fn find_matching_nodes( &self, ids: impl IntoIterator, - matches: &impl Fn(&Path, &Node) -> bool, + matches: &impl Fn(&UnixPath, &Node) -> bool, ) -> RusticResult { Tree::find_matching_nodes(self.dbe(), self.index(), ids, matches) } @@ -1823,7 +1824,7 @@ impl Repository { let p = &self.pb.progress_counter("getting snapshot..."); let snap = SnapshotFile::from_str(self.dbe(), id, filter, p)?; - Tree::node_from_path(self.dbe(), self.index(), snap.tree, Path::new(path)) + Tree::node_from_path(self.dbe(), self.index(), snap.tree, UnixPath::new(path)) } /// Get a [`Node`] from a [`SnapshotFile`] and a `path` @@ -1843,7 +1844,7 @@ impl Repository { snap: &SnapshotFile, path: &str, ) -> RusticResult { - Tree::node_from_path(self.dbe(), self.index(), snap.tree, Path::new(path)) + Tree::node_from_path(self.dbe(), self.index(), snap.tree, UnixPath::new(path)) } /// Reads a raw tree from a "SNAP\[:PATH\]" syntax /// @@ -1888,7 +1889,7 @@ impl Repository { &self, node: &Node, ls_opts: &LsOptions, - ) -> RusticResult> + Clone + '_> { + ) -> RusticResult> + Clone + '_> { NodeStreamer::new_with_glob(self.dbe().clone(), self.index(), node, ls_opts) } @@ -1908,7 +1909,7 @@ impl Repository { &self, restore_infos: RestorePlan, opts: &RestoreOptions, - node_streamer: impl Iterator>, + node_streamer: impl Iterator>, dest: &LocalDestination, ) -> RusticResult<()> { restore_repository(restore_infos, self, *opts, node_streamer, dest) @@ -2090,7 +2091,7 @@ impl Repository { pub fn prepare_restore( &self, opts: &RestoreOptions, - node_streamer: impl Iterator>, + node_streamer: impl Iterator>, dest: &LocalDestination, dry_run: bool, ) -> RusticResult { diff --git a/crates/core/src/repository/command_input.rs b/crates/core/src/repository/command_input.rs index 67d4e12f..741f1b0d 100644 --- a/crates/core/src/repository/command_input.rs +++ b/crates/core/src/repository/command_input.rs @@ -53,7 +53,7 @@ pub enum CommandInputErrorKind { command: CommandInput, /// The path in which the command was tried to be executed - path: std::path::PathBuf, + path: String, /// The source of the error source: std::io::Error, diff --git a/crates/core/src/vfs.rs b/crates/core/src/vfs.rs index 7ab4607c..41af5fb1 100644 --- a/crates/core/src/vfs.rs +++ b/crates/core/src/vfs.rs @@ -1,14 +1,11 @@ mod format; -use std::{ - collections::BTreeMap, - ffi::{OsStr, OsString}, - path::{Component, Path, PathBuf}, -}; +use std::collections::BTreeMap; use bytes::{Bytes, BytesMut}; use runtime_format::FormatArgs; use strum::EnumString; +use typed_path::{TypedPath, UnixComponent, UnixPath, UnixPathBuf}; use crate::{ blob::{BlobId, DataId, tree::TreeId}, @@ -27,7 +24,7 @@ pub enum VfsErrorKind { /// Only normal paths allowed OnlyNormalPathsAreAllowed, /// Name `{0:?}` doesn't exist - NameDoesNotExist(OsString), + NameDoesNotExist(String), } pub(crate) type VfsResult = Result; @@ -56,22 +53,22 @@ pub enum Latest { /// A potentially virtual tree in the [`Vfs`] enum VfsTree { /// A symlink to the given link target - Link(OsString), + Link(Vec), /// A repository tree; id of the tree RusticTree(TreeId), /// A purely virtual tree containing subtrees - VirtualTree(BTreeMap), + VirtualTree(BTreeMap, VfsTree>), } #[derive(Debug)] /// A resolved path within a [`Vfs`] enum VfsPath<'a> { /// Path is the given symlink - Link(&'a OsString), + Link(&'a [u8]), /// Path is within repository, give the tree [`Id`] and remaining path. - RusticPath(&'a TreeId, PathBuf), + RusticPath(&'a TreeId, UnixPathBuf), /// Path is the given virtual tree - VirtualTree(&'a BTreeMap), + VirtualTree(&'a BTreeMap, VfsTree>), } impl VfsTree { @@ -95,19 +92,19 @@ impl VfsTree { /// # Returns /// /// `Ok(())` if the tree was added successfully - fn add_tree(&mut self, path: &Path, new_tree: Self) -> VfsResult<()> { + fn add_tree(&mut self, path: &UnixPath, new_tree: Self) -> VfsResult<()> { let mut tree = self; let mut components = path.components(); - let Some(Component::Normal(last)) = components.next_back() else { + let Some(UnixComponent::Normal(last)) = components.next_back() else { return Err(VfsErrorKind::OnlyNormalPathsAreAllowed); }; for comp in components { - if let Component::Normal(name) = comp { + if let UnixComponent::Normal(name) = comp { match tree { Self::VirtualTree(virtual_tree) => { tree = virtual_tree - .entry(name.to_os_string()) + .entry(name.to_vec()) .or_insert(Self::VirtualTree(BTreeMap::new())); } _ => { @@ -121,7 +118,7 @@ impl VfsTree { return Err(VfsErrorKind::DirectoryExistsAsNonVirtual); }; - _ = virtual_tree.insert(last.to_os_string(), new_tree); + _ = virtual_tree.insert(last.to_vec(), new_tree); Ok(()) } @@ -138,21 +135,23 @@ impl VfsTree { /// # Returns /// /// If the path is within a real repository tree, this returns the [`VfsTree::RusticTree`] and the remaining path - fn get_path(&self, path: &Path) -> VfsResult> { + fn get_path(&self, path: &UnixPath) -> VfsResult> { let mut tree = self; let mut components = path.components(); loop { match tree { Self::RusticTree(id) => { - let path: PathBuf = components.collect(); + let path = components.collect(); return Ok(VfsPath::RusticPath(id, path)); } Self::VirtualTree(virtual_tree) => match components.next() { - Some(Component::Normal(name)) => { + Some(UnixComponent::Normal(name)) => { if let Some(new_tree) = virtual_tree.get(name) { tree = new_tree; } else { - return Err(VfsErrorKind::NameDoesNotExist(name.to_os_string())); + return Err(VfsErrorKind::NameDoesNotExist( + String::from_utf8_lossy(name).to_string(), + )); } } None => { @@ -246,9 +245,9 @@ impl Vfs { }, ) .to_string(); - let path = Path::new(&path); - let filename = path.file_name().map(OsStr::to_os_string); - let parent_path = path.parent().map(Path::to_path_buf); + let path = UnixPath::new(&path).to_path_buf(); + let filename = path.file_name().map(<[u8]>::to_vec); + let parent_path = path.parent().map(UnixPath::to_path_buf); // Save paths for latest entries, if requested if matches!(latest_option, Latest::AsLink) { @@ -265,27 +264,27 @@ impl Vfs { && last_tree == snap.tree { if let Some(name) = last_name { - tree.add_tree(path, VfsTree::Link(name.clone())) + tree.add_tree(&path, VfsTree::Link(name.clone())) .map_err(|err| { RusticError::with_source( ErrorKind::Vfs, "Failed to add a link `{name}` to root tree at `{path}`", err, ) - .attach_context("path", path.display().to_string()) - .attach_context("name", name.to_string_lossy()) + .attach_context("path", path.to_string_lossy().to_string()) + .attach_context("name", String::from_utf8_lossy(&name)) .ask_report() })?; } } else { - tree.add_tree(path, VfsTree::RusticTree(snap.tree)) + tree.add_tree(&path, VfsTree::RusticTree(snap.tree)) .map_err(|err| { RusticError::with_source( ErrorKind::Vfs, "Failed to add repository tree `{tree_id}` to root tree at `{path}`", err, ) - .attach_context("path", path.display().to_string()) + .attach_context("path", path.to_string_lossy().to_string()) .attach_context("tree_id", snap.tree.to_string()) .ask_report() })?; @@ -310,8 +309,8 @@ impl Vfs { "Failed to link latest `{target}` entry to root tree at `{path}`", err, ) - .attach_context("path", path.display().to_string()) - .attach_context("target", target.to_string_lossy()) + .attach_context("path", path.to_string_lossy().to_string()) + .attach_context("target", String::from_utf8_lossy(&target)) .attach_context("latest", "link") .ask_report() })?; @@ -329,7 +328,7 @@ impl Vfs { "Failed to add latest subtree id `{id}` to root tree at `{path}`", err, ) - .attach_context("path", path.display().to_string()) + .attach_context("path", path.to_string_lossy().to_string()) .attach_context("tree_id", subtree.to_string()) .attach_context("latest", "dir") .ask_report() @@ -360,7 +359,7 @@ impl Vfs { pub fn node_from_path( &self, repo: &Repository, - path: &Path, + path: &UnixPath, ) -> RusticResult { let meta = Metadata::default(); match self.tree.get_path(path).map_err(|err| { @@ -378,7 +377,7 @@ impl Vfs { } VfsPath::Link(target) => Ok(Node::new( String::new(), - NodeType::from_link(Path::new(target)), + NodeType::from_link(&TypedPath::derive(target)), meta, None, None, @@ -409,7 +408,7 @@ impl Vfs { pub fn dir_entries_from_path( &self, repo: &Repository, - path: &Path, + path: &UnixPath, ) -> RusticResult> { let result = match self.tree.get_path(path).map_err(|err| { RusticError::with_source( @@ -433,7 +432,7 @@ impl Vfs { .iter() .map(|(name, tree)| { let node_type = match tree { - VfsTree::Link(target) => NodeType::from_link(Path::new(target)), + VfsTree::Link(target) => NodeType::from_link(&TypedPath::derive(target)), _ => NodeType::Dir, }; Node::new_node(name, node_type, Metadata::default()) @@ -444,7 +443,7 @@ impl Vfs { ErrorKind::Vfs, "No directory entries for symlink `{symlink}` found. Is the path valid unicode?", ) - .attach_context("symlink", str.to_string_lossy().to_string())); + .attach_context("symlink", String::from_utf8_lossy(str).to_string())); } }; Ok(result) diff --git a/crates/core/tests/integration/backup.rs b/crates/core/tests/integration/backup.rs index 834dc41c..c37ce00c 100644 --- a/crates/core/tests/integration/backup.rs +++ b/crates/core/tests/integration/backup.rs @@ -1,7 +1,4 @@ -use std::{ - path::{Path, PathBuf}, - str::FromStr, -}; +use std::{path::PathBuf, str::FromStr}; use anyhow::Result; use insta::Settings; @@ -13,6 +10,7 @@ use rustic_core::{ StringList, repofile::{PackId, SnapshotFile}, }; +use typed_path::UnixPath; use super::{ RepoOpen, TestSource, assert_with_win, insta_node_redaction, insta_snapshotfile_redaction, @@ -52,7 +50,7 @@ fn test_backup_with_tar_gz_passes( // tree of first backup // re-read index let repo = repo.to_indexed_ids()?; - let tree = repo.node_from_path(first_snapshot.tree, Path::new("test/0/tests"))?; + let tree = repo.node_from_path(first_snapshot.tree, UnixPath::new("test/0/tests"))?; let tree: rustic_core::repofile::Tree = repo.get_tree(&tree.subtree.expect("Sub tree"))?; insta_node_redaction.bind(|| { @@ -186,7 +184,7 @@ fn test_backup_dry_run_with_tar_gz_passes( // tree of first backup // re-read index let repo = repo.to_indexed_ids()?; - let tree = repo.node_from_path(first_snapshot.tree, Path::new("test/0/tests"))?; + let tree = repo.node_from_path(first_snapshot.tree, UnixPath::new("test/0/tests"))?; let tree = repo.get_tree(&tree.subtree.expect("Sub tree"))?; insta_node_redaction.bind(|| { diff --git a/crates/core/tests/integration/find.rs b/crates/core/tests/integration/find.rs index 787abca1..0cfe5e86 100644 --- a/crates/core/tests/integration/find.rs +++ b/crates/core/tests/integration/find.rs @@ -1,3 +1,6 @@ +// don't warn about try_from paths conversion on unix +#![allow(clippy::unnecessary_fallible_conversions)] + use std::{ path::{Path, PathBuf}, str::FromStr, @@ -5,12 +8,14 @@ use std::{ use anyhow::Result; use globset::Glob; +use insta::assert_debug_snapshot; use rstest::rstest; use rustic_core::{ BackupOptions, FindMatches, FindNode, repofile::{Node, SnapshotFile}, }; +use typed_path::UnixPath; use super::{RepoOpen, TestSource, assert_with_win, set_up_repo, tar_gz_testdata}; @@ -29,33 +34,41 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) let repo = repo.to_indexed_ids()?; // test non-existing path - let not_found = repo.find_nodes_from_path(vec![snapshot.tree], Path::new("not_existing"))?; + let not_found = + repo.find_nodes_from_path(vec![snapshot.tree], UnixPath::new("not_existing"))?; assert_with_win("find-nodes-not-found", not_found); // test non-existing match let glob = Glob::new("not_existing")?.compile_matcher(); - let not_found = - repo.find_matching_nodes(vec![snapshot.tree], &|path, _| glob.is_match(path))?; - assert_with_win("find-matching-nodes-not-found", not_found); + let not_found = repo.find_matching_nodes(vec![snapshot.tree], &|path, _| { + glob.is_match(PathBuf::try_from(path).unwrap()) + })?; + assert_debug_snapshot!("find-matching-nodes-not-found", not_found); // test existing path let FindNode { matches, .. } = - repo.find_nodes_from_path(vec![snapshot.tree], Path::new("test/0/tests/testfile"))?; + repo.find_nodes_from_path(vec![snapshot.tree], UnixPath::new("test/0/tests/testfile"))?; assert_with_win("find-nodes-existing", matches); // test existing match let glob = Glob::new("testfile")?.compile_matcher(); - let match_func = |path: &Path, _: &Node| { - glob.is_match(path) || path.file_name().is_some_and(|f| glob.is_match(f)) + let match_func = |path: &UnixPath, _: &Node| { + glob.is_match(PathBuf::try_from(path).unwrap()) + || path + .file_name() + .is_some_and(|f| glob.is_match(Path::new(&String::from_utf8(f.to_vec()).unwrap()))) }; let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; - assert_with_win("find-matching-existing", (paths, matches)); + assert_debug_snapshot!("find-matching-existing", (paths, matches)); // test existing match let glob = Glob::new("testfile*")?.compile_matcher(); - let match_func = |path: &Path, _: &Node| { - glob.is_match(path) || path.file_name().is_some_and(|f| glob.is_match(f)) + let match_func = |path: &UnixPath, _: &Node| { + glob.is_match(PathBuf::try_from(path).unwrap()) + || path + .file_name() + .is_some_and(|f| glob.is_match(Path::new(&String::from_utf8(f.to_vec()).unwrap()))) }; let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; - assert_with_win("find-matching-wildcard-existing", (paths, matches)); + assert_debug_snapshot!("find-matching-wildcard-existing", (paths, matches)); Ok(()) } diff --git a/crates/core/tests/integration/ls.rs b/crates/core/tests/integration/ls.rs index 00452258..3274032c 100644 --- a/crates/core/tests/integration/ls.rs +++ b/crates/core/tests/integration/ls.rs @@ -1,8 +1,8 @@ -use std::{collections::BTreeMap, ffi::OsStr}; +use std::collections::BTreeMap; use std::{path::PathBuf, str::FromStr}; use anyhow::Result; -use insta::Settings; +use insta::{Settings, assert_debug_snapshot}; use rstest::rstest; use rustic_core::{ @@ -10,9 +10,7 @@ use rustic_core::{ repofile::{Metadata, Node, SnapshotFile}, }; -use super::{ - RepoOpen, TestSource, assert_with_win, insta_node_redaction, set_up_repo, tar_gz_testdata, -}; +use super::{RepoOpen, TestSource, insta_node_redaction, set_up_repo, tar_gz_testdata}; #[rstest] fn test_ls( @@ -31,7 +29,7 @@ fn test_ls( // test non-existing entries let mut node = Node::new_node( - OsStr::new(""), + &[], rustic_core::repofile::NodeType::Dir, Metadata::default(), ); @@ -45,7 +43,7 @@ fn test_ls( .collect::>()?; insta_node_redaction.bind(|| { - assert_with_win("ls", &entries); + assert_debug_snapshot!("ls", &entries); }); Ok(()) diff --git a/crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap.new b/crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap.new new file mode 100644 index 00000000..448cec04 --- /dev/null +++ b/crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap.new @@ -0,0 +1,12 @@ +--- +source: crates/core/tests/integration/find.rs +assertion_line: 45 +expression: not_found +--- +FindMatches { + paths: [], + nodes: [], + matches: [ + [], + ], +} diff --git a/crates/core/tests/integration/snapshots/integration__integration__ls__ls.snap.new b/crates/core/tests/integration/snapshots/integration__integration__ls__ls.snap.new new file mode 100644 index 00000000..75edf71e --- /dev/null +++ b/crates/core/tests/integration/snapshots/integration__integration__ls__ls.snap.new @@ -0,0 +1,4672 @@ +--- +source: crates/core/tests/integration/ls.rs +assertion_line: 46 +expression: "&entries" +--- +{ + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + ], + }: Node { + name: "test", + node_type: Dir, + meta: Metadata { + mode: Some( + 493, + ), + mtime: None, + atime: None, + ctime: None, + uid: None, + gid: None, + user: None, + group: None, + inode: 0, + device_id: 0, + size: 0, + links: 0, + extended_attributes: [], + }, + content: None, + subtree: Some( + TreeId( + 43a5917774aebc9ae029aa26be74aa14c9e79b88279389d6c53b9c8f238ad2eb, + ), + ), + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + ], + }: Node { + name: "0", + node_type: Dir, + meta: Metadata { + mode: Some( + 2147484141, + ), + mtime: Some( + 2025-10-22T22:24:12.609105638+02:00, + ), + atime: Some( + 2025-10-22T22:24:12.609105638+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609306758+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1719, + device_id: 48, + size: 0, + links: 0, + extended_attributes: [], + }, + content: None, + subtree: Some( + TreeId( + d476f10522de896704c69222812cf28106c9e26726ce5b25a057593993b4d0c3, + ), + ), + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + ], + }: Node { + name: "0", + node_type: Dir, + meta: Metadata { + mode: Some( + 2147484141, + ), + mtime: Some( + 2025-10-22T22:24:12.595413596+02:00, + ), + atime: Some( + 2025-10-22T22:24:12.595413596+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609306758+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1720, + device_id: 48, + size: 0, + links: 0, + extended_attributes: [], + }, + content: None, + subtree: Some( + TreeId( + 7e616ff2cea902a2864da30f0ff98ea5b8050299bf4dc46e776025cab7b46478, + ), + ), + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + ], + }: Node { + name: "9", + node_type: Dir, + meta: Metadata { + mode: Some( + 2147484141, + ), + mtime: Some( + 2025-10-22T22:24:12.609105638+02:00, + ), + atime: Some( + 2025-10-22T22:24:12.609105638+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609306758+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1721, + device_id: 48, + size: 0, + links: 0, + extended_attributes: [], + }, + content: None, + subtree: Some( + TreeId( + ddc76bc20bcb185e355b1b1608d75bad879305909ec3a1f81d27864bca438472, + ), + ), + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 48, + ], + }: Node { + name: "0", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595413596+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1722, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + aa6da76dc4b3bd8d8cd9d42c5ed6db2e9c3e43cecef7e7ee925a3accab96ad79, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + ], + }: Node { + name: "1", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.607515957+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1758, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + bd15c2d9117aa305a0cf99c9cc30359b66ee454b00b897fb1a925c56025f4556, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 48, + ], + }: Node { + name: "10", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608340687+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1774, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 753d116d34c772059a9a4a2ca33f6ea9ac618470acc13687d08fff7eb5ad3c72, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 49, + ], + }: Node { + name: "11", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595960860+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1736, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + efe42dd90fd602df1c5b3b7675cd845e23befba069eac0222a9997fea2a9852f, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 50, + ], + }: Node { + name: "12", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608615622+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1780, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 032ad465747269b41dbdfc87accad1fd49e8dc574ae4bfaf49f036150ec92e57, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 51, + ], + }: Node { + name: "13", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595919909+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1735, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + b0b5afc29b6391722bd60a9a74c386c856cb130a219954f34944a4f19bbecc41, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 52, + ], + }: Node { + name: "14", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596636353+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1751, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + b19a49a2e711d5547e1fb247170c58d2da17f9b0bbf75eb84507097a301f1c6d, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 53, + ], + }: Node { + name: "15", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595505664+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1723, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + f11e72e0290dc75261ee48c39fd42597bdcd80e110ffd0651f71d54ea22030e5, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 54, + ], + }: Node { + name: "16", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595825633+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1732, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 71935a6f85198c65584ed5c4ff5e34cf93cb79756023e82ecc85153663be2b00, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 55, + ], + }: Node { + name: "17", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609105638+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1790, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + b81f6dd6ad54fbed57ffac74bcecf9f1a56ce8948edc92153ee34ed415ed68d1, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 56, + ], + }: Node { + name: "18", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609008998+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1788, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 1fad45c4bf21695fae9e100626f42d35d8c2c48cb90f6a6256973c262608879d, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 49, + 57, + ], + }: Node { + name: "19", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608418596+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1776, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 92c4aa4fa67ede99b9ab1b694bc32b4f3d127f406a7e6583ecbe55b1bb919928, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + ], + }: Node { + name: "2", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596278897+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1745, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + f5a3517234ef2f213afe617cdab9d25364e1c8c9e61198bf387b157542ee7dbb, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 48, + ], + }: Node { + name: "20", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608200399+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1771, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 737c19313675671c589bfc613b0b736b95a58218486d09d3a7a649f324d81ef7, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 49, + ], + }: Node { + name: "21", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596060385+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1739, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + ee25130a4e74b1306087799914d8b92aed2c385b06256705b18266ff088524f4, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 50, + ], + }: Node { + name: "22", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596315581+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1746, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 3492c28f8ddbb9186e483ef9eb1cc7042c5adf871a4d15d2edf8ee7b2b04069b, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 51, + ], + }: Node { + name: "23", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608012312+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1767, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + b321dedc16124312ed08cb7f9c67006bd6a4f946ca04bc178323e6147b081943, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 52, + ], + }: Node { + name: "24", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608799815+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1784, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 7020db85ffe6a3fd5f34971ff37a20fa5d32947181692bfbcca62cfc4a9221d0, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 53, + ], + }: Node { + name: "25", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609051604+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1789, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 75e7274ef54e91c2857ba0de4c869388df3f9c657d776a3e90eaf3f3f619536d, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 54, + ], + }: Node { + name: "26", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596174507+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1742, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 418b691988d9bc7357b99c5baf31e83436315d1bcc887482dd83a8537183af17, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 55, + ], + }: Node { + name: "27", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595576317+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1725, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 06a5749af5715af19fde59d1caaa542df00c56aa0058bf99b67dec5216c27df6, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 56, + ], + }: Node { + name: "28", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.607854465+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1764, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 61e8ffda5a11d743d7dc4bcfd1e18e8b6179a53f8b28b25adf55d3acb0b10eff, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 50, + 57, + ], + }: Node { + name: "29", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608688401+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1781, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 60c1c2f3a0b8e5b1c4e2b7f1453661170395c5c26f2e2d241e81eb929cfc930f, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + ], + }: Node { + name: "3", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.607682609+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1761, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + f0ad279385b5b5c862e1e2fdd599616115729e0b8085833d7b31de68c8326b85, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 48, + ], + }: Node { + name: "30", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608945209+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1787, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 790c34f1d4d95a6067f2406a78aa0dcca2fc2aa1db932fbd865ce9cafd7baa23, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 49, + ], + }: Node { + name: "31", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608768028+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1783, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 8e53f45db9249373828c588774471d34c9c1e436b83b1603a49d675de810db7e, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 50, + ], + }: Node { + name: "32", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596958041+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1754, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + c4eccf95ba2409d21283981297bf9919ec15c2cc0dadef84aafaad49a0c79130, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 51, + ], + }: Node { + name: "33", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595605532+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1726, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + da8b8b2d79bba6a3d4784233e4421293cea0cf27fd03521e11ce0c7c941f6dbb, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 52, + ], + }: Node { + name: "34", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608539886+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1778, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + a1413ef1018e7a4f3df7f19aad942a611cbda5bdd45d4269cb54d5f9fa58cd01, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 53, + ], + }: Node { + name: "35", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596432194+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1749, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + f534a7fe839d440a0fdd1d45254921601b2a90ff5b61edcdc631b722f84a0dbb, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 54, + ], + }: Node { + name: "36", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596136613+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1741, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + d56e2a8c47f4ae36886784c852916f36036f3daffa547a5922dda5814ecc5a86, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 55, + ], + }: Node { + name: "37", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608094635+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1769, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 5800df73dcfdbd261800f2474c8631fe0727fbc1788cfd70bd206ab2f50b0707, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 56, + ], + }: Node { + name: "38", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608385895+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1775, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + e7c987bd8d9257bea1e44f882bc075c84ee7f103d68eec8677e37b49d2854d90, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 51, + 57, + ], + }: Node { + name: "39", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596203681+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1743, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 03a1411035add5b10ed183b2ca7d8db8deee2402f45ee445f54de50896c3e26f, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + ], + }: Node { + name: "4", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.607766434+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1763, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 917aae16764fc905abf0514e965133ed80e2b66c426fb09be24eabd452d4438d, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 48, + ], + }: Node { + name: "40", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596105649+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1740, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 1adcd19cf770f7d8ab3bb4b3996fb6187f59c468953848ec4f119c9cef484f28, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 49, + ], + }: Node { + name: "41", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595891536+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1734, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + b5debda793f22adb93204dc81f107d03636b7431192fc8497f5c9a340e43460c, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 50, + ], + }: Node { + name: "42", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596785460+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1753, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + e6e9b35f3ca77afbd0043bc42085b814ceea8cfb93a0bcba9696dee5034ffead, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 51, + ], + }: Node { + name: "43", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596700291+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1752, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 31d87fd77a88ca00e9c5612f578c0b3243347ba328a6daa09742fdc8c7cec6f4, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 52, + ], + }: Node { + name: "44", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595854468+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1733, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 76aec0f2d829d01eff332099ebd5628f61adc72ff44e7d6b82beabc28fa44f1f, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 53, + ], + }: Node { + name: "45", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595644291+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1727, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 121242341ff94a05074a31cb2a0f566093e5c052e64dcde4c0c9b686e58daf87, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 54, + ], + }: Node { + name: "46", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595680037+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1728, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + a69efe0ab923672b5aa1a2113f54e80684a7c126c1114af4fac3953be18bea74, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 55, + ], + }: Node { + name: "47", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596031722+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1738, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + a50a7ab010d0d349b213576d1e2d2d2b345334be1be6f62798d250fc1e5cc51f, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 56, + ], + }: Node { + name: "48", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596997979+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1755, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 32715f839edd796c5b6f2d48c88748857ed439867c2dc4b2b65c5f96c6d9ef73, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 52, + 57, + ], + }: Node { + name: "49", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.607558482+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1759, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + d3fdbaa72299c1abfe47bc26bdf201cc5cb66efc031e517111d413c38bfea882, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + ], + }: Node { + name: "5", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595993941+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1737, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + de68adab4fe82b31fe5b14abe5d17faecf4f7c8d93d74374cbf3793701290ede, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 48, + ], + }: Node { + name: "50", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608726416+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1782, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 939e25879149a8b87ecb6b44ded96f6b029d5463d38fe5525005984d6d42c34a, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 49, + ], + }: Node { + name: "51", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595750657+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1730, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 958a554f813d199f9f897c9f5c4d90571dfe7f67d8a3733a64598906a8e7208d, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 50, + ], + }: Node { + name: "52", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595713600+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1729, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 2e8799330876d03e95428975593175ea5dd75ed3d5f3d83b4d9115dd2c6d3b71, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 51, + ], + }: Node { + name: "53", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.607898677+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1765, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + f476577492f325d8e617814bde3ecc83eb9e8662023d2e057c6c958ce77511e5, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 52, + ], + }: Node { + name: "54", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608273779+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1773, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + b03ca92f97233541fcc95c22455cadf63c5a25089117af4b433ffc1ed9a8991f, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 53, + ], + }: Node { + name: "55", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608046859+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1768, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 63d9f467b844e7d5a48120b236408e381b160c9c8355b0e3113cd407ad694ab4, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 54, + ], + }: Node { + name: "56", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596242308+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1744, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + c93bcbbab1c234a01574f913bc49cc2857ccc17e4d5d8c9dc9b9b720729ed3f7, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 55, + ], + }: Node { + name: "57", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608870720+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1785, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 86f85e1d9af7cac33a60fae72c05f0f410ec0c9dfd917ad18a7e2308a272e1fb, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 56, + ], + }: Node { + name: "58", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596344884+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1747, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 858f7dc0f2b2e7532fec3b2a2daa551855ebaf27e29b60a90807e3f770d60815, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 53, + 57, + ], + }: Node { + name: "59", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608904154+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1786, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 6047339cfc27b694b82bfc1621888997bf0e954bc5459a7ed51c424d7bd92583, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + ], + }: Node { + name: "6", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.606978417+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1757, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + f6eca86a06925045b3b3a09f38999144ace49570b7e3a203ca7f24644fe35f20, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + 48, + ], + }: Node { + name: "60", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608233150+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1772, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + fcd9ec0c99f7992c184666e3040831b919f3375157bd563a2b65cde1c6789847, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + 49, + ], + }: Node { + name: "61", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.606978417+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1756, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + f5738472e3ab1372588f9048c3198bb9e05338acc94c33aca3c34277c675116c, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + 50, + ], + }: Node { + name: "62", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608128200+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1770, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 7d07ba1cacc97dc5ae31b7e49a8926bec040102f1211fda9e98f0ea70a6aecf9, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + 51, + ], + }: Node { + name: "63", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.607939727+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1766, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + c8d553f609367281f390160dd77113ca952dbb7a7fd073daa2ac4620a63148b4, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + 52, + ], + }: Node { + name: "64", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.607605703+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1760, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 5d2c26fc0563f43d161b5abc982af18696d7178ad1f7aeeae940c595415cc6df, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + 53, + ], + }: Node { + name: "65", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595779512+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1731, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 2888d521727e1ba03471bcea1d48927818b54d42331caef8115fac5be94406b6, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + 54, + ], + }: Node { + name: "66", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.607724157+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1762, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + a9ba60dcf9b908c3488d03d2f3ecf9aff2fa1b419f30f3d938b2eb3c319566cc, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + 55, + ], + }: Node { + name: "67", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596470336+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1750, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 7246d1d67000b94ac3e1928fcef3804263156e72aeb886b00b3f59ec1090c427, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 54, + 56, + ], + }: Node { + name: "68", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.595547746+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1724, + device_id: 48, + size: 11520, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + a8fafe2f1d514d1efc327d76fcc33916c20acd6838f524848a259de752862c41, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 55, + ], + }: Node { + name: "7", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608459202+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1777, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 6a1da85d5dccfdefb75bb607e1f3ded8f6068085a7359ceb41a1b9930d9442f3, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 56, + ], + }: Node { + name: "8", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.596399830+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1748, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 105068aefc2d42cce9acc224bb369002f53f0036da394632f4623170f3b0277e, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 48, + 47, + 57, + 47, + 57, + ], + }: Node { + name: "9", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-05T22:44:35+02:00, + ), + atime: Some( + 2014-08-05T22:44:35+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.608582798+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1779, + device_id: 48, + size: 16384, + links: 1, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 656039f259149cf97af18fb11ebee3194cfaa059ff72d6bc9a2b91b0889d65cc, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 116, + 101, + 115, + 116, + 115, + ], + }: Node { + name: "tests", + node_type: Dir, + meta: Metadata { + mode: Some( + 2147484141, + ), + mtime: Some( + 2025-10-22T22:24:12.609283517+02:00, + ), + atime: Some( + 2025-10-22T22:24:12.609283517+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609306758+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1791, + device_id: 48, + size: 0, + links: 0, + extended_attributes: [], + }, + content: None, + subtree: Some( + TreeId( + 2bccf0bb6407b030100d471dcde0cc0637fcb335628548b2424df9404e91dd5c, + ), + ), + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 116, + 101, + 115, + 116, + 115, + 47, + 101, + 109, + 112, + 116, + 121, + 45, + 102, + 105, + 108, + 101, + ], + }: Node { + name: "empty-file", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-11-30T16:03:11+01:00, + ), + atime: Some( + 2014-11-30T16:03:11+01:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609260244+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1793, + device_id: 48, + size: 0, + links: 1, + extended_attributes: [], + }, + content: Some( + [], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 116, + 101, + 115, + 116, + 115, + 47, + 116, + 101, + 115, + 116, + 102, + 105, + 108, + 101, + ], + }: Node { + name: "testfile", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-09T14:14:20+02:00, + ), + atime: Some( + 2014-08-09T14:14:20+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609105638+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1792, + device_id: 48, + size: 21, + links: 2, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 649b8b471e7d7bc175eec758a7006ac693c434c8297c07db15286788c837154a, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 116, + 101, + 115, + 116, + 115, + 47, + 116, + 101, + 115, + 116, + 102, + 105, + 108, + 101, + 45, + 104, + 97, + 114, + 100, + 108, + 105, + 110, + 107, + ], + }: Node { + name: "testfile-hardlink", + node_type: File, + meta: Metadata { + mode: Some( + 420, + ), + mtime: Some( + 2014-08-09T14:14:20+02:00, + ), + atime: Some( + 2014-08-09T14:14:20+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609105638+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1792, + device_id: 48, + size: 21, + links: 2, + extended_attributes: [], + }, + content: Some( + [ + DataId( + 649b8b471e7d7bc175eec758a7006ac693c434c8297c07db15286788c837154a, + ), + ], + ), + subtree: None, + }, + PathBuf { + _encoding: "unix", + inner: [ + 116, + 101, + 115, + 116, + 47, + 48, + 47, + 116, + 101, + 115, + 116, + 115, + 47, + 116, + 101, + 115, + 116, + 102, + 105, + 108, + 101, + 45, + 115, + 121, + 109, + 108, + 105, + 110, + 107, + ], + }: Node { + name: "testfile-symlink", + node_type: Symlink { + linktarget: "testfile", + linktarget_raw: None, + }, + meta: Metadata { + mode: Some( + 134218239, + ), + mtime: Some( + 2014-08-09T14:14:28+02:00, + ), + atime: Some( + 2014-08-09T14:14:28+02:00, + ), + ctime: Some( + 2025-10-22T22:24:12.609283517+02:00, + ), + uid: Some( + 1002, + ), + gid: Some( + 1002, + ), + user: Some( + "alex-dev", + ), + group: Some( + "alex-dev", + ), + inode: 1794, + device_id: 48, + size: 8, + links: 1, + extended_attributes: [], + }, + content: None, + subtree: None, + }, +} diff --git a/crates/core/tests/integration/vfs.rs b/crates/core/tests/integration/vfs.rs index a2fd819a..e4d14823 100644 --- a/crates/core/tests/integration/vfs.rs +++ b/crates/core/tests/integration/vfs.rs @@ -7,6 +7,7 @@ use pretty_assertions::assert_eq; use rstest::rstest; use rustic_core::{BackupOptions, repofile::SnapshotFile, vfs::Vfs}; +use typed_path::UnixPath; use super::{ RepoOpen, TestSource, assert_with_win, insta_node_redaction, set_up_repo, tar_gz_testdata, @@ -34,15 +35,13 @@ fn test_vfs( let vfs = Vfs::from_dir_node(&node); // test reading a directory using vfs - let path: PathBuf = ["test", "0", "tests"].iter().collect(); - let entries = vfs.dir_entries_from_path(&repo, &path)?; + let entries = vfs.dir_entries_from_path(&repo, UnixPath::new("test/0/tests/testfile"))?; insta_node_redaction.bind(|| { assert_with_win("vfs", &entries); }); // test reading a file from the repository - let path: PathBuf = ["test", "0", "tests", "testfile"].iter().collect(); - let node = vfs.node_from_path(&repo, &path)?; + let node = vfs.node_from_path(&repo, UnixPath::new("test/0/tests/testfile"))?; let file = repo.open_file(&node)?; let data = repo.read_file_at(&file, 0, 21)?; // read full content @@ -52,8 +51,7 @@ fn test_vfs( assert_eq!(Bytes::from("test"), repo.read_file_at(&file, 10, 4)?); // read partial content // test reading an empty file from the repository - let path: PathBuf = ["test", "0", "tests", "empty-file"].iter().collect(); - let node = vfs.node_from_path(&repo, &path)?; + let node = vfs.node_from_path(&repo, UnixPath::new("test/0/tests/empty-file"))?; let file = repo.open_file(&node)?; assert_eq!(Bytes::new(), repo.read_file_at(&file, 0, 0)?); // empty files Ok(()) diff --git a/examples/find/Cargo.toml b/examples/find/Cargo.toml index adee4527..35b806ae 100644 --- a/examples/find/Cargo.toml +++ b/examples/find/Cargo.toml @@ -12,6 +12,7 @@ globset = "0.4.16" rustic_backend = { workspace = true } rustic_core = { workspace = true } simplelog = { workspace = true } +typed-path = "0.10.0" [[example]] name = "find" diff --git a/examples/find/examples/find.rs b/examples/find/examples/find.rs index a1f06d1a..df9b48da 100644 --- a/examples/find/examples/find.rs +++ b/examples/find/examples/find.rs @@ -3,8 +3,10 @@ use globset::Glob; use rustic_backend::BackendOptions; use rustic_core::{FindMatches, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; -use std::error::Error; +use std::{error::Error, path::PathBuf}; +// don't warn about try_from paths conversion on unix +#[allow(clippy::unnecessary_fallible_conversions)] fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); @@ -30,7 +32,9 @@ fn main() -> Result<(), Box> { paths, nodes, matches, - } = repo.find_matching_nodes(tree_ids, &|path, _| glob.is_match(path))?; + } = repo.find_matching_nodes(tree_ids, &|path, _| { + glob.is_match(PathBuf::try_from(path).unwrap()) + })?; for (snap, matches) in snapshots.iter().zip(matches) { println!("results in {snap:?}"); for (path_idx, node_idx) in matches { From af7037f5041ca68f44d73311cb24bd3af5419134 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sat, 11 Jan 2025 22:55:22 +0100 Subject: [PATCH 02/22] fix windows build --- crates/core/src/backend/ignore.rs | 13 ++++++++----- crates/core/src/backend/local_destination.rs | 7 ++++++- crates/core/src/backend/node.rs | 20 -------------------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/crates/core/src/backend/ignore.rs b/crates/core/src/backend/ignore.rs index a3b7d44a..4393ba3a 100644 --- a/crates/core/src/backend/ignore.rs +++ b/crates/core/src/backend/ignore.rs @@ -444,7 +444,7 @@ fn map_entry( with_atime: bool, _ignore_devid: bool, ) -> IgnoreResult> { - let name = entry.file_name(); + let name = entry.file_name().as_encoded_bytes(); let m = entry .metadata() .map_err(|err| IgnoreErrorKind::FailedToGetMetadata { source: err })?; @@ -498,10 +498,12 @@ fn map_entry( Node::new_node(name, NodeType::Dir, meta) } else if m.is_symlink() { let path = entry.path(); - let target = read_link(path).map_err(|err| IgnoreErrorKind::ErrorLink { - path: path.to_path_buf(), - source: err, - })?; + let target = read_link(path) + .map_err(|err| IgnoreErrorKind::ErrorLink { + path: path.to_path_buf(), + source: err, + })? + .as_encoded_bytes(); let node_type = NodeType::from_link(&target); Node::new_node(name, node_type, meta) } else { @@ -510,6 +512,7 @@ fn map_entry( let path = entry.into_path(); let open = Some(OpenFile(path.clone())); + let path: UnixPathBuf = path.try_into().unwrap(); // TODO: Error handling Ok(ReadSourceEntry { path, node, open }) } diff --git a/crates/core/src/backend/local_destination.rs b/crates/core/src/backend/local_destination.rs index 68f6b72b..451f4e96 100644 --- a/crates/core/src/backend/local_destination.rs +++ b/crates/core/src/backend/local_destination.rs @@ -103,6 +103,9 @@ pub enum LocalDestinationErrorKind { filename: PathBuf, source: std::io::Error, }, + #[cfg(windows)] + /// Non-UTF8 filename is not allowed: `{0:?}` + Utf8Error(std::str::Utf8Error), } pub(crate) type LocalDestinationResult = Result; @@ -202,7 +205,9 @@ impl LocalDestination { } #[cfg(windows)] { - let item = PathBuf::try_from(item.as_ref())?; + // only utf8 items are allowed on windows + let item = std::str::from_utf8(item.as_ref().as_bytes()) + .map_err(LocalDestinationErrorKind::Utf8Error)?; Ok(self.path.join(item)) } } diff --git a/crates/core/src/backend/node.rs b/crates/core/src/backend/node.rs index 6c16ff3c..1114e7d4 100644 --- a/crates/core/src/backend/node.rs +++ b/crates/core/src/backend/node.rs @@ -338,25 +338,6 @@ pub fn last_modified_node(n1: &Node, n2: &Node) -> Ordering { n1.meta.mtime.cmp(&n2.meta.mtime) } -// TODO: Should be probably called `_lossy` -// TODO(Windows): This is not able to handle non-unicode filenames and -// doesn't treat filenames which need and escape (like `\`, `"`, ...) correctly -#[cfg(windows)] -fn escape_filename(name: &OsStr) -> String { - name.to_string_lossy().to_string() -} - -/// Unescape a filename -/// -/// # Arguments -/// -/// * `s` - The escaped filename -#[cfg(windows)] -fn unescape_filename(s: &str) -> Result { - OsString::from_str(s) -} - -#[cfg(not(windows))] /// Escape a filename /// /// # Arguments @@ -414,7 +395,6 @@ fn escape_filename(name: &[u8]) -> String { s } -#[cfg(not(windows))] /// Unescape a filename /// /// # Arguments From aff6924c894f51eddc60547ae4ef8cf4843b0488 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sat, 11 Jan 2025 23:15:59 +0100 Subject: [PATCH 03/22] more windows fixes --- crates/core/src/backend/ignore.rs | 11 ++++------- crates/core/src/backend/node.rs | 4 ---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/crates/core/src/backend/ignore.rs b/crates/core/src/backend/ignore.rs index 4393ba3a..bf7acecf 100644 --- a/crates/core/src/backend/ignore.rs +++ b/crates/core/src/backend/ignore.rs @@ -20,6 +20,7 @@ use log::warn; #[cfg(not(windows))] use nix::unistd::{Gid, Group, Uid, User}; use serde_with::{DisplayFromStr, serde_as}; +use typed_path::UnixPath; #[cfg(not(windows))] use crate::backend::node::ExtendedAttribute; @@ -512,7 +513,7 @@ fn map_entry( let path = entry.into_path(); let open = Some(OpenFile(path.clone())); - let path: UnixPathBuf = path.try_into().unwrap(); // TODO: Error handling + let path = UnixPath::new(path.as_os_str().as_encoded_bytes()).to_path_buf(); Ok(ReadSourceEntry { path, node, open }) } @@ -613,11 +614,7 @@ fn map_entry( with_atime: bool, ignore_devid: bool, ) -> IgnoreResult> { - use std::os::unix::ffi::OsStrExt; - - use typed_path::{UnixPath, UnixPathBuf}; - - let name = entry.file_name().as_bytes(); + let name = entry.file_name().as_encoded_bytes(); let m = entry .metadata() .map_err(|err| IgnoreErrorKind::AcquiringMetadataFailed { @@ -715,7 +712,7 @@ fn map_entry( }; let path = entry.into_path(); let open = Some(OpenFile(path.clone())); - let path: UnixPathBuf = path.try_into().unwrap(); // TODO: Error handling + let path = UnixPath::new(path.as_os_str().as_encoded_bytes()).to_path_buf(); Ok(ReadSourceEntry { path, node, open }) } diff --git a/crates/core/src/backend/node.rs b/crates/core/src/backend/node.rs index 1114e7d4..e31d52bc 100644 --- a/crates/core/src/backend/node.rs +++ b/crates/core/src/backend/node.rs @@ -19,7 +19,6 @@ use typed_path::TypedPath; use crate::blob::{DataId, tree::TreeId}; -#[cfg(not(windows))] /// [`NodeErrorKind`] describes the errors that can be returned by an action utilizing a node in Backends #[derive(thiserror::Error, Debug, displaydoc::Display)] #[non_exhaustive] @@ -76,7 +75,6 @@ pub enum NodeErrorKind<'a> { }, } -#[cfg(not(windows))] pub(crate) type NodeResult<'a, T> = Result>; #[derive( @@ -501,7 +499,6 @@ fn unescape_filename(s: &str) -> NodeResult<'_, Vec> { Ok(u) } -#[cfg(not(windows))] #[inline] // Iterator#take cannot be used because it consumes the iterator fn take>(iterator: &mut I, n: usize) -> String { @@ -512,7 +509,6 @@ fn take>(iterator: &mut I, n: usize) -> String { s } -#[cfg(not(windows))] #[cfg(test)] mod tests { use super::*; From a1f2a3353dff9b59eaf626d0a4c5b0f3bb1f6796 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sat, 11 Jan 2025 23:21:03 +0100 Subject: [PATCH 04/22] more windows fixes --- crates/core/src/backend/local_destination.rs | 6 +++--- crates/core/src/backend/node.rs | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/crates/core/src/backend/local_destination.rs b/crates/core/src/backend/local_destination.rs index 451f4e96..cb33ba4f 100644 --- a/crates/core/src/backend/local_destination.rs +++ b/crates/core/src/backend/local_destination.rs @@ -314,7 +314,7 @@ impl LocalDestination { #[allow(clippy::unused_self, clippy::unnecessary_wraps)] pub(crate) fn set_user_group( &self, - _item: impl AsRef, + _item: impl AsRef, _meta: &Metadata, ) -> LocalDestinationResult<()> { // https://learn.microsoft.com/en-us/windows/win32/fileio/file-security-and-access-rights @@ -370,7 +370,7 @@ impl LocalDestination { #[allow(clippy::unused_self, clippy::unnecessary_wraps)] pub(crate) fn set_uid_gid( &self, - _item: impl AsRef, + _item: impl AsRef, _meta: &Metadata, ) -> LocalDestinationResult<()> { Ok(()) @@ -418,7 +418,7 @@ impl LocalDestination { #[allow(clippy::unused_self, clippy::unnecessary_wraps)] pub(crate) fn set_permission( &self, - _item: impl AsRef, + _item: impl AsRef, _node: &Node, ) -> LocalDestinationResult<()> { Ok(()) diff --git a/crates/core/src/backend/node.rs b/crates/core/src/backend/node.rs index e31d52bc..14882273 100644 --- a/crates/core/src/backend/node.rs +++ b/crates/core/src/backend/node.rs @@ -1,6 +1,5 @@ use std::{borrow::Cow, cmp::Ordering, fmt::Debug}; -#[cfg(not(windows))] use std::fmt::Write; #[cfg(not(windows))] use std::num::ParseIntError; @@ -24,7 +23,6 @@ use crate::blob::{DataId, tree::TreeId}; #[non_exhaustive] pub enum NodeErrorKind<'a> { /// Unexpected EOF while parsing filename: `{file_name}` - #[cfg(not(windows))] UnexpectedEOF { /// The filename file_name: String, @@ -32,7 +30,6 @@ pub enum NodeErrorKind<'a> { chars: std::str::Chars<'a>, }, /// Invalid unicode - #[cfg(not(windows))] InvalidUnicode { /// The filename file_name: String, @@ -42,7 +39,6 @@ pub enum NodeErrorKind<'a> { chars: std::str::Chars<'a>, }, /// Unrecognized Escape while parsing filename: `{file_name}` - #[cfg(not(windows))] UnrecognizedEscape { /// The filename file_name: String, @@ -50,7 +46,6 @@ pub enum NodeErrorKind<'a> { chars: std::str::Chars<'a>, }, /// Parsing hex chars {chars:?} failed for `{hex}` in filename: `{file_name}` : `{source}` - #[cfg(not(windows))] ParsingHexFailed { /// The filename file_name: String, @@ -62,7 +57,6 @@ pub enum NodeErrorKind<'a> { source: ParseIntError, }, /// Parsing unicode chars {chars:?} failed for `{target}` in filename: `{file_name}` : `{source}` - #[cfg(not(windows))] ParsingUnicodeFailed { /// The filename file_name: String, From b8516d229dccbc02c80237ba74025818a857be49 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sat, 11 Jan 2025 23:32:31 +0100 Subject: [PATCH 05/22] more windows fixes --- crates/core/src/backend/ignore.rs | 23 ++++++++++------------- crates/core/src/backend/node.rs | 1 - 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/crates/core/src/backend/ignore.rs b/crates/core/src/backend/ignore.rs index bf7acecf..714e36c0 100644 --- a/crates/core/src/backend/ignore.rs +++ b/crates/core/src/backend/ignore.rs @@ -499,12 +499,11 @@ fn map_entry( Node::new_node(name, NodeType::Dir, meta) } else if m.is_symlink() { let path = entry.path(); - let target = read_link(path) - .map_err(|err| IgnoreErrorKind::ErrorLink { - path: path.to_path_buf(), - source: err, - })? - .as_encoded_bytes(); + let target = read_link(path).map_err(|err| IgnoreErrorKind::ErrorLink { + path: path.to_path_buf(), + source: err, + })?; + let target = target.as_os_str().as_encoded_bytes(); let node_type = NodeType::from_link(&target); Node::new_node(name, node_type, meta) } else { @@ -688,13 +687,11 @@ fn map_entry( Node::new_node(name, NodeType::Dir, meta) } else if m.is_symlink() { let path = entry.path(); - let target = read_link(path) - .map_err(|err| IgnoreErrorKind::ErrorLink { - path: path.to_path_buf(), - source: err, - })? - .into_os_string(); - let target = target.as_encoded_bytes(); + let target = read_link(path).map_err(|err| IgnoreErrorKind::ErrorLink { + path: path.to_path_buf(), + source: err, + })?; + let target = target.as_os_str().as_encoded_bytes(); let node_type = NodeType::from_link(&UnixPath::new(target).to_typed_path()); Node::new_node(name, node_type, meta) } else if filetype.is_block_device() { diff --git a/crates/core/src/backend/node.rs b/crates/core/src/backend/node.rs index 14882273..036ec468 100644 --- a/crates/core/src/backend/node.rs +++ b/crates/core/src/backend/node.rs @@ -1,7 +1,6 @@ use std::{borrow::Cow, cmp::Ordering, fmt::Debug}; use std::fmt::Write; -#[cfg(not(windows))] use std::num::ParseIntError; use chrono::{DateTime, Local}; From c71e783b57128e5ee962d53fe77f102aa502984a Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sat, 11 Jan 2025 23:35:57 +0100 Subject: [PATCH 06/22] more windows fixes --- crates/core/src/backend/ignore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/src/backend/ignore.rs b/crates/core/src/backend/ignore.rs index 714e36c0..5167d02e 100644 --- a/crates/core/src/backend/ignore.rs +++ b/crates/core/src/backend/ignore.rs @@ -504,7 +504,7 @@ fn map_entry( source: err, })?; let target = target.as_os_str().as_encoded_bytes(); - let node_type = NodeType::from_link(&target); + let node_type = NodeType::from_link(&UnixPath::new(target).to_typed_path()); Node::new_node(name, node_type, meta) } else { Node::new_node(name, NodeType::File, meta) From 377d52fa06a34e2ced15cdfab890a76e838ec5c2 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sun, 12 Jan 2025 09:14:17 +0100 Subject: [PATCH 07/22] add util::GlobMatcherExt --- crates/core/Cargo.toml | 4 ++-- crates/core/src/lib.rs | 1 + crates/core/src/util.rs | 29 +++++++++++++++++++++++++++ crates/core/tests/integration/find.rs | 21 ++++++------------- examples/find/examples/find.rs | 8 +++----- 5 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 crates/core/src/util.rs diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 7fac30d0..8b9d5cd0 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -97,8 +97,9 @@ chrono = { version = "0.4.42", default-features = false, features = ["clock", "s ecow = "0.2.6" enum-map = { workspace = true } enum-map-derive = "0.17.0" -enumset = { version = "1.1.10", features = ["serde"] } +enumset = { version = "1.1.5", features = ["serde"] } gethostname = "1.0.2" +globset = "0.4.16" humantime = "2.3.0" itertools = "0.14.0" quick_cache = "0.6.16" @@ -123,7 +124,6 @@ xattr = "1" anyhow = { workspace = true } expect-test = "1.5.1" flate2 = "1.1.2" -globset = "0.4.16" insta = { version = "1.43.2", features = ["redactions", "ron"] } mockall = "0.13" pretty_assertions = "1.4.1" diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 01dbcd2d..c3c8f215 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -117,6 +117,7 @@ pub(crate) mod progress; /// Structs which are saved in JSON or binary format in the repository pub mod repofile; pub(crate) mod repository; +pub mod util; /// Virtual File System support - allows to act on the repository like on a file system pub mod vfs; diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs new file mode 100644 index 00000000..74791af8 --- /dev/null +++ b/crates/core/src/util.rs @@ -0,0 +1,29 @@ +use globset::GlobMatcher; + +/// Extend `globset::GlobMatcher` to allow mathing on unix paths (on every platform) +pub trait GlobMatcherExt { + /// Match on unix paths, i.e. paths which are available as `&[u8]` + fn is_unix_match(&self, path: impl AsRef<[u8]>) -> bool; +} + +impl GlobMatcherExt for GlobMatcher { + // This is a hacky implementation, espeically for windows where we convert lossily + // into an utf8 string and match on the windows path given by that string. + // Note: `GlobMatcher` internally converts into a `&[u8]` to perform the matching + // TODO: Use https://github.com/BurntSushi/ripgrep/pull/2955 once it is available. + #[cfg(not(windows))] + fn is_unix_match(&self, path: impl AsRef<[u8]>) -> bool { + use std::{ffi::OsStr, os::unix::ffi::OsStrExt, path::PathBuf}; + + let path = PathBuf::from(OsStr::from_bytes(path.as_ref())); + self.is_match(&path) + } + #[cfg(windows)] + fn is_unix_match(&self, path: impl AsRef<[u8]>) -> bool { + use std::{ffi::OsStr, path::Path}; + + let string: &str = &String::from_utf8_lossy(path.as_ref()); + let path = Path::new(OsStr::new(string)); + self.is_match(path) + } +} diff --git a/crates/core/tests/integration/find.rs b/crates/core/tests/integration/find.rs index 0cfe5e86..cee7d79b 100644 --- a/crates/core/tests/integration/find.rs +++ b/crates/core/tests/integration/find.rs @@ -1,10 +1,7 @@ // don't warn about try_from paths conversion on unix #![allow(clippy::unnecessary_fallible_conversions)] -use std::{ - path::{Path, PathBuf}, - str::FromStr, -}; +use std::{path::PathBuf, str::FromStr}; use anyhow::Result; use globset::Glob; @@ -14,6 +11,7 @@ use rstest::rstest; use rustic_core::{ BackupOptions, FindMatches, FindNode, repofile::{Node, SnapshotFile}, + util::GlobMatcherExt, }; use typed_path::UnixPath; @@ -39,9 +37,8 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) assert_with_win("find-nodes-not-found", not_found); // test non-existing match let glob = Glob::new("not_existing")?.compile_matcher(); - let not_found = repo.find_matching_nodes(vec![snapshot.tree], &|path, _| { - glob.is_match(PathBuf::try_from(path).unwrap()) - })?; + let not_found = + repo.find_matching_nodes(vec![snapshot.tree], &|path, _| glob.is_unix_match(path))?; assert_debug_snapshot!("find-matching-nodes-not-found", not_found); // test existing path @@ -51,10 +48,7 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) // test existing match let glob = Glob::new("testfile")?.compile_matcher(); let match_func = |path: &UnixPath, _: &Node| { - glob.is_match(PathBuf::try_from(path).unwrap()) - || path - .file_name() - .is_some_and(|f| glob.is_match(Path::new(&String::from_utf8(f.to_vec()).unwrap()))) + glob.is_unix_match(path) || path.file_name().is_some_and(|f| glob.is_unix_match(f)) }; let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; @@ -62,10 +56,7 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) // test existing match let glob = Glob::new("testfile*")?.compile_matcher(); let match_func = |path: &UnixPath, _: &Node| { - glob.is_match(PathBuf::try_from(path).unwrap()) - || path - .file_name() - .is_some_and(|f| glob.is_match(Path::new(&String::from_utf8(f.to_vec()).unwrap()))) + glob.is_unix_match(path) || path.file_name().is_some_and(|f| glob.is_unix_match(f)) }; let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; diff --git a/examples/find/examples/find.rs b/examples/find/examples/find.rs index df9b48da..b5c1854d 100644 --- a/examples/find/examples/find.rs +++ b/examples/find/examples/find.rs @@ -1,9 +1,9 @@ //! `ls` example use globset::Glob; use rustic_backend::BackendOptions; -use rustic_core::{FindMatches, Repository, RepositoryOptions}; +use rustic_core::{util::GlobMatcherExt, FindMatches, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; -use std::{error::Error, path::PathBuf}; +use std::error::Error; // don't warn about try_from paths conversion on unix #[allow(clippy::unnecessary_fallible_conversions)] @@ -32,9 +32,7 @@ fn main() -> Result<(), Box> { paths, nodes, matches, - } = repo.find_matching_nodes(tree_ids, &|path, _| { - glob.is_match(PathBuf::try_from(path).unwrap()) - })?; + } = repo.find_matching_nodes(tree_ids, &|path, _| glob.is_unix_match(path))?; for (snap, matches) in snapshots.iter().zip(matches) { println!("results in {snap:?}"); for (path_idx, node_idx) in matches { From d3bb945b39405570821c1690ffa260dfe2679d6e Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sun, 12 Jan 2025 17:00:10 +0100 Subject: [PATCH 08/22] handle as_path correctly --- crates/core/src/archiver.rs | 7 ++---- crates/core/src/backend/ignore.rs | 20 ++++++++++++++++- crates/core/src/commands/backup.rs | 35 ++++++++---------------------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/crates/core/src/archiver.rs b/crates/core/src/archiver.rs index 4df183b5..b1e1d55c 100644 --- a/crates/core/src/archiver.rs +++ b/crates/core/src/archiver.rs @@ -6,7 +6,7 @@ pub(crate) mod tree_archiver; use chrono::Local; use log::warn; use pariter::{IteratorExt, scope}; -use typed_path::{UnixPath, UnixPathBuf}; +use typed_path::UnixPathBuf; use crate::{ Progress, @@ -125,7 +125,6 @@ impl<'a, BE: DecryptFullBackend, I: ReadGlobalIndex> Archiver<'a, BE, I> { pub fn archive( mut self, src: &R, - backup_path: &UnixPath, as_path: Option<&UnixPathBuf>, skip_identical_parent: bool, no_scan: bool, @@ -156,9 +155,7 @@ impl<'a, BE: DecryptFullBackend, I: ReadGlobalIndex> Archiver<'a, BE, I> { } Ok(ReadSourceEntry { path, node, open }) => { let snapshot_path = if let Some(as_path) = as_path { - as_path - .clone() - .join(path.strip_prefix(backup_path).unwrap()) + as_path.clone().join(path) } else { path }; diff --git a/crates/core/src/backend/ignore.rs b/crates/core/src/backend/ignore.rs index 5167d02e..8edce2f4 100644 --- a/crates/core/src/backend/ignore.rs +++ b/crates/core/src/backend/ignore.rs @@ -66,6 +66,7 @@ pub(crate) type IgnoreResult = Result; /// A [`LocalSource`] is a source from local paths which is used to be read from (i.e. to backup it). #[derive(Debug)] pub struct LocalSource { + base_path: PathBuf, /// The walk builder. builder: WalkBuilder, /// The save options to use. @@ -310,7 +311,17 @@ impl LocalSource { let builder = walk_builder; - Ok(Self { builder, save_opts }) + let base_path = if backup_paths.len() == 1 { + backup_paths[0].as_ref().to_path_buf() + } else { + PathBuf::new() + }; + + Ok(Self { + base_path, + builder, + save_opts, + }) } } @@ -375,6 +386,7 @@ impl ReadSource for LocalSource { /// An iterator over the entries of the local source. fn entries(&self) -> Self::Iter { LocalSourceWalker { + base_path: self.base_path.clone(), walker: self.builder.build(), save_opts: self.save_opts, } @@ -384,6 +396,7 @@ impl ReadSource for LocalSource { // Walk doesn't implement Debug #[allow(missing_debug_implementations)] pub struct LocalSourceWalker { + base_path: PathBuf, /// The walk iterator. walker: Walk, /// The save options to use. @@ -411,6 +424,7 @@ impl Iterator for LocalSourceWalker { ) .ask_report() })?, + &self.base_path, self.save_opts.with_atime, self.save_opts.ignore_devid, ) @@ -442,6 +456,7 @@ impl Iterator for LocalSourceWalker { #[allow(clippy::similar_names)] fn map_entry( entry: DirEntry, + base_path: &Path, with_atime: bool, _ignore_devid: bool, ) -> IgnoreResult> { @@ -512,6 +527,7 @@ fn map_entry( let path = entry.into_path(); let open = Some(OpenFile(path.clone())); + let path = path.strip_prefix(base_path).unwrap(); let path = UnixPath::new(path.as_os_str().as_encoded_bytes()).to_path_buf(); Ok(ReadSourceEntry { path, node, open }) } @@ -610,6 +626,7 @@ fn list_extended_attributes(path: &Path) -> IgnoreResult> #[allow(clippy::similar_names)] fn map_entry( entry: DirEntry, + base_path: &Path, with_atime: bool, ignore_devid: bool, ) -> IgnoreResult> { @@ -709,6 +726,7 @@ fn map_entry( }; let path = entry.into_path(); let open = Some(OpenFile(path.clone())); + let path = path.strip_prefix(base_path).unwrap(); let path = UnixPath::new(path.as_os_str().as_encoded_bytes()).to_path_buf(); Ok(ReadSourceEntry { path, node, open }) } diff --git a/crates/core/src/commands/backup.rs b/crates/core/src/commands/backup.rs index bc885833..6948c1bd 100644 --- a/crates/core/src/commands/backup.rs +++ b/crates/core/src/commands/backup.rs @@ -1,11 +1,10 @@ //! `backup` subcommand use derive_setters::Setters; use log::info; -use typed_path::UnixPathBuf; +use typed_path::{UnixPath, UnixPathBuf}; use std::path::PathBuf; -use path_dedot::ParseDot; use serde_derive::{Deserialize, Serialize}; use serde_with::{DisplayFromStr, serde_as}; @@ -222,24 +221,12 @@ pub(crate) fn backup( source.paths() }; - let as_path: Option = opts - .as_path - .as_ref() - .map(|p| -> RusticResult<_> { - Ok(p.parse_dot() - .map_err(|err| { - RusticError::with_source( - ErrorKind::InvalidInput, - "Failed to parse dotted path `{path}`", - err, - ) - .attach_context("path", p.display().to_string()) - })? - .to_path_buf() - .try_into() - .unwrap()) // TODO: error handling - }) - .transpose()?; + let as_path = match &opts.as_path { + Some(p) => Some(p), + None if backup_path.len() == 1 => Some(&backup_path[0]), + None => None, + } + .map(|p| UnixPath::new(p.as_os_str().as_encoded_bytes()).normalize()); match &as_path { Some(p) => snap @@ -299,10 +286,9 @@ pub(crate) fn backup( let path = &backup_path[0]; if let Some(command) = &opts.stdin_command { let unix_path: UnixPathBuf = path.clone().try_into().unwrap(); // todo: error handling - let src = ChildStdoutSource::new(command, unix_path.clone())?; + let src = ChildStdoutSource::new(command, unix_path)?; let res = archiver.archive( &src, - &unix_path, as_path.as_ref(), opts.parent_opts.skip_if_unchanged, opts.no_scan, @@ -312,10 +298,9 @@ pub(crate) fn backup( res } else { let path: UnixPathBuf = path.clone().try_into().unwrap(); // TODO: error handling - let src = StdinSource::new(path.clone()); + let src = StdinSource::new(path); archiver.archive( &src, - &path, as_path.as_ref(), opts.parent_opts.skip_if_unchanged, opts.no_scan, @@ -328,10 +313,8 @@ pub(crate) fn backup( &opts.ignore_filter_opts, &backup_path, )?; - let backup_path: UnixPathBuf = backup_path[0].clone().try_into().unwrap(); archiver.archive( &src, - &backup_path, as_path.as_ref(), opts.parent_opts.skip_if_unchanged, opts.no_scan, From 06b086a1afe2663873e7bbbfee94b3abd8843e69 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 13 Jan 2025 11:42:58 +0100 Subject: [PATCH 09/22] fix unix tests --- crates/core/src/commands/backup.rs | 2 +- crates/core/src/util.rs | 15 +++++++++++++++ crates/core/tests/integration/find.rs | 14 ++++++++------ crates/core/tests/integration/ls.rs | 11 ++++++++--- crates/core/tests/integration/vfs.rs | 2 +- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/crates/core/src/commands/backup.rs b/crates/core/src/commands/backup.rs index 6948c1bd..989852e7 100644 --- a/crates/core/src/commands/backup.rs +++ b/crates/core/src/commands/backup.rs @@ -223,7 +223,7 @@ pub(crate) fn backup( let as_path = match &opts.as_path { Some(p) => Some(p), - None if backup_path.len() == 1 => Some(&backup_path[0]), + None if !backup_stdin && backup_path.len() == 1 => Some(&backup_path[0]), None => None, } .map(|p| UnixPath::new(p.as_os_str().as_encoded_bytes()).normalize()); diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 74791af8..d3d9e5d1 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -1,4 +1,6 @@ use globset::GlobMatcher; +use serde::{Serialize, Serializer}; +use typed_path::{UnixPath, UnixPathBuf}; /// Extend `globset::GlobMatcher` to allow mathing on unix paths (on every platform) pub trait GlobMatcherExt { @@ -27,3 +29,16 @@ impl GlobMatcherExt for GlobMatcher { self.is_match(path) } } + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)] +#[serde(transparent)] +/// Like `UnixPathBuf` , but implements `Serialize` +pub struct SerializablePath(#[serde(serialize_with = "serialize_unix_path")] pub UnixPathBuf); + +fn serialize_unix_path(path: &UnixPath, serializer: S) -> Result +where + S: Serializer, +{ + let s = format!("{}", path.display()); + serializer.serialize_str(&s) +} diff --git a/crates/core/tests/integration/find.rs b/crates/core/tests/integration/find.rs index cee7d79b..4e15cd74 100644 --- a/crates/core/tests/integration/find.rs +++ b/crates/core/tests/integration/find.rs @@ -5,13 +5,12 @@ use std::{path::PathBuf, str::FromStr}; use anyhow::Result; use globset::Glob; -use insta::assert_debug_snapshot; use rstest::rstest; use rustic_core::{ BackupOptions, FindMatches, FindNode, repofile::{Node, SnapshotFile}, - util::GlobMatcherExt, + util::{GlobMatcherExt, SerializablePath}, }; use typed_path::UnixPath; @@ -37,9 +36,10 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) assert_with_win("find-nodes-not-found", not_found); // test non-existing match let glob = Glob::new("not_existing")?.compile_matcher(); - let not_found = + let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &|path, _| glob.is_unix_match(path))?; - assert_debug_snapshot!("find-matching-nodes-not-found", not_found); + assert!(paths.is_empty()); + assert_eq!(matches, [[]]); // test existing path let FindNode { matches, .. } = @@ -52,7 +52,8 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) }; let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; - assert_debug_snapshot!("find-matching-existing", (paths, matches)); + let paths: Vec<_> = paths.into_iter().map(SerializablePath).collect(); + assert_with_win("find-matching-existing", (paths, matches)); // test existing match let glob = Glob::new("testfile*")?.compile_matcher(); let match_func = |path: &UnixPath, _: &Node| { @@ -60,6 +61,7 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) }; let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; - assert_debug_snapshot!("find-matching-wildcard-existing", (paths, matches)); + let paths: Vec<_> = paths.into_iter().map(SerializablePath).collect(); + assert_with_win("find-matching-wildcard-existing", (paths, matches)); Ok(()) } diff --git a/crates/core/tests/integration/ls.rs b/crates/core/tests/integration/ls.rs index 3274032c..2b2c2160 100644 --- a/crates/core/tests/integration/ls.rs +++ b/crates/core/tests/integration/ls.rs @@ -2,15 +2,19 @@ use std::collections::BTreeMap; use std::{path::PathBuf, str::FromStr}; use anyhow::Result; -use insta::{Settings, assert_debug_snapshot}; +use insta::Settings; +use itertools::Itertools; use rstest::rstest; use rustic_core::{ BackupOptions, LsOptions, RusticResult, repofile::{Metadata, Node, SnapshotFile}, + util::SerializablePath, }; -use super::{RepoOpen, TestSource, insta_node_redaction, set_up_repo, tar_gz_testdata}; +use super::{ + RepoOpen, TestSource, assert_with_win, insta_node_redaction, set_up_repo, tar_gz_testdata, +}; #[rstest] fn test_ls( @@ -40,10 +44,11 @@ fn test_ls( let entries: BTreeMap<_, _> = repo .ls(&node, &LsOptions::default())? + .map_ok(|(path, node)| (SerializablePath(path), node)) .collect::>()?; insta_node_redaction.bind(|| { - assert_debug_snapshot!("ls", &entries); + assert_with_win("ls", &entries); }); Ok(()) diff --git a/crates/core/tests/integration/vfs.rs b/crates/core/tests/integration/vfs.rs index e4d14823..f58685fb 100644 --- a/crates/core/tests/integration/vfs.rs +++ b/crates/core/tests/integration/vfs.rs @@ -35,7 +35,7 @@ fn test_vfs( let vfs = Vfs::from_dir_node(&node); // test reading a directory using vfs - let entries = vfs.dir_entries_from_path(&repo, UnixPath::new("test/0/tests/testfile"))?; + let entries = vfs.dir_entries_from_path(&repo, UnixPath::new("test/0/tests"))?; insta_node_redaction.bind(|| { assert_with_win("vfs", &entries); }); From d614f110454ae93bc948c2c826c3c5bd044ea0d1 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 13 Jan 2025 12:42:48 +0100 Subject: [PATCH 10/22] correctly convert windows into unix path --- crates/core/src/backend/ignore.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/core/src/backend/ignore.rs b/crates/core/src/backend/ignore.rs index 8edce2f4..6817dffc 100644 --- a/crates/core/src/backend/ignore.rs +++ b/crates/core/src/backend/ignore.rs @@ -460,6 +460,8 @@ fn map_entry( with_atime: bool, _ignore_devid: bool, ) -> IgnoreResult> { + use typed_path::{UnixEncoding, WindowsPath}; + let name = entry.file_name().as_encoded_bytes(); let m = entry .metadata() @@ -528,7 +530,9 @@ fn map_entry( let path = entry.into_path(); let open = Some(OpenFile(path.clone())); let path = path.strip_prefix(base_path).unwrap(); - let path = UnixPath::new(path.as_os_str().as_encoded_bytes()).to_path_buf(); + let path = WindowsPath::new(path.as_os_str().as_encoded_bytes()) + .to_path_buf() + .with_encoding::(); Ok(ReadSourceEntry { path, node, open }) } From 080349545a1ca3c2210b24925bb78cc6c4dac503 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 13 Jan 2025 15:02:37 +0100 Subject: [PATCH 11/22] update windwos snapshots --- ...ation__find-matching-existing-windows.snap | 2 +- .../snapshots/integration__ls-windows.snap | 154 +++++++++--------- 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/crates/core/tests/snapshots/integration__find-matching-existing-windows.snap b/crates/core/tests/snapshots/integration__find-matching-existing-windows.snap index 174a9544..8e3b67a1 100644 --- a/crates/core/tests/snapshots/integration__find-matching-existing-windows.snap +++ b/crates/core/tests/snapshots/integration__find-matching-existing-windows.snap @@ -3,7 +3,7 @@ source: crates/core/tests/integration.rs expression: snap --- ([ - "test\\0\\tests\\testfile", + "test/0/tests/testfile", ], [ [ (0, 0), diff --git a/crates/core/tests/snapshots/integration__ls-windows.snap b/crates/core/tests/snapshots/integration__ls-windows.snap index 714b0ecb..871f8bab 100644 --- a/crates/core/tests/snapshots/integration__ls-windows.snap +++ b/crates/core/tests/snapshots/integration__ls-windows.snap @@ -10,7 +10,7 @@ expression: snap "content": None, "subtree": "[some]", }, - "test\\0": { + "test/0": { "name": "0", "type": "dir", "mtime": "[some]", @@ -19,7 +19,7 @@ expression: snap "content": None, "subtree": "[some]", }, - "test\\0\\0": { + "test/0/0": { "name": "0", "type": "dir", "mtime": "[some]", @@ -28,7 +28,7 @@ expression: snap "content": None, "subtree": "[some]", }, - "test\\0\\0\\9": { + "test/0/0/9": { "name": "9", "type": "dir", "mtime": "[some]", @@ -37,7 +37,7 @@ expression: snap "content": None, "subtree": "[some]", }, - "test\\0\\0\\9\\0": { + "test/0/0/9/0": { "name": "0", "type": "file", "mtime": "[some]", @@ -48,7 +48,7 @@ expression: snap Id("aa6da76dc4b3bd8d8cd9d42c5ed6db2e9c3e43cecef7e7ee925a3accab96ad79"), ]), }, - "test\\0\\0\\9\\1": { + "test/0/0/9/1": { "name": "1", "type": "file", "mtime": "[some]", @@ -59,7 +59,7 @@ expression: snap Id("bd15c2d9117aa305a0cf99c9cc30359b66ee454b00b897fb1a925c56025f4556"), ]), }, - "test\\0\\0\\9\\10": { + "test/0/0/9/10": { "name": "10", "type": "file", "mtime": "[some]", @@ -70,7 +70,7 @@ expression: snap Id("753d116d34c772059a9a4a2ca33f6ea9ac618470acc13687d08fff7eb5ad3c72"), ]), }, - "test\\0\\0\\9\\11": { + "test/0/0/9/11": { "name": "11", "type": "file", "mtime": "[some]", @@ -81,7 +81,7 @@ expression: snap Id("efe42dd90fd602df1c5b3b7675cd845e23befba069eac0222a9997fea2a9852f"), ]), }, - "test\\0\\0\\9\\12": { + "test/0/0/9/12": { "name": "12", "type": "file", "mtime": "[some]", @@ -92,7 +92,7 @@ expression: snap Id("032ad465747269b41dbdfc87accad1fd49e8dc574ae4bfaf49f036150ec92e57"), ]), }, - "test\\0\\0\\9\\13": { + "test/0/0/9/13": { "name": "13", "type": "file", "mtime": "[some]", @@ -103,7 +103,7 @@ expression: snap Id("b0b5afc29b6391722bd60a9a74c386c856cb130a219954f34944a4f19bbecc41"), ]), }, - "test\\0\\0\\9\\14": { + "test/0/0/9/14": { "name": "14", "type": "file", "mtime": "[some]", @@ -114,7 +114,7 @@ expression: snap Id("b19a49a2e711d5547e1fb247170c58d2da17f9b0bbf75eb84507097a301f1c6d"), ]), }, - "test\\0\\0\\9\\15": { + "test/0/0/9/15": { "name": "15", "type": "file", "mtime": "[some]", @@ -125,7 +125,7 @@ expression: snap Id("f11e72e0290dc75261ee48c39fd42597bdcd80e110ffd0651f71d54ea22030e5"), ]), }, - "test\\0\\0\\9\\16": { + "test/0/0/9/16": { "name": "16", "type": "file", "mtime": "[some]", @@ -136,7 +136,7 @@ expression: snap Id("71935a6f85198c65584ed5c4ff5e34cf93cb79756023e82ecc85153663be2b00"), ]), }, - "test\\0\\0\\9\\17": { + "test/0/0/9/17": { "name": "17", "type": "file", "mtime": "[some]", @@ -147,7 +147,7 @@ expression: snap Id("b81f6dd6ad54fbed57ffac74bcecf9f1a56ce8948edc92153ee34ed415ed68d1"), ]), }, - "test\\0\\0\\9\\18": { + "test/0/0/9/18": { "name": "18", "type": "file", "mtime": "[some]", @@ -158,7 +158,7 @@ expression: snap Id("1fad45c4bf21695fae9e100626f42d35d8c2c48cb90f6a6256973c262608879d"), ]), }, - "test\\0\\0\\9\\19": { + "test/0/0/9/19": { "name": "19", "type": "file", "mtime": "[some]", @@ -169,7 +169,7 @@ expression: snap Id("92c4aa4fa67ede99b9ab1b694bc32b4f3d127f406a7e6583ecbe55b1bb919928"), ]), }, - "test\\0\\0\\9\\2": { + "test/0/0/9/2": { "name": "2", "type": "file", "mtime": "[some]", @@ -180,7 +180,7 @@ expression: snap Id("f5a3517234ef2f213afe617cdab9d25364e1c8c9e61198bf387b157542ee7dbb"), ]), }, - "test\\0\\0\\9\\20": { + "test/0/0/9/20": { "name": "20", "type": "file", "mtime": "[some]", @@ -191,7 +191,7 @@ expression: snap Id("737c19313675671c589bfc613b0b736b95a58218486d09d3a7a649f324d81ef7"), ]), }, - "test\\0\\0\\9\\21": { + "test/0/0/9/21": { "name": "21", "type": "file", "mtime": "[some]", @@ -202,7 +202,7 @@ expression: snap Id("ee25130a4e74b1306087799914d8b92aed2c385b06256705b18266ff088524f4"), ]), }, - "test\\0\\0\\9\\22": { + "test/0/0/9/22": { "name": "22", "type": "file", "mtime": "[some]", @@ -213,7 +213,7 @@ expression: snap Id("3492c28f8ddbb9186e483ef9eb1cc7042c5adf871a4d15d2edf8ee7b2b04069b"), ]), }, - "test\\0\\0\\9\\23": { + "test/0/0/9/23": { "name": "23", "type": "file", "mtime": "[some]", @@ -224,7 +224,7 @@ expression: snap Id("b321dedc16124312ed08cb7f9c67006bd6a4f946ca04bc178323e6147b081943"), ]), }, - "test\\0\\0\\9\\24": { + "test/0/0/9/24": { "name": "24", "type": "file", "mtime": "[some]", @@ -235,7 +235,7 @@ expression: snap Id("7020db85ffe6a3fd5f34971ff37a20fa5d32947181692bfbcca62cfc4a9221d0"), ]), }, - "test\\0\\0\\9\\25": { + "test/0/0/9/25": { "name": "25", "type": "file", "mtime": "[some]", @@ -246,7 +246,7 @@ expression: snap Id("75e7274ef54e91c2857ba0de4c869388df3f9c657d776a3e90eaf3f3f619536d"), ]), }, - "test\\0\\0\\9\\26": { + "test/0/0/9/26": { "name": "26", "type": "file", "mtime": "[some]", @@ -257,7 +257,7 @@ expression: snap Id("418b691988d9bc7357b99c5baf31e83436315d1bcc887482dd83a8537183af17"), ]), }, - "test\\0\\0\\9\\27": { + "test/0/0/9/27": { "name": "27", "type": "file", "mtime": "[some]", @@ -268,7 +268,7 @@ expression: snap Id("06a5749af5715af19fde59d1caaa542df00c56aa0058bf99b67dec5216c27df6"), ]), }, - "test\\0\\0\\9\\28": { + "test/0/0/9/28": { "name": "28", "type": "file", "mtime": "[some]", @@ -279,7 +279,7 @@ expression: snap Id("61e8ffda5a11d743d7dc4bcfd1e18e8b6179a53f8b28b25adf55d3acb0b10eff"), ]), }, - "test\\0\\0\\9\\29": { + "test/0/0/9/29": { "name": "29", "type": "file", "mtime": "[some]", @@ -290,7 +290,7 @@ expression: snap Id("60c1c2f3a0b8e5b1c4e2b7f1453661170395c5c26f2e2d241e81eb929cfc930f"), ]), }, - "test\\0\\0\\9\\3": { + "test/0/0/9/3": { "name": "3", "type": "file", "mtime": "[some]", @@ -301,7 +301,7 @@ expression: snap Id("f0ad279385b5b5c862e1e2fdd599616115729e0b8085833d7b31de68c8326b85"), ]), }, - "test\\0\\0\\9\\30": { + "test/0/0/9/30": { "name": "30", "type": "file", "mtime": "[some]", @@ -312,7 +312,7 @@ expression: snap Id("790c34f1d4d95a6067f2406a78aa0dcca2fc2aa1db932fbd865ce9cafd7baa23"), ]), }, - "test\\0\\0\\9\\31": { + "test/0/0/9/31": { "name": "31", "type": "file", "mtime": "[some]", @@ -323,7 +323,7 @@ expression: snap Id("8e53f45db9249373828c588774471d34c9c1e436b83b1603a49d675de810db7e"), ]), }, - "test\\0\\0\\9\\32": { + "test/0/0/9/32": { "name": "32", "type": "file", "mtime": "[some]", @@ -334,7 +334,7 @@ expression: snap Id("c4eccf95ba2409d21283981297bf9919ec15c2cc0dadef84aafaad49a0c79130"), ]), }, - "test\\0\\0\\9\\33": { + "test/0/0/9/33": { "name": "33", "type": "file", "mtime": "[some]", @@ -345,7 +345,7 @@ expression: snap Id("da8b8b2d79bba6a3d4784233e4421293cea0cf27fd03521e11ce0c7c941f6dbb"), ]), }, - "test\\0\\0\\9\\34": { + "test/0/0/9/34": { "name": "34", "type": "file", "mtime": "[some]", @@ -356,7 +356,7 @@ expression: snap Id("a1413ef1018e7a4f3df7f19aad942a611cbda5bdd45d4269cb54d5f9fa58cd01"), ]), }, - "test\\0\\0\\9\\35": { + "test/0/0/9/35": { "name": "35", "type": "file", "mtime": "[some]", @@ -367,7 +367,7 @@ expression: snap Id("f534a7fe839d440a0fdd1d45254921601b2a90ff5b61edcdc631b722f84a0dbb"), ]), }, - "test\\0\\0\\9\\36": { + "test/0/0/9/36": { "name": "36", "type": "file", "mtime": "[some]", @@ -378,7 +378,7 @@ expression: snap Id("d56e2a8c47f4ae36886784c852916f36036f3daffa547a5922dda5814ecc5a86"), ]), }, - "test\\0\\0\\9\\37": { + "test/0/0/9/37": { "name": "37", "type": "file", "mtime": "[some]", @@ -389,7 +389,7 @@ expression: snap Id("5800df73dcfdbd261800f2474c8631fe0727fbc1788cfd70bd206ab2f50b0707"), ]), }, - "test\\0\\0\\9\\38": { + "test/0/0/9/38": { "name": "38", "type": "file", "mtime": "[some]", @@ -400,7 +400,7 @@ expression: snap Id("e7c987bd8d9257bea1e44f882bc075c84ee7f103d68eec8677e37b49d2854d90"), ]), }, - "test\\0\\0\\9\\39": { + "test/0/0/9/39": { "name": "39", "type": "file", "mtime": "[some]", @@ -411,7 +411,7 @@ expression: snap Id("03a1411035add5b10ed183b2ca7d8db8deee2402f45ee445f54de50896c3e26f"), ]), }, - "test\\0\\0\\9\\4": { + "test/0/0/9/4": { "name": "4", "type": "file", "mtime": "[some]", @@ -422,7 +422,7 @@ expression: snap Id("917aae16764fc905abf0514e965133ed80e2b66c426fb09be24eabd452d4438d"), ]), }, - "test\\0\\0\\9\\40": { + "test/0/0/9/40": { "name": "40", "type": "file", "mtime": "[some]", @@ -433,7 +433,7 @@ expression: snap Id("1adcd19cf770f7d8ab3bb4b3996fb6187f59c468953848ec4f119c9cef484f28"), ]), }, - "test\\0\\0\\9\\41": { + "test/0/0/9/41": { "name": "41", "type": "file", "mtime": "[some]", @@ -444,7 +444,7 @@ expression: snap Id("b5debda793f22adb93204dc81f107d03636b7431192fc8497f5c9a340e43460c"), ]), }, - "test\\0\\0\\9\\42": { + "test/0/0/9/42": { "name": "42", "type": "file", "mtime": "[some]", @@ -455,7 +455,7 @@ expression: snap Id("e6e9b35f3ca77afbd0043bc42085b814ceea8cfb93a0bcba9696dee5034ffead"), ]), }, - "test\\0\\0\\9\\43": { + "test/0/0/9/43": { "name": "43", "type": "file", "mtime": "[some]", @@ -466,7 +466,7 @@ expression: snap Id("31d87fd77a88ca00e9c5612f578c0b3243347ba328a6daa09742fdc8c7cec6f4"), ]), }, - "test\\0\\0\\9\\44": { + "test/0/0/9/44": { "name": "44", "type": "file", "mtime": "[some]", @@ -477,7 +477,7 @@ expression: snap Id("76aec0f2d829d01eff332099ebd5628f61adc72ff44e7d6b82beabc28fa44f1f"), ]), }, - "test\\0\\0\\9\\45": { + "test/0/0/9/45": { "name": "45", "type": "file", "mtime": "[some]", @@ -488,7 +488,7 @@ expression: snap Id("121242341ff94a05074a31cb2a0f566093e5c052e64dcde4c0c9b686e58daf87"), ]), }, - "test\\0\\0\\9\\46": { + "test/0/0/9/46": { "name": "46", "type": "file", "mtime": "[some]", @@ -499,7 +499,7 @@ expression: snap Id("a69efe0ab923672b5aa1a2113f54e80684a7c126c1114af4fac3953be18bea74"), ]), }, - "test\\0\\0\\9\\47": { + "test/0/0/9/47": { "name": "47", "type": "file", "mtime": "[some]", @@ -510,7 +510,7 @@ expression: snap Id("a50a7ab010d0d349b213576d1e2d2d2b345334be1be6f62798d250fc1e5cc51f"), ]), }, - "test\\0\\0\\9\\48": { + "test/0/0/9/48": { "name": "48", "type": "file", "mtime": "[some]", @@ -521,7 +521,7 @@ expression: snap Id("32715f839edd796c5b6f2d48c88748857ed439867c2dc4b2b65c5f96c6d9ef73"), ]), }, - "test\\0\\0\\9\\49": { + "test/0/0/9/49": { "name": "49", "type": "file", "mtime": "[some]", @@ -532,7 +532,7 @@ expression: snap Id("d3fdbaa72299c1abfe47bc26bdf201cc5cb66efc031e517111d413c38bfea882"), ]), }, - "test\\0\\0\\9\\5": { + "test/0/0/9/5": { "name": "5", "type": "file", "mtime": "[some]", @@ -543,7 +543,7 @@ expression: snap Id("de68adab4fe82b31fe5b14abe5d17faecf4f7c8d93d74374cbf3793701290ede"), ]), }, - "test\\0\\0\\9\\50": { + "test/0/0/9/50": { "name": "50", "type": "file", "mtime": "[some]", @@ -554,7 +554,7 @@ expression: snap Id("939e25879149a8b87ecb6b44ded96f6b029d5463d38fe5525005984d6d42c34a"), ]), }, - "test\\0\\0\\9\\51": { + "test/0/0/9/51": { "name": "51", "type": "file", "mtime": "[some]", @@ -565,7 +565,7 @@ expression: snap Id("958a554f813d199f9f897c9f5c4d90571dfe7f67d8a3733a64598906a8e7208d"), ]), }, - "test\\0\\0\\9\\52": { + "test/0/0/9/52": { "name": "52", "type": "file", "mtime": "[some]", @@ -576,7 +576,7 @@ expression: snap Id("2e8799330876d03e95428975593175ea5dd75ed3d5f3d83b4d9115dd2c6d3b71"), ]), }, - "test\\0\\0\\9\\53": { + "test/0/0/9/53": { "name": "53", "type": "file", "mtime": "[some]", @@ -587,7 +587,7 @@ expression: snap Id("f476577492f325d8e617814bde3ecc83eb9e8662023d2e057c6c958ce77511e5"), ]), }, - "test\\0\\0\\9\\54": { + "test/0/0/9/54": { "name": "54", "type": "file", "mtime": "[some]", @@ -598,7 +598,7 @@ expression: snap Id("b03ca92f97233541fcc95c22455cadf63c5a25089117af4b433ffc1ed9a8991f"), ]), }, - "test\\0\\0\\9\\55": { + "test/0/0/9/55": { "name": "55", "type": "file", "mtime": "[some]", @@ -609,7 +609,7 @@ expression: snap Id("63d9f467b844e7d5a48120b236408e381b160c9c8355b0e3113cd407ad694ab4"), ]), }, - "test\\0\\0\\9\\56": { + "test/0/0/9/56": { "name": "56", "type": "file", "mtime": "[some]", @@ -620,7 +620,7 @@ expression: snap Id("c93bcbbab1c234a01574f913bc49cc2857ccc17e4d5d8c9dc9b9b720729ed3f7"), ]), }, - "test\\0\\0\\9\\57": { + "test/0/0/9/57": { "name": "57", "type": "file", "mtime": "[some]", @@ -631,7 +631,7 @@ expression: snap Id("86f85e1d9af7cac33a60fae72c05f0f410ec0c9dfd917ad18a7e2308a272e1fb"), ]), }, - "test\\0\\0\\9\\58": { + "test/0/0/9/58": { "name": "58", "type": "file", "mtime": "[some]", @@ -642,7 +642,7 @@ expression: snap Id("858f7dc0f2b2e7532fec3b2a2daa551855ebaf27e29b60a90807e3f770d60815"), ]), }, - "test\\0\\0\\9\\59": { + "test/0/0/9/59": { "name": "59", "type": "file", "mtime": "[some]", @@ -653,7 +653,7 @@ expression: snap Id("6047339cfc27b694b82bfc1621888997bf0e954bc5459a7ed51c424d7bd92583"), ]), }, - "test\\0\\0\\9\\6": { + "test/0/0/9/6": { "name": "6", "type": "file", "mtime": "[some]", @@ -664,7 +664,7 @@ expression: snap Id("f6eca86a06925045b3b3a09f38999144ace49570b7e3a203ca7f24644fe35f20"), ]), }, - "test\\0\\0\\9\\60": { + "test/0/0/9/60": { "name": "60", "type": "file", "mtime": "[some]", @@ -675,7 +675,7 @@ expression: snap Id("fcd9ec0c99f7992c184666e3040831b919f3375157bd563a2b65cde1c6789847"), ]), }, - "test\\0\\0\\9\\61": { + "test/0/0/9/61": { "name": "61", "type": "file", "mtime": "[some]", @@ -686,7 +686,7 @@ expression: snap Id("f5738472e3ab1372588f9048c3198bb9e05338acc94c33aca3c34277c675116c"), ]), }, - "test\\0\\0\\9\\62": { + "test/0/0/9/62": { "name": "62", "type": "file", "mtime": "[some]", @@ -697,7 +697,7 @@ expression: snap Id("7d07ba1cacc97dc5ae31b7e49a8926bec040102f1211fda9e98f0ea70a6aecf9"), ]), }, - "test\\0\\0\\9\\63": { + "test/0/0/9/63": { "name": "63", "type": "file", "mtime": "[some]", @@ -708,7 +708,7 @@ expression: snap Id("c8d553f609367281f390160dd77113ca952dbb7a7fd073daa2ac4620a63148b4"), ]), }, - "test\\0\\0\\9\\64": { + "test/0/0/9/64": { "name": "64", "type": "file", "mtime": "[some]", @@ -719,7 +719,7 @@ expression: snap Id("5d2c26fc0563f43d161b5abc982af18696d7178ad1f7aeeae940c595415cc6df"), ]), }, - "test\\0\\0\\9\\65": { + "test/0/0/9/65": { "name": "65", "type": "file", "mtime": "[some]", @@ -730,7 +730,7 @@ expression: snap Id("2888d521727e1ba03471bcea1d48927818b54d42331caef8115fac5be94406b6"), ]), }, - "test\\0\\0\\9\\66": { + "test/0/0/9/66": { "name": "66", "type": "file", "mtime": "[some]", @@ -741,7 +741,7 @@ expression: snap Id("a9ba60dcf9b908c3488d03d2f3ecf9aff2fa1b419f30f3d938b2eb3c319566cc"), ]), }, - "test\\0\\0\\9\\67": { + "test/0/0/9/67": { "name": "67", "type": "file", "mtime": "[some]", @@ -752,7 +752,7 @@ expression: snap Id("7246d1d67000b94ac3e1928fcef3804263156e72aeb886b00b3f59ec1090c427"), ]), }, - "test\\0\\0\\9\\68": { + "test/0/0/9/68": { "name": "68", "type": "file", "mtime": "[some]", @@ -763,7 +763,7 @@ expression: snap Id("a8fafe2f1d514d1efc327d76fcc33916c20acd6838f524848a259de752862c41"), ]), }, - "test\\0\\0\\9\\7": { + "test/0/0/9/7": { "name": "7", "type": "file", "mtime": "[some]", @@ -774,7 +774,7 @@ expression: snap Id("6a1da85d5dccfdefb75bb607e1f3ded8f6068085a7359ceb41a1b9930d9442f3"), ]), }, - "test\\0\\0\\9\\8": { + "test/0/0/9/8": { "name": "8", "type": "file", "mtime": "[some]", @@ -785,7 +785,7 @@ expression: snap Id("105068aefc2d42cce9acc224bb369002f53f0036da394632f4623170f3b0277e"), ]), }, - "test\\0\\0\\9\\9": { + "test/0/0/9/9": { "name": "9", "type": "file", "mtime": "[some]", @@ -796,7 +796,7 @@ expression: snap Id("656039f259149cf97af18fb11ebee3194cfaa059ff72d6bc9a2b91b0889d65cc"), ]), }, - "test\\0\\tests": { + "test/0/tests": { "name": "tests", "type": "dir", "mtime": "[some]", @@ -805,7 +805,7 @@ expression: snap "content": None, "subtree": "[some]", }, - "test\\0\\tests\\empty-file": { + "test/0/tests/empty-file": { "name": "empty-file", "type": "file", "mtime": "[some]", @@ -813,7 +813,7 @@ expression: snap "ctime": "[some]", "content": Some([]), }, - "test\\0\\tests\\testfile": { + "test/0/tests/testfile": { "name": "testfile", "type": "file", "mtime": "[some]", @@ -824,7 +824,7 @@ expression: snap Id("649b8b471e7d7bc175eec758a7006ac693c434c8297c07db15286788c837154a"), ]), }, - "test\\0\\tests\\testfile-hardlink": { + "test/0/tests/testfile-hardlink": { "name": "testfile-hardlink", "type": "file", "mtime": "[some]", @@ -835,7 +835,7 @@ expression: snap Id("649b8b471e7d7bc175eec758a7006ac693c434c8297c07db15286788c837154a"), ]), }, - "test\\0\\tests\\testfile-symlink": { + "test/0/tests/testfile-symlink": { "name": "testfile-symlink", "type": "symlink", "linktarget": "testfile", From d127fcea2bd72e9f44c5b008c041237b0c66b130 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 13 Jan 2025 15:57:27 +0100 Subject: [PATCH 12/22] update snapshots --- ...on__find-matching-nodes-not-found-nix.snap | 11 - ...find-matching-nodes-not-found-windows.snap | 11 - ...nd-matching-wildcard-existing-windows.snap | 6 +- .../tests/snapshots/integration__windows.snap | 847 ------------------ 4 files changed, 3 insertions(+), 872 deletions(-) delete mode 100644 crates/core/tests/snapshots/integration__find-matching-nodes-not-found-nix.snap delete mode 100644 crates/core/tests/snapshots/integration__find-matching-nodes-not-found-windows.snap delete mode 100644 crates/core/tests/snapshots/integration__windows.snap diff --git a/crates/core/tests/snapshots/integration__find-matching-nodes-not-found-nix.snap b/crates/core/tests/snapshots/integration__find-matching-nodes-not-found-nix.snap deleted file mode 100644 index 9badfa81..00000000 --- a/crates/core/tests/snapshots/integration__find-matching-nodes-not-found-nix.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: crates/core/tests/integration.rs -expression: snap ---- -FindMatches( - paths: [], - nodes: [], - matches: [ - [], - ], -) diff --git a/crates/core/tests/snapshots/integration__find-matching-nodes-not-found-windows.snap b/crates/core/tests/snapshots/integration__find-matching-nodes-not-found-windows.snap deleted file mode 100644 index 9badfa81..00000000 --- a/crates/core/tests/snapshots/integration__find-matching-nodes-not-found-windows.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: crates/core/tests/integration.rs -expression: snap ---- -FindMatches( - paths: [], - nodes: [], - matches: [ - [], - ], -) diff --git a/crates/core/tests/snapshots/integration__find-matching-wildcard-existing-windows.snap b/crates/core/tests/snapshots/integration__find-matching-wildcard-existing-windows.snap index 5f367bb1..ab3e2ba4 100644 --- a/crates/core/tests/snapshots/integration__find-matching-wildcard-existing-windows.snap +++ b/crates/core/tests/snapshots/integration__find-matching-wildcard-existing-windows.snap @@ -3,9 +3,9 @@ source: crates/core/tests/integration.rs expression: snap --- ([ - "test\\0\\tests\\testfile", - "test\\0\\tests\\testfile-hardlink", - "test\\0\\tests\\testfile-symlink", + "test/0/tests/testfile", + "test/0/tests/testfile-hardlink", + "test/0/tests/testfile-symlink", ], [ [ (0, 0), diff --git a/crates/core/tests/snapshots/integration__windows.snap b/crates/core/tests/snapshots/integration__windows.snap deleted file mode 100644 index 2e93393b..00000000 --- a/crates/core/tests/snapshots/integration__windows.snap +++ /dev/null @@ -1,847 +0,0 @@ ---- -source: crates/core/tests/integration.rs -expression: snap ---- -{ - "test": { - "name": "test", - "type": "dir", - "content": None, - "subtree": "[some]", - }, - "test\\0": { - "name": "0", - "type": "dir", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "content": None, - "subtree": "[some]", - }, - "test\\0\\0": { - "name": "0", - "type": "dir", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "content": None, - "subtree": "[some]", - }, - "test\\0\\0\\9": { - "name": "9", - "type": "dir", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "content": None, - "subtree": "[some]", - }, - "test\\0\\0\\9\\0": { - "name": "0", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("aa6da76dc4b3bd8d8cd9d42c5ed6db2e9c3e43cecef7e7ee925a3accab96ad79"), - ]), - }, - "test\\0\\0\\9\\1": { - "name": "1", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("bd15c2d9117aa305a0cf99c9cc30359b66ee454b00b897fb1a925c56025f4556"), - ]), - }, - "test\\0\\0\\9\\10": { - "name": "10", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("753d116d34c772059a9a4a2ca33f6ea9ac618470acc13687d08fff7eb5ad3c72"), - ]), - }, - "test\\0\\0\\9\\11": { - "name": "11", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("efe42dd90fd602df1c5b3b7675cd845e23befba069eac0222a9997fea2a9852f"), - ]), - }, - "test\\0\\0\\9\\12": { - "name": "12", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("032ad465747269b41dbdfc87accad1fd49e8dc574ae4bfaf49f036150ec92e57"), - ]), - }, - "test\\0\\0\\9\\13": { - "name": "13", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("b0b5afc29b6391722bd60a9a74c386c856cb130a219954f34944a4f19bbecc41"), - ]), - }, - "test\\0\\0\\9\\14": { - "name": "14", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("b19a49a2e711d5547e1fb247170c58d2da17f9b0bbf75eb84507097a301f1c6d"), - ]), - }, - "test\\0\\0\\9\\15": { - "name": "15", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("f11e72e0290dc75261ee48c39fd42597bdcd80e110ffd0651f71d54ea22030e5"), - ]), - }, - "test\\0\\0\\9\\16": { - "name": "16", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("71935a6f85198c65584ed5c4ff5e34cf93cb79756023e82ecc85153663be2b00"), - ]), - }, - "test\\0\\0\\9\\17": { - "name": "17", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("b81f6dd6ad54fbed57ffac74bcecf9f1a56ce8948edc92153ee34ed415ed68d1"), - ]), - }, - "test\\0\\0\\9\\18": { - "name": "18", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("1fad45c4bf21695fae9e100626f42d35d8c2c48cb90f6a6256973c262608879d"), - ]), - }, - "test\\0\\0\\9\\19": { - "name": "19", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("92c4aa4fa67ede99b9ab1b694bc32b4f3d127f406a7e6583ecbe55b1bb919928"), - ]), - }, - "test\\0\\0\\9\\2": { - "name": "2", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("f5a3517234ef2f213afe617cdab9d25364e1c8c9e61198bf387b157542ee7dbb"), - ]), - }, - "test\\0\\0\\9\\20": { - "name": "20", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("737c19313675671c589bfc613b0b736b95a58218486d09d3a7a649f324d81ef7"), - ]), - }, - "test\\0\\0\\9\\21": { - "name": "21", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("ee25130a4e74b1306087799914d8b92aed2c385b06256705b18266ff088524f4"), - ]), - }, - "test\\0\\0\\9\\22": { - "name": "22", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("3492c28f8ddbb9186e483ef9eb1cc7042c5adf871a4d15d2edf8ee7b2b04069b"), - ]), - }, - "test\\0\\0\\9\\23": { - "name": "23", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("b321dedc16124312ed08cb7f9c67006bd6a4f946ca04bc178323e6147b081943"), - ]), - }, - "test\\0\\0\\9\\24": { - "name": "24", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("7020db85ffe6a3fd5f34971ff37a20fa5d32947181692bfbcca62cfc4a9221d0"), - ]), - }, - "test\\0\\0\\9\\25": { - "name": "25", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("75e7274ef54e91c2857ba0de4c869388df3f9c657d776a3e90eaf3f3f619536d"), - ]), - }, - "test\\0\\0\\9\\26": { - "name": "26", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("418b691988d9bc7357b99c5baf31e83436315d1bcc887482dd83a8537183af17"), - ]), - }, - "test\\0\\0\\9\\27": { - "name": "27", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("06a5749af5715af19fde59d1caaa542df00c56aa0058bf99b67dec5216c27df6"), - ]), - }, - "test\\0\\0\\9\\28": { - "name": "28", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("61e8ffda5a11d743d7dc4bcfd1e18e8b6179a53f8b28b25adf55d3acb0b10eff"), - ]), - }, - "test\\0\\0\\9\\29": { - "name": "29", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("60c1c2f3a0b8e5b1c4e2b7f1453661170395c5c26f2e2d241e81eb929cfc930f"), - ]), - }, - "test\\0\\0\\9\\3": { - "name": "3", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("f0ad279385b5b5c862e1e2fdd599616115729e0b8085833d7b31de68c8326b85"), - ]), - }, - "test\\0\\0\\9\\30": { - "name": "30", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("790c34f1d4d95a6067f2406a78aa0dcca2fc2aa1db932fbd865ce9cafd7baa23"), - ]), - }, - "test\\0\\0\\9\\31": { - "name": "31", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("8e53f45db9249373828c588774471d34c9c1e436b83b1603a49d675de810db7e"), - ]), - }, - "test\\0\\0\\9\\32": { - "name": "32", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("c4eccf95ba2409d21283981297bf9919ec15c2cc0dadef84aafaad49a0c79130"), - ]), - }, - "test\\0\\0\\9\\33": { - "name": "33", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("da8b8b2d79bba6a3d4784233e4421293cea0cf27fd03521e11ce0c7c941f6dbb"), - ]), - }, - "test\\0\\0\\9\\34": { - "name": "34", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("a1413ef1018e7a4f3df7f19aad942a611cbda5bdd45d4269cb54d5f9fa58cd01"), - ]), - }, - "test\\0\\0\\9\\35": { - "name": "35", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("f534a7fe839d440a0fdd1d45254921601b2a90ff5b61edcdc631b722f84a0dbb"), - ]), - }, - "test\\0\\0\\9\\36": { - "name": "36", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("d56e2a8c47f4ae36886784c852916f36036f3daffa547a5922dda5814ecc5a86"), - ]), - }, - "test\\0\\0\\9\\37": { - "name": "37", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("5800df73dcfdbd261800f2474c8631fe0727fbc1788cfd70bd206ab2f50b0707"), - ]), - }, - "test\\0\\0\\9\\38": { - "name": "38", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("e7c987bd8d9257bea1e44f882bc075c84ee7f103d68eec8677e37b49d2854d90"), - ]), - }, - "test\\0\\0\\9\\39": { - "name": "39", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("03a1411035add5b10ed183b2ca7d8db8deee2402f45ee445f54de50896c3e26f"), - ]), - }, - "test\\0\\0\\9\\4": { - "name": "4", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("917aae16764fc905abf0514e965133ed80e2b66c426fb09be24eabd452d4438d"), - ]), - }, - "test\\0\\0\\9\\40": { - "name": "40", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("1adcd19cf770f7d8ab3bb4b3996fb6187f59c468953848ec4f119c9cef484f28"), - ]), - }, - "test\\0\\0\\9\\41": { - "name": "41", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("b5debda793f22adb93204dc81f107d03636b7431192fc8497f5c9a340e43460c"), - ]), - }, - "test\\0\\0\\9\\42": { - "name": "42", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("e6e9b35f3ca77afbd0043bc42085b814ceea8cfb93a0bcba9696dee5034ffead"), - ]), - }, - "test\\0\\0\\9\\43": { - "name": "43", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("31d87fd77a88ca00e9c5612f578c0b3243347ba328a6daa09742fdc8c7cec6f4"), - ]), - }, - "test\\0\\0\\9\\44": { - "name": "44", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("76aec0f2d829d01eff332099ebd5628f61adc72ff44e7d6b82beabc28fa44f1f"), - ]), - }, - "test\\0\\0\\9\\45": { - "name": "45", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("121242341ff94a05074a31cb2a0f566093e5c052e64dcde4c0c9b686e58daf87"), - ]), - }, - "test\\0\\0\\9\\46": { - "name": "46", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("a69efe0ab923672b5aa1a2113f54e80684a7c126c1114af4fac3953be18bea74"), - ]), - }, - "test\\0\\0\\9\\47": { - "name": "47", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("a50a7ab010d0d349b213576d1e2d2d2b345334be1be6f62798d250fc1e5cc51f"), - ]), - }, - "test\\0\\0\\9\\48": { - "name": "48", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("32715f839edd796c5b6f2d48c88748857ed439867c2dc4b2b65c5f96c6d9ef73"), - ]), - }, - "test\\0\\0\\9\\49": { - "name": "49", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("d3fdbaa72299c1abfe47bc26bdf201cc5cb66efc031e517111d413c38bfea882"), - ]), - }, - "test\\0\\0\\9\\5": { - "name": "5", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("de68adab4fe82b31fe5b14abe5d17faecf4f7c8d93d74374cbf3793701290ede"), - ]), - }, - "test\\0\\0\\9\\50": { - "name": "50", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("939e25879149a8b87ecb6b44ded96f6b029d5463d38fe5525005984d6d42c34a"), - ]), - }, - "test\\0\\0\\9\\51": { - "name": "51", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("958a554f813d199f9f897c9f5c4d90571dfe7f67d8a3733a64598906a8e7208d"), - ]), - }, - "test\\0\\0\\9\\52": { - "name": "52", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("2e8799330876d03e95428975593175ea5dd75ed3d5f3d83b4d9115dd2c6d3b71"), - ]), - }, - "test\\0\\0\\9\\53": { - "name": "53", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("f476577492f325d8e617814bde3ecc83eb9e8662023d2e057c6c958ce77511e5"), - ]), - }, - "test\\0\\0\\9\\54": { - "name": "54", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("b03ca92f97233541fcc95c22455cadf63c5a25089117af4b433ffc1ed9a8991f"), - ]), - }, - "test\\0\\0\\9\\55": { - "name": "55", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("63d9f467b844e7d5a48120b236408e381b160c9c8355b0e3113cd407ad694ab4"), - ]), - }, - "test\\0\\0\\9\\56": { - "name": "56", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("c93bcbbab1c234a01574f913bc49cc2857ccc17e4d5d8c9dc9b9b720729ed3f7"), - ]), - }, - "test\\0\\0\\9\\57": { - "name": "57", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("86f85e1d9af7cac33a60fae72c05f0f410ec0c9dfd917ad18a7e2308a272e1fb"), - ]), - }, - "test\\0\\0\\9\\58": { - "name": "58", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("858f7dc0f2b2e7532fec3b2a2daa551855ebaf27e29b60a90807e3f770d60815"), - ]), - }, - "test\\0\\0\\9\\59": { - "name": "59", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("6047339cfc27b694b82bfc1621888997bf0e954bc5459a7ed51c424d7bd92583"), - ]), - }, - "test\\0\\0\\9\\6": { - "name": "6", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("f6eca86a06925045b3b3a09f38999144ace49570b7e3a203ca7f24644fe35f20"), - ]), - }, - "test\\0\\0\\9\\60": { - "name": "60", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("fcd9ec0c99f7992c184666e3040831b919f3375157bd563a2b65cde1c6789847"), - ]), - }, - "test\\0\\0\\9\\61": { - "name": "61", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("f5738472e3ab1372588f9048c3198bb9e05338acc94c33aca3c34277c675116c"), - ]), - }, - "test\\0\\0\\9\\62": { - "name": "62", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("7d07ba1cacc97dc5ae31b7e49a8926bec040102f1211fda9e98f0ea70a6aecf9"), - ]), - }, - "test\\0\\0\\9\\63": { - "name": "63", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("c8d553f609367281f390160dd77113ca952dbb7a7fd073daa2ac4620a63148b4"), - ]), - }, - "test\\0\\0\\9\\64": { - "name": "64", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("5d2c26fc0563f43d161b5abc982af18696d7178ad1f7aeeae940c595415cc6df"), - ]), - }, - "test\\0\\0\\9\\65": { - "name": "65", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("2888d521727e1ba03471bcea1d48927818b54d42331caef8115fac5be94406b6"), - ]), - }, - "test\\0\\0\\9\\66": { - "name": "66", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("a9ba60dcf9b908c3488d03d2f3ecf9aff2fa1b419f30f3d938b2eb3c319566cc"), - ]), - }, - "test\\0\\0\\9\\67": { - "name": "67", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("7246d1d67000b94ac3e1928fcef3804263156e72aeb886b00b3f59ec1090c427"), - ]), - }, - "test\\0\\0\\9\\68": { - "name": "68", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 11520, - "content": Some([ - Id("a8fafe2f1d514d1efc327d76fcc33916c20acd6838f524848a259de752862c41"), - ]), - }, - "test\\0\\0\\9\\7": { - "name": "7", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("6a1da85d5dccfdefb75bb607e1f3ded8f6068085a7359ceb41a1b9930d9442f3"), - ]), - }, - "test\\0\\0\\9\\8": { - "name": "8", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("105068aefc2d42cce9acc224bb369002f53f0036da394632f4623170f3b0277e"), - ]), - }, - "test\\0\\0\\9\\9": { - "name": "9", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 16384, - "content": Some([ - Id("656039f259149cf97af18fb11ebee3194cfaa059ff72d6bc9a2b91b0889d65cc"), - ]), - }, - "test\\0\\tests": { - "name": "tests", - "type": "dir", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "content": None, - "subtree": "[some]", - }, - "test\\0\\tests\\empty-file": { - "name": "empty-file", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "content": Some([]), - }, - "test\\0\\tests\\testfile": { - "name": "testfile", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 21, - "content": Some([ - Id("649b8b471e7d7bc175eec758a7006ac693c434c8297c07db15286788c837154a"), - ]), - }, - "test\\0\\tests\\testfile-hardlink": { - "name": "testfile-hardlink", - "type": "file", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 21, - "content": Some([ - Id("649b8b471e7d7bc175eec758a7006ac693c434c8297c07db15286788c837154a"), - ]), - }, - "test\\0\\tests\\testfile-symlink": { - "name": "testfile-symlink", - "type": "symlink", - "linktarget": "testfile", - "mtime": "[some]", - "atime": "[some]", - "ctime": "[some]", - "size": 8, - "content": None, - }, -} From 04f2e0bce0b1c14174f4bc0d3b5aa30c562289a3 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 13 Jan 2025 16:36:59 +0100 Subject: [PATCH 13/22] remove platform dependency in find snapshots --- crates/core/tests/integration.rs | 6 ++++++ crates/core/tests/integration/find.rs | 10 +++++----- ...tegration__find-matching-existing-windows.snap | 11 ----------- ...p => integration__find-matching-existing.snap} | 0 ...__find-matching-wildcard-existing-windows.snap | 15 --------------- ...gration__find-matching-wildcard-existing.snap} | 0 .../integration__find-nodes-existing-windows.snap | 7 ------- ...snap => integration__find-nodes-existing.snap} | 0 ...integration__find-nodes-not-found-windows.snap | 10 ---------- ...nap => integration__find-nodes-not-found.snap} | 0 10 files changed, 11 insertions(+), 48 deletions(-) delete mode 100644 crates/core/tests/snapshots/integration__find-matching-existing-windows.snap rename crates/core/tests/snapshots/{integration__find-matching-existing-nix.snap => integration__find-matching-existing.snap} (100%) delete mode 100644 crates/core/tests/snapshots/integration__find-matching-wildcard-existing-windows.snap rename crates/core/tests/snapshots/{integration__find-matching-wildcard-existing-nix.snap => integration__find-matching-wildcard-existing.snap} (100%) delete mode 100644 crates/core/tests/snapshots/integration__find-nodes-existing-windows.snap rename crates/core/tests/snapshots/{integration__find-nodes-existing-nix.snap => integration__find-nodes-existing.snap} (100%) delete mode 100644 crates/core/tests/snapshots/integration__find-nodes-not-found-windows.snap rename crates/core/tests/snapshots/{integration__find-nodes-not-found-nix.snap => integration__find-nodes-not-found.snap} (100%) diff --git a/crates/core/tests/integration.rs b/crates/core/tests/integration.rs index 85ad67d5..d33555be 100644 --- a/crates/core/tests/integration.rs +++ b/crates/core/tests/integration.rs @@ -171,6 +171,12 @@ fn assert_with_win(test: &str, snap: T) { assert_ron_snapshot!(format!("{test}-nix"), snap); } +// helper function +// TODO: Use macro directly (need to refactor snapshot location) +fn assert_ron(test: &str, snap: T) { + assert_ron_snapshot!(test, snap); +} + #[test] fn repo_with_commands() -> Result<()> { let be = InMemoryBackend::new(); diff --git a/crates/core/tests/integration/find.rs b/crates/core/tests/integration/find.rs index 4e15cd74..a7d1c1c4 100644 --- a/crates/core/tests/integration/find.rs +++ b/crates/core/tests/integration/find.rs @@ -14,7 +14,7 @@ use rustic_core::{ }; use typed_path::UnixPath; -use super::{RepoOpen, TestSource, assert_with_win, set_up_repo, tar_gz_testdata}; +use super::{RepoOpen, TestSource, assert_ron, set_up_repo, tar_gz_testdata}; #[rstest] fn test_find(tar_gz_testdata: Result, set_up_repo: Result) -> Result<()> { @@ -33,7 +33,7 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) // test non-existing path let not_found = repo.find_nodes_from_path(vec![snapshot.tree], UnixPath::new("not_existing"))?; - assert_with_win("find-nodes-not-found", not_found); + assert_ron("find-nodes-not-found", not_found); // test non-existing match let glob = Glob::new("not_existing")?.compile_matcher(); let FindMatches { paths, matches, .. } = @@ -44,7 +44,7 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) // test existing path let FindNode { matches, .. } = repo.find_nodes_from_path(vec![snapshot.tree], UnixPath::new("test/0/tests/testfile"))?; - assert_with_win("find-nodes-existing", matches); + assert_ron("find-nodes-existing", matches); // test existing match let glob = Glob::new("testfile")?.compile_matcher(); let match_func = |path: &UnixPath, _: &Node| { @@ -53,7 +53,7 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; let paths: Vec<_> = paths.into_iter().map(SerializablePath).collect(); - assert_with_win("find-matching-existing", (paths, matches)); + assert_ron("find-matching-existing", (paths, matches)); // test existing match let glob = Glob::new("testfile*")?.compile_matcher(); let match_func = |path: &UnixPath, _: &Node| { @@ -62,6 +62,6 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; let paths: Vec<_> = paths.into_iter().map(SerializablePath).collect(); - assert_with_win("find-matching-wildcard-existing", (paths, matches)); + assert_ron("find-matching-wildcard-existing", (paths, matches)); Ok(()) } diff --git a/crates/core/tests/snapshots/integration__find-matching-existing-windows.snap b/crates/core/tests/snapshots/integration__find-matching-existing-windows.snap deleted file mode 100644 index 8e3b67a1..00000000 --- a/crates/core/tests/snapshots/integration__find-matching-existing-windows.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: crates/core/tests/integration.rs -expression: snap ---- -([ - "test/0/tests/testfile", -], [ - [ - (0, 0), - ], -]) diff --git a/crates/core/tests/snapshots/integration__find-matching-existing-nix.snap b/crates/core/tests/snapshots/integration__find-matching-existing.snap similarity index 100% rename from crates/core/tests/snapshots/integration__find-matching-existing-nix.snap rename to crates/core/tests/snapshots/integration__find-matching-existing.snap diff --git a/crates/core/tests/snapshots/integration__find-matching-wildcard-existing-windows.snap b/crates/core/tests/snapshots/integration__find-matching-wildcard-existing-windows.snap deleted file mode 100644 index ab3e2ba4..00000000 --- a/crates/core/tests/snapshots/integration__find-matching-wildcard-existing-windows.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/core/tests/integration.rs -expression: snap ---- -([ - "test/0/tests/testfile", - "test/0/tests/testfile-hardlink", - "test/0/tests/testfile-symlink", -], [ - [ - (0, 0), - (1, 1), - (2, 2), - ], -]) diff --git a/crates/core/tests/snapshots/integration__find-matching-wildcard-existing-nix.snap b/crates/core/tests/snapshots/integration__find-matching-wildcard-existing.snap similarity index 100% rename from crates/core/tests/snapshots/integration__find-matching-wildcard-existing-nix.snap rename to crates/core/tests/snapshots/integration__find-matching-wildcard-existing.snap diff --git a/crates/core/tests/snapshots/integration__find-nodes-existing-windows.snap b/crates/core/tests/snapshots/integration__find-nodes-existing-windows.snap deleted file mode 100644 index 90f957a2..00000000 --- a/crates/core/tests/snapshots/integration__find-nodes-existing-windows.snap +++ /dev/null @@ -1,7 +0,0 @@ ---- -source: crates/core/tests/integration.rs -expression: snap ---- -[ - Some(0), -] diff --git a/crates/core/tests/snapshots/integration__find-nodes-existing-nix.snap b/crates/core/tests/snapshots/integration__find-nodes-existing.snap similarity index 100% rename from crates/core/tests/snapshots/integration__find-nodes-existing-nix.snap rename to crates/core/tests/snapshots/integration__find-nodes-existing.snap diff --git a/crates/core/tests/snapshots/integration__find-nodes-not-found-windows.snap b/crates/core/tests/snapshots/integration__find-nodes-not-found-windows.snap deleted file mode 100644 index 56f60279..00000000 --- a/crates/core/tests/snapshots/integration__find-nodes-not-found-windows.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/core/tests/integration.rs -expression: snap ---- -FindNode( - nodes: [], - matches: [ - None, - ], -) diff --git a/crates/core/tests/snapshots/integration__find-nodes-not-found-nix.snap b/crates/core/tests/snapshots/integration__find-nodes-not-found.snap similarity index 100% rename from crates/core/tests/snapshots/integration__find-nodes-not-found-nix.snap rename to crates/core/tests/snapshots/integration__find-nodes-not-found.snap From f4e27f2f7bcc6ee66467fdaf5c5e93d1e89ee07e Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 13 Jan 2025 16:37:43 +0100 Subject: [PATCH 14/22] add util description --- crates/core/src/util.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index d3d9e5d1..43f19ab2 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -1,3 +1,4 @@ +/// Utilities for handling paths on rustic_core use globset::GlobMatcher; use serde::{Serialize, Serializer}; use typed_path::{UnixPath, UnixPathBuf}; From 3e421ae24b9398792a94a77bf14342fb5a8cddb7 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 13 Jan 2025 20:34:34 +0100 Subject: [PATCH 15/22] docu for mod util --- crates/core/src/lib.rs | 1 + crates/core/src/util.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index c3c8f215..a6bf6ef5 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -117,6 +117,7 @@ pub(crate) mod progress; /// Structs which are saved in JSON or binary format in the repository pub mod repofile; pub(crate) mod repository; +/// Utils for using unix paths pub mod util; /// Virtual File System support - allows to act on the repository like on a file system pub mod vfs; diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 43f19ab2..a5e8f1f2 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -1,4 +1,4 @@ -/// Utilities for handling paths on rustic_core +/// Utilities for handling paths on ``rustic_core`` use globset::GlobMatcher; use serde::{Serialize, Serializer}; use typed_path::{UnixPath, UnixPathBuf}; From 219f56c20fe8523ae67778f16870fbe95f03bf98 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Tue, 14 Jan 2025 20:52:53 +0100 Subject: [PATCH 16/22] add tests --- crates/core/src/blob/tree.rs | 42 +--------------- crates/core/src/util.rs | 92 +++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 42 deletions(-) diff --git a/crates/core/src/blob/tree.rs b/crates/core/src/blob/tree.rs index 575c631a..edb4a421 100644 --- a/crates/core/src/blob/tree.rs +++ b/crates/core/src/blob/tree.rs @@ -1,5 +1,4 @@ use std::{ - borrow::Cow, cmp::Ordering, collections::{BTreeMap, BTreeSet, BinaryHeap}, mem, @@ -13,7 +12,7 @@ use ignore::Match; use ignore::overrides::{Override, OverrideBuilder}; use serde::{Deserialize, Deserializer}; use serde_derive::Serialize; -use typed_path::{Component, TypedPath, UnixPath, UnixPathBuf, WindowsComponent, WindowsPrefix}; +use typed_path::{Component, UnixPath, UnixPathBuf}; use crate::{ backend::{ @@ -400,45 +399,6 @@ pub struct FindMatches { pub matches: Vec>, } -/// Converts a [`TypedPath`] to an [`Cow`]. -/// -/// # Arguments -/// -/// * `p` - The component to convert. -/// -/// # Errors -/// -/// * If the component is a current or parent directory. -/// * If the component is not UTF-8 conform. -pub(crate) fn typed_path_to_unix_path<'a>(p: &'a TypedPath<'_>) -> Cow<'a, UnixPath> { - match p { - TypedPath::Unix(path) => Cow::Borrowed(path), - TypedPath::Windows(path) => { - let mut unix_path = UnixPathBuf::new(); - for c in path.with_windows_encoding().components() { - match c { - WindowsComponent::Prefix(p) => match p.kind() { - WindowsPrefix::Verbatim(p) | WindowsPrefix::DeviceNS(p) => { - unix_path.push(p); - } - WindowsPrefix::VerbatimUNC(_, q) | WindowsPrefix::UNC(_, q) => { - unix_path.push(q); - } - WindowsPrefix::VerbatimDisk(p) | WindowsPrefix::Disk(p) => { - let c = vec![p]; - unix_path.push(&c); - } - }, - c => { - unix_path.push(c); - } - } - } - Cow::Owned(unix_path) - } - } -} - impl IntoIterator for Tree { type Item = Node; type IntoIter = std::vec::IntoIter; diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index a5e8f1f2..fa4071ea 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -1,7 +1,12 @@ /// Utilities for handling paths on ``rustic_core`` +use std::borrow::Cow; + use globset::GlobMatcher; use serde::{Serialize, Serializer}; -use typed_path::{UnixPath, UnixPathBuf}; +use typed_path::{ + Component, TypedPath, UnixComponent, UnixPath, UnixPathBuf, WindowsComponent, WindowsPath, + WindowsPrefix, +}; /// Extend `globset::GlobMatcher` to allow mathing on unix paths (on every platform) pub trait GlobMatcherExt { @@ -43,3 +48,88 @@ where let s = format!("{}", path.display()); serializer.serialize_str(&s) } + +/// Converts a [`TypedPath`] to an [`Cow`]. +/// +/// # Arguments +/// +/// * `path` - The path to convert. +#[must_use] +pub fn typed_path_to_unix_path<'a>(path: &'a TypedPath<'_>) -> Cow<'a, UnixPath> { + match path { + TypedPath::Unix(p) => Cow::Borrowed(p), + TypedPath::Windows(p) => Cow::Owned(windows_path_to_unix_path(p)), + } +} + +/// Converts a [`WindowsPath`] to a [`UnixPathBuf`]. +/// +/// # Arguments +/// +/// * `path` - The path to convert. +#[must_use] +pub fn windows_path_to_unix_path(path: &WindowsPath) -> UnixPathBuf { + let mut unix_path = UnixPathBuf::new(); + let mut components = path.components(); + if let Some(c) = components.next() { + match c { + WindowsComponent::Prefix(p) => { + unix_path.push(UnixComponent::RootDir); + match p.kind() { + WindowsPrefix::Verbatim(p) | WindowsPrefix::DeviceNS(p) => { + unix_path.push(p); + } + WindowsPrefix::VerbatimUNC(_, q) | WindowsPrefix::UNC(_, q) => { + unix_path.push(q); + } + WindowsPrefix::VerbatimDisk(p) | WindowsPrefix::Disk(p) => { + let c = vec![p]; + unix_path.push(&c); + } + } + // remove RootDir from iterator + _ = components.next(); + } + WindowsComponent::RootDir => { + unix_path.push(UnixComponent::RootDir); + } + c => { + unix_path.push(c.as_bytes()); + } + } + } + for c in components { + match c { + WindowsComponent::RootDir => { + unix_path.push(UnixComponent::RootDir); + } + c => { + unix_path.push(c); + } + } + } + unix_path +} + +#[cfg(test)] +mod tests { + use super::*; + use rstest::rstest; + #[rstest] + #[case("/", "/")] + #[case(r#"\"#, "/")] + #[case("/test/test2", "/test/test2")] + #[case(r#"\test\test2"#, "/test/test2")] + #[case(r#"C:\"#, "/C")] + #[case(r#"C:\dir"#, "/C/dir")] + #[case(r#"a\b\"#, "a/b")] + #[case(r#"a\b\c"#, "a/b/c")] + fn test_typed_path_to_unix_path(#[case] windows_path: &str, #[case] unix_path: &str) { + assert_eq!( + windows_path_to_unix_path(WindowsPath::new(windows_path)) + .to_str() + .unwrap(), + UnixPath::new(unix_path).to_str().unwrap() + ); + } +} From 9401a4ec8ec76d67a9d0e93fc7c03d882b95ddfe Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Tue, 14 Jan 2025 22:42:51 +0100 Subject: [PATCH 17/22] more tests --- crates/core/tests/integration/vfs.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/core/tests/integration/vfs.rs b/crates/core/tests/integration/vfs.rs index f58685fb..da0b88e2 100644 --- a/crates/core/tests/integration/vfs.rs +++ b/crates/core/tests/integration/vfs.rs @@ -6,7 +6,10 @@ use insta::Settings; use pretty_assertions::assert_eq; use rstest::rstest; -use rustic_core::{BackupOptions, repofile::SnapshotFile, vfs::Vfs}; +use rustic_core::{ + BackupOptions, SnapshotOptions, + vfs::{IdenticalSnapshot, Latest, Vfs}, +}; use typed_path::UnixPath; use super::{ @@ -26,7 +29,8 @@ fn test_vfs( // we use as_path to not depend on the actual tempdir let opts = BackupOptions::default().as_path(PathBuf::from_str("test")?); // backup test-data - let snapshot = repo.backup(&opts, paths, SnapshotFile::default())?; + let snap_opts = SnapshotOptions::default().label("label".to_string()); + let snapshot = repo.backup(&opts, paths, snap_opts.to_snapshot()?)?; // re-read index let repo = repo.to_indexed()?; @@ -54,5 +58,17 @@ fn test_vfs( let node = vfs.node_from_path(&repo, UnixPath::new("test/0/tests/empty-file"))?; let file = repo.open_file(&node)?; assert_eq!(Bytes::new(), repo.read_file_at(&file, 0, 0)?); // empty files + + // create Vfs from snapshots + let vfs = Vfs::from_snapshots( + vec![snapshot], + "{label}/{time}", + "%Y", + Latest::AsDir, + IdenticalSnapshot::AsLink, + )?; + let entries_from_snapshots = + vfs.dir_entries_from_path(&repo, UnixPath::new("label/latest/test/0/tests"))?; + assert_eq!(entries_from_snapshots, entries); Ok(()) } From 465527585ecf685d1bdb14df682dd270fa176c34 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sun, 19 Jan 2025 22:00:55 +0100 Subject: [PATCH 18/22] more util funcs --- crates/core/src/backend/ignore.rs | 9 +- crates/core/src/backend/local_destination.rs | 27 ++--- crates/core/src/util.rs | 109 +++++++++++++++++-- 3 files changed, 115 insertions(+), 30 deletions(-) diff --git a/crates/core/src/backend/ignore.rs b/crates/core/src/backend/ignore.rs index 6817dffc..b5132ed5 100644 --- a/crates/core/src/backend/ignore.rs +++ b/crates/core/src/backend/ignore.rs @@ -31,6 +31,7 @@ use crate::{ node::{Metadata, Node, NodeType}, }, error::{ErrorKind, RusticError, RusticResult}, + util::path_to_unix_path, }; /// [`IgnoreErrorKind`] describes the errors that can be returned by a Ignore action in Backends @@ -59,6 +60,8 @@ pub enum IgnoreErrorKind { #[cfg(not(windows))] /// Error acquiring metadata for `{name}`: `{source:?}` AcquiringMetadataFailed { name: String, source: ignore::Error }, + /// Non-UTF8 filename is not allowed: `{0:?}` + Utf8Error(#[from] std::str::Utf8Error), } pub(crate) type IgnoreResult = Result; @@ -530,9 +533,7 @@ fn map_entry( let path = entry.into_path(); let open = Some(OpenFile(path.clone())); let path = path.strip_prefix(base_path).unwrap(); - let path = WindowsPath::new(path.as_os_str().as_encoded_bytes()) - .to_path_buf() - .with_encoding::(); + let path = path_to_unix_path(path)?.to_path_buf(); Ok(ReadSourceEntry { path, node, open }) } @@ -731,7 +732,7 @@ fn map_entry( let path = entry.into_path(); let open = Some(OpenFile(path.clone())); let path = path.strip_prefix(base_path).unwrap(); - let path = UnixPath::new(path.as_os_str().as_encoded_bytes()).to_path_buf(); + let path = path_to_unix_path(path)?.to_path_buf(); Ok(ReadSourceEntry { path, node, open }) } diff --git a/crates/core/src/backend/local_destination.rs b/crates/core/src/backend/local_destination.rs index cb33ba4f..82a28ac4 100644 --- a/crates/core/src/backend/local_destination.rs +++ b/crates/core/src/backend/local_destination.rs @@ -32,6 +32,7 @@ use crate::backend::node::NodeType; use crate::{ backend::node::{ExtendedAttribute, Metadata, Node}, error::{ErrorKind, RusticError, RusticResult}, + util::{typed_path_to_path, unix_path_to_path}, }; /// [`LocalDestinationErrorKind`] describes the errors that can be returned by an action on the filesystem in Backends @@ -103,9 +104,8 @@ pub enum LocalDestinationErrorKind { filename: PathBuf, source: std::io::Error, }, - #[cfg(windows)] /// Non-UTF8 filename is not allowed: `{0:?}` - Utf8Error(std::str::Utf8Error), + Utf8Error(#[from] std::str::Utf8Error), } pub(crate) type LocalDestinationResult = Result; @@ -198,18 +198,8 @@ impl LocalDestination { if self.is_file { return Ok(self.path.clone()); } - #[cfg(not(windows))] - { - let item = PathBuf::from(item.as_ref()); - Ok(self.path.join(item)) - } - #[cfg(windows)] - { - // only utf8 items are allowed on windows - let item = std::str::from_utf8(item.as_ref().as_bytes()) - .map_err(LocalDestinationErrorKind::Utf8Error)?; - Ok(self.path.join(item)) - } + let item = unix_path_to_path(item.as_ref())?; + Ok(self.path.join(item)) } /// Remove the given directory (relative to the base path) @@ -648,11 +638,12 @@ impl LocalDestination { match &node.node_type { NodeType::Symlink { .. } => { - let linktarget: PathBuf = - node.node_type.to_link().to_path_buf().try_into().unwrap(); // TODO: Error handling - symlink(linktarget.clone(), &filename).map_err(|err| { + let linktarget = node.node_type.to_link(); + let linktarget = typed_path_to_path(&linktarget) + .map_err(LocalDestinationErrorKind::Utf8Error)?; + symlink(&linktarget, &filename).map_err(|err| { LocalDestinationErrorKind::SymlinkingFailed { - linktarget, + linktarget: linktarget.to_path_buf(), filename, source: err, } diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index fa4071ea..807bafeb 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -1,5 +1,6 @@ /// Utilities for handling paths on ``rustic_core`` -use std::borrow::Cow; +/// +use std::{borrow::Cow, ffi::OsStr, path::Path, str::Utf8Error}; use globset::GlobMatcher; use serde::{Serialize, Serializer}; @@ -49,7 +50,96 @@ where serializer.serialize_str(&s) } -/// Converts a [`TypedPath`] to an [`Cow`]. +/// Converts a [`Path`] to a [`WindowsPath`]. +/// +/// # Arguments +/// +/// * `path` - The path to convert. +/// +/// # Errors +/// +/// * If the path is non-unicode +pub fn path_to_windows_path(path: &Path) -> Result<&WindowsPath, Utf8Error> { + let str = std::str::from_utf8(path.as_os_str().as_encoded_bytes())?; + Ok(WindowsPath::new(str)) +} + +/// Converts a [`Path`] to a [`Cow`]. +/// +/// Note: On windows, this converts prefixes into unix paths, e.g. "C:\dir" into "/c/dir" +/// +/// # Arguments +/// +/// * `path` - The path to convert. +/// +/// # Errors +/// +/// * If the path is non-unicode and we are using windows +pub fn path_to_unix_path(path: &Path) -> Result, Utf8Error> { + #[cfg(not(windows))] + { + let path = UnixPath::new(path.as_os_str().as_encoded_bytes()); + Ok(Cow::Borrowed(path)) + } + #[cfg(windows)] + { + let path = windows_path_to_unix_path(path_to_windows_path(path)?); + Ok(Cow::Owned(path)) + } +} + +/// Converts a [`TypedPath`] to a [`Cow`]. +/// +/// Note: On unix, this converts windows prefixes into unix paths, e.g. "C:\dir" into "/c/dir" +/// +/// # Arguments +/// +/// * `path` - The path to convert. +/// +/// # Errors +/// +/// * If the path is non-unicode and we are using windows +pub fn typed_path_to_path<'a>(path: &'a TypedPath<'a>) -> Result, Utf8Error> { + #[cfg(not(windows))] + { + let path = match typed_path_to_unix_path(path) { + Cow::Borrowed(path) => Cow::Borrowed(unix_path_to_path(path)?), + Cow::Owned(path) => Cow::Owned(unix_path_to_path(&path)?.to_path_buf()), + }; + Ok(path) + } + #[cfg(windows)] + { + // only utf8 items are allowed on windows + let str = std::str::from_utf8(path.as_bytes())?; + Ok(Cow::Borrowed(Path::new(str))) + } +} + +/// Converts a [`UnixPath`] to a [`Path`]. +/// +/// # Arguments +/// +/// * `path` - The path to convert. +/// +/// # Errors +/// +/// * If the path is non-unicode and we are using windows +pub fn unix_path_to_path(path: &UnixPath) -> Result<&Path, Utf8Error> { + #[cfg(not(windows))] + { + let osstr: &OsStr = path.as_ref(); + Ok(Path::new(osstr)) + } + #[cfg(windows)] + { + // only utf8 items are allowed on windows + let str = std::str::from_utf8(path.as_bytes())?; + Ok(Path::new(str)) + } +} + +/// Converts a [`TypedPath`] to a [`Cow`]. /// /// # Arguments /// @@ -64,6 +154,8 @@ pub fn typed_path_to_unix_path<'a>(path: &'a TypedPath<'_>) -> Cow<'a, UnixPath> /// Converts a [`WindowsPath`] to a [`UnixPathBuf`]. /// +/// Note: This converts windows prefixes into unix paths, e.g. "C:\dir" into "/c/dir" +/// /// # Arguments /// /// * `path` - The path to convert. @@ -77,13 +169,13 @@ pub fn windows_path_to_unix_path(path: &WindowsPath) -> UnixPathBuf { unix_path.push(UnixComponent::RootDir); match p.kind() { WindowsPrefix::Verbatim(p) | WindowsPrefix::DeviceNS(p) => { - unix_path.push(p); + unix_path.push(p.to_ascii_lowercase()); } WindowsPrefix::VerbatimUNC(_, q) | WindowsPrefix::UNC(_, q) => { - unix_path.push(q); + unix_path.push(q.to_ascii_lowercase()); } WindowsPrefix::VerbatimDisk(p) | WindowsPrefix::Disk(p) => { - let c = vec![p]; + let c = vec![p.to_ascii_lowercase()]; unix_path.push(&c); } } @@ -120,11 +212,12 @@ mod tests { #[case(r#"\"#, "/")] #[case("/test/test2", "/test/test2")] #[case(r#"\test\test2"#, "/test/test2")] - #[case(r#"C:\"#, "/C")] - #[case(r#"C:\dir"#, "/C/dir")] + #[case(r#"C:\"#, "/c")] + #[case(r#"C:\dir"#, "/c/dir")] + #[case(r#"d:\"#, "/d")] #[case(r#"a\b\"#, "a/b")] #[case(r#"a\b\c"#, "a/b/c")] - fn test_typed_path_to_unix_path(#[case] windows_path: &str, #[case] unix_path: &str) { + fn test_windows_path_to_unix_path(#[case] windows_path: &str, #[case] unix_path: &str) { assert_eq!( windows_path_to_unix_path(WindowsPath::new(windows_path)) .to_str() From b6abc4eea5eec13a5e51a7b5ed55d6960e28772c Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Fri, 23 May 2025 22:34:20 +0200 Subject: [PATCH 19/22] adaptions to use with rustic --- crates/core/src/lib.rs | 3 ++ crates/core/src/util.rs | 53 ++++++++++++--------------- crates/core/tests/integration/find.rs | 18 ++++++--- examples/find/examples/find.rs | 4 +- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index a6bf6ef5..214fd09a 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -160,3 +160,6 @@ pub use crate::{ command_input::{CommandInput, CommandInputErrorKind}, }, }; + +// re-exports +pub use typed_path; diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 807bafeb..6dbd8b7c 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -1,42 +1,18 @@ /// Utilities for handling paths on ``rustic_core`` /// -use std::{borrow::Cow, ffi::OsStr, path::Path, str::Utf8Error}; +use std::{ + borrow::Cow, + ffi::OsStr, + path::{Path, PathBuf}, + str::Utf8Error, +}; -use globset::GlobMatcher; use serde::{Serialize, Serializer}; use typed_path::{ Component, TypedPath, UnixComponent, UnixPath, UnixPathBuf, WindowsComponent, WindowsPath, WindowsPrefix, }; -/// Extend `globset::GlobMatcher` to allow mathing on unix paths (on every platform) -pub trait GlobMatcherExt { - /// Match on unix paths, i.e. paths which are available as `&[u8]` - fn is_unix_match(&self, path: impl AsRef<[u8]>) -> bool; -} - -impl GlobMatcherExt for GlobMatcher { - // This is a hacky implementation, espeically for windows where we convert lossily - // into an utf8 string and match on the windows path given by that string. - // Note: `GlobMatcher` internally converts into a `&[u8]` to perform the matching - // TODO: Use https://github.com/BurntSushi/ripgrep/pull/2955 once it is available. - #[cfg(not(windows))] - fn is_unix_match(&self, path: impl AsRef<[u8]>) -> bool { - use std::{ffi::OsStr, os::unix::ffi::OsStrExt, path::PathBuf}; - - let path = PathBuf::from(OsStr::from_bytes(path.as_ref())); - self.is_match(&path) - } - #[cfg(windows)] - fn is_unix_match(&self, path: impl AsRef<[u8]>) -> bool { - use std::{ffi::OsStr, path::Path}; - - let string: &str = &String::from_utf8_lossy(path.as_ref()); - let path = Path::new(OsStr::new(string)); - self.is_match(path) - } -} - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)] #[serde(transparent)] /// Like `UnixPathBuf` , but implements `Serialize` @@ -139,6 +115,23 @@ pub fn unix_path_to_path(path: &UnixPath) -> Result<&Path, Utf8Error> { } } +/// Converts a [`[u8]`] to a [`PathBuf`]. +// This is a hacky implementation, espeically for windows where we convert lossily +// into an utf8 string and match on the windows path given by that string. +// Note: `GlobMatcher` internally converts into a `&[u8]` to perform the matching +// TODO: Use https://github.com/BurntSushi/ripgrep/pull/2955 once it is available. +#[cfg(not(windows))] +pub fn u8_to_path(path: impl AsRef<[u8]>) -> PathBuf { + use std::{ffi::OsStr, os::unix::ffi::OsStrExt}; + Path::new(OsStr::from_bytes(path.as_ref())).to_path_buf() +} +#[cfg(windows)] +pub fn u8_to_path(&self, path: impl AsRef<[u8]>) -> PathBuf { + use std::ffi::OsStr; + let string: &str = &String::from_utf8_lossy(path.as_ref()); + Path::new(OsStr::new(string)).to_path_buf() +} + /// Converts a [`TypedPath`] to a [`Cow`]. /// /// # Arguments diff --git a/crates/core/tests/integration/find.rs b/crates/core/tests/integration/find.rs index a7d1c1c4..d7b44f81 100644 --- a/crates/core/tests/integration/find.rs +++ b/crates/core/tests/integration/find.rs @@ -10,7 +10,7 @@ use rstest::rstest; use rustic_core::{ BackupOptions, FindMatches, FindNode, repofile::{Node, SnapshotFile}, - util::{GlobMatcherExt, SerializablePath}, + util::{SerializablePath, u8_to_path}, }; use typed_path::UnixPath; @@ -36,8 +36,10 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) assert_ron("find-nodes-not-found", not_found); // test non-existing match let glob = Glob::new("not_existing")?.compile_matcher(); - let FindMatches { paths, matches, .. } = - repo.find_matching_nodes(vec![snapshot.tree], &|path, _| glob.is_unix_match(path))?; + let FindMatches { paths, matches, .. } = repo + .find_matching_nodes(vec![snapshot.tree], &|path, _| { + glob.is_match(u8_to_path(path)) + })?; assert!(paths.is_empty()); assert_eq!(matches, [[]]); @@ -48,7 +50,10 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) // test existing match let glob = Glob::new("testfile")?.compile_matcher(); let match_func = |path: &UnixPath, _: &Node| { - glob.is_unix_match(path) || path.file_name().is_some_and(|f| glob.is_unix_match(f)) + glob.is_match(u8_to_path(path)) + || path + .file_name() + .is_some_and(|f| glob.is_match(u8_to_path(f))) }; let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; @@ -57,7 +62,10 @@ fn test_find(tar_gz_testdata: Result, set_up_repo: Result) // test existing match let glob = Glob::new("testfile*")?.compile_matcher(); let match_func = |path: &UnixPath, _: &Node| { - glob.is_unix_match(path) || path.file_name().is_some_and(|f| glob.is_unix_match(f)) + glob.is_match(u8_to_path(path)) + || path + .file_name() + .is_some_and(|f| glob.is_match(u8_to_path(f))) }; let FindMatches { paths, matches, .. } = repo.find_matching_nodes(vec![snapshot.tree], &match_func)?; diff --git a/examples/find/examples/find.rs b/examples/find/examples/find.rs index b5c1854d..060847b8 100644 --- a/examples/find/examples/find.rs +++ b/examples/find/examples/find.rs @@ -1,7 +1,7 @@ //! `ls` example use globset::Glob; use rustic_backend::BackendOptions; -use rustic_core::{util::GlobMatcherExt, FindMatches, Repository, RepositoryOptions}; +use rustic_core::{util::u8_to_path, FindMatches, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; use std::error::Error; @@ -32,7 +32,7 @@ fn main() -> Result<(), Box> { paths, nodes, matches, - } = repo.find_matching_nodes(tree_ids, &|path, _| glob.is_unix_match(path))?; + } = repo.find_matching_nodes(tree_ids, &|path, _| glob.is_match(u8_to_path(path)))?; for (snap, matches) in snapshots.iter().zip(matches) { println!("results in {snap:?}"); for (path_idx, node_idx) in matches { From 098264443175e11bc521d19f7c4a5188f328e12b Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Fri, 23 May 2025 22:57:52 +0200 Subject: [PATCH 20/22] fix windows build --- crates/core/src/backend/ignore.rs | 2 -- crates/core/src/backend/local_destination.rs | 4 ++-- crates/core/src/util.rs | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/core/src/backend/ignore.rs b/crates/core/src/backend/ignore.rs index b5132ed5..c1267aaa 100644 --- a/crates/core/src/backend/ignore.rs +++ b/crates/core/src/backend/ignore.rs @@ -463,8 +463,6 @@ fn map_entry( with_atime: bool, _ignore_devid: bool, ) -> IgnoreResult> { - use typed_path::{UnixEncoding, WindowsPath}; - let name = entry.file_name().as_encoded_bytes(); let m = entry .metadata() diff --git a/crates/core/src/backend/local_destination.rs b/crates/core/src/backend/local_destination.rs index 82a28ac4..f8a8f288 100644 --- a/crates/core/src/backend/local_destination.rs +++ b/crates/core/src/backend/local_destination.rs @@ -28,11 +28,11 @@ use typed_path::UnixPath; #[cfg(not(windows))] use crate::backend::ignore::mapper::map_mode_from_go; #[cfg(not(windows))] -use crate::backend::node::NodeType; +use crate::{backend::node::NodeType, util::typed_path_to_path}; use crate::{ backend::node::{ExtendedAttribute, Metadata, Node}, error::{ErrorKind, RusticError, RusticResult}, - util::{typed_path_to_path, unix_path_to_path}, + util::unix_path_to_path, }; /// [`LocalDestinationErrorKind`] describes the errors that can be returned by an action on the filesystem in Backends diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 6dbd8b7c..acacfa2c 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -2,7 +2,6 @@ /// use std::{ borrow::Cow, - ffi::OsStr, path::{Path, PathBuf}, str::Utf8Error, }; @@ -104,6 +103,7 @@ pub fn typed_path_to_path<'a>(path: &'a TypedPath<'a>) -> Result, pub fn unix_path_to_path(path: &UnixPath) -> Result<&Path, Utf8Error> { #[cfg(not(windows))] { + use std::ffi::OsStr; let osstr: &OsStr = path.as_ref(); Ok(Path::new(osstr)) } @@ -126,7 +126,7 @@ pub fn u8_to_path(path: impl AsRef<[u8]>) -> PathBuf { Path::new(OsStr::from_bytes(path.as_ref())).to_path_buf() } #[cfg(windows)] -pub fn u8_to_path(&self, path: impl AsRef<[u8]>) -> PathBuf { +pub fn u8_to_path(path: impl AsRef<[u8]>) -> PathBuf { use std::ffi::OsStr; let string: &str = &String::from_utf8_lossy(path.as_ref()); Path::new(OsStr::new(string)).to_path_buf() From 4a343072c910bbf3af0ce5cb153a7d3b593cad1c Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Wed, 9 Jul 2025 14:33:43 +0200 Subject: [PATCH 21/22] make helper funcs public --- crates/core/src/util.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index b070b5e4..6fb55f71 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -140,7 +140,7 @@ pub fn u8_to_path(path: impl AsRef<[u8]>) -> PathBuf { /// /// * `path` - The path to convert. #[must_use] -pub fn typed_path_to_unix_path<'a>(path: &'a TypedPath<'_>) -> Cow<'a, UnixPath> { +fn typed_path_to_unix_path<'a>(path: &'a TypedPath<'_>) -> Cow<'a, UnixPath> { match path { TypedPath::Unix(p) => Cow::Borrowed(p), TypedPath::Windows(p) => Cow::Owned(windows_path_to_unix_path(p)), @@ -155,7 +155,7 @@ pub fn typed_path_to_unix_path<'a>(path: &'a TypedPath<'_>) -> Cow<'a, UnixPath> /// /// * `path` - The path to convert. #[must_use] -pub fn windows_path_to_unix_path(path: &WindowsPath) -> UnixPathBuf { +fn windows_path_to_unix_path(path: &WindowsPath) -> UnixPathBuf { let mut unix_path = UnixPathBuf::new(); let mut components = path.components(); if let Some(c) = components.next() { From 5db575d1791ea5d6e3dc1917b1e975e0fa5d42f0 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Thu, 23 Oct 2025 11:15:09 +0200 Subject: [PATCH 22/22] adapt check errors --- crates/core/src/commands/check.rs | 35 +- crates/core/src/util.rs | 11 +- crates/core/tests/integration/check.rs | 2 +- ...check__repo-index-missing-blob.tar.gz.snap | 8 +- ..._find__find-matching-nodes-not-found.snap} | 1 - .../integration__integration__ls__ls.snap.new | 4672 ----------------- 6 files changed, 36 insertions(+), 4693 deletions(-) rename crates/core/tests/integration/snapshots/{integration__integration__find__find-matching-nodes-not-found.snap.new => integration__integration__find__find-matching-nodes-not-found.snap} (89%) delete mode 100644 crates/core/tests/integration/snapshots/integration__integration__ls__ls.snap.new diff --git a/crates/core/src/commands/check.rs b/crates/core/src/commands/check.rs index 35b977f6..8ce86349 100644 --- a/crates/core/src/commands/check.rs +++ b/crates/core/src/commands/check.rs @@ -3,7 +3,6 @@ use std::{ collections::{BTreeMap, BTreeSet, HashMap}, fmt::Debug, num::ParseIntError, - path::PathBuf, str::FromStr, sync::Mutex, }; @@ -35,6 +34,7 @@ use crate::{ IndexFile, IndexPack, PackHeader, PackHeaderLength, PackHeaderRef, packfile::PackId, }, repository::{Open, Repository}, + util::SerializablePath, }; #[derive(Clone, Copy, Debug, Default)] @@ -643,14 +643,14 @@ fn check_trees( NodeType::File => node.content.as_ref().map_or_else( || { collector.add_error(CheckError::FileHasNoContent { - file: path.join(node.name()), + file: SerializablePath(path.join(node.name())), }); }, |content| { for (i, id) in content.iter().enumerate() { if id.is_null() { collector.add_error(CheckError::FileBlobHasNullId { - file: path.join(node.name()), + file: SerializablePath(path.join(node.name())), blob_num: i, }); } @@ -658,7 +658,7 @@ fn check_trees( match index.get_data(id) { None => { collector.add_error(CheckError::FileBlobNotInIndex { - file: path.join(node.name()), + file: SerializablePath(path.join(node.name())), blob_id: *id, }); } @@ -673,17 +673,17 @@ fn check_trees( NodeType::Dir => { match node.subtree { None => collector.add_error(CheckError::NoSubTree { - dir: path.join(node.name()), + dir: SerializablePath(path.join(node.name())), }), Some(tree) if tree.is_null() => { collector.add_error(CheckError::NullSubTree { - dir: path.join(node.name()), + dir: SerializablePath(path.join(node.name())), }); } Some(id) => match index.get_tree(&id) { None => { collector.add_error(CheckError::SubTreeMissingInIndex { - dir: path.join(node.name()), + dir: SerializablePath(path.join(node.name())), blob_id: id, }); } @@ -887,17 +887,26 @@ pub enum CheckError { /// pack {id} is referenced by the index but not present! To repair: 'rustic repair index'. NoPack { id: PackId }, /// file {file:?} doesn't have a content - FileHasNoContent { file: PathBuf }, + FileHasNoContent { file: SerializablePath }, /// file {file:?} blob {blob_num} has null ID - FileBlobHasNullId { file: PathBuf, blob_num: usize }, + FileBlobHasNullId { + file: SerializablePath, + blob_num: usize, + }, /// file {file:?} blob {blob_id} is missing in index - FileBlobNotInIndex { file: PathBuf, blob_id: DataId }, + FileBlobNotInIndex { + file: SerializablePath, + blob_id: DataId, + }, /// dir {dir:?} doesn't have a subtree - NoSubTree { dir: PathBuf }, + NoSubTree { dir: SerializablePath }, /// "dir {dir:?} subtree has null ID - NullSubTree { dir: PathBuf }, + NullSubTree { dir: SerializablePath }, /// "dir {dir:?} subtree blob {blob_id} is missing in index", - SubTreeMissingInIndex { dir: PathBuf, blob_id: TreeId }, + SubTreeMissingInIndex { + dir: SerializablePath, + blob_id: TreeId, + }, /// pack {id}: data size does not match expected size. Read: {size} bytes, expected: {expected} bytes PackSizeMismatch { id: PackId, diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 6fb55f71..83495529 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -2,6 +2,7 @@ /// use std::{ borrow::Cow, + fmt::Debug, path::{Path, PathBuf}, str::Utf8Error, }; @@ -12,9 +13,9 @@ use typed_path::{ WindowsPrefix, }; -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Serialize)] #[serde(transparent)] -/// Like `UnixPathBuf`, but implements `Serialize` +/// Like `UnixPathBuf`, but implements `Serialize` and a nicer `Debug` pub struct SerializablePath(#[serde(serialize_with = "serialize_unix_path")] pub UnixPathBuf); fn serialize_unix_path(path: &UnixPath, serializer: S) -> Result @@ -25,6 +26,12 @@ where serializer.serialize_str(&s) } +impl Debug for SerializablePath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0.display()) + } +} + /// Converts a [`Path`] to a [`WindowsPath`]. /// /// # Arguments diff --git a/crates/core/tests/integration/check.rs b/crates/core/tests/integration/check.rs index a23b9c24..15d0e726 100644 --- a/crates/core/tests/integration/check.rs +++ b/crates/core/tests/integration/check.rs @@ -13,7 +13,7 @@ use rustic_core::{CheckOptions, Repository, RepositoryBackends, RepositoryOption #[rstest] #[case("repo-data-missing.tar.gz")] #[case("repo-duplicates.tar.gz")] -// #[case("repo-index-missing-blob.tar.gz")] TODO: Activate when paths are identical for unix/windows +#[case("repo-index-missing-blob.tar.gz")] #[case("repo-index-missing.tar.gz")] #[case("repo-mixed.tar.gz")] #[case("repo-obsolete-index.tar.gz")] diff --git a/crates/core/tests/integration/snapshots/integration__integration__check__repo-index-missing-blob.tar.gz.snap b/crates/core/tests/integration/snapshots/integration__integration__check__repo-index-missing-blob.tar.gz.snap index 3556989e..ab1cd0c5 100644 --- a/crates/core/tests/integration/snapshots/integration__integration__check__repo-index-missing-blob.tar.gz.snap +++ b/crates/core/tests/integration/snapshots/integration__integration__check__repo-index-missing-blob.tar.gz.snap @@ -2,8 +2,8 @@ source: crates/core/tests/integration/check.rs expression: check_results --- -CheckResults { - errors: [ +CheckResults( + [ ( Warn, PackNotReferenced { @@ -19,11 +19,11 @@ CheckResults { ( Error, FileBlobNotInIndex { - file: "home/thinkpad/data/test", + file: home/thinkpad/data/test, blob_id: DataId( f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2, ), }, ), ], -} +) diff --git a/crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap.new b/crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap similarity index 89% rename from crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap.new rename to crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap index 448cec04..3561b35a 100644 --- a/crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap.new +++ b/crates/core/tests/integration/snapshots/integration__integration__find__find-matching-nodes-not-found.snap @@ -1,6 +1,5 @@ --- source: crates/core/tests/integration/find.rs -assertion_line: 45 expression: not_found --- FindMatches { diff --git a/crates/core/tests/integration/snapshots/integration__integration__ls__ls.snap.new b/crates/core/tests/integration/snapshots/integration__integration__ls__ls.snap.new deleted file mode 100644 index 75edf71e..00000000 --- a/crates/core/tests/integration/snapshots/integration__integration__ls__ls.snap.new +++ /dev/null @@ -1,4672 +0,0 @@ ---- -source: crates/core/tests/integration/ls.rs -assertion_line: 46 -expression: "&entries" ---- -{ - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - ], - }: Node { - name: "test", - node_type: Dir, - meta: Metadata { - mode: Some( - 493, - ), - mtime: None, - atime: None, - ctime: None, - uid: None, - gid: None, - user: None, - group: None, - inode: 0, - device_id: 0, - size: 0, - links: 0, - extended_attributes: [], - }, - content: None, - subtree: Some( - TreeId( - 43a5917774aebc9ae029aa26be74aa14c9e79b88279389d6c53b9c8f238ad2eb, - ), - ), - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - ], - }: Node { - name: "0", - node_type: Dir, - meta: Metadata { - mode: Some( - 2147484141, - ), - mtime: Some( - 2025-10-22T22:24:12.609105638+02:00, - ), - atime: Some( - 2025-10-22T22:24:12.609105638+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609306758+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1719, - device_id: 48, - size: 0, - links: 0, - extended_attributes: [], - }, - content: None, - subtree: Some( - TreeId( - d476f10522de896704c69222812cf28106c9e26726ce5b25a057593993b4d0c3, - ), - ), - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - ], - }: Node { - name: "0", - node_type: Dir, - meta: Metadata { - mode: Some( - 2147484141, - ), - mtime: Some( - 2025-10-22T22:24:12.595413596+02:00, - ), - atime: Some( - 2025-10-22T22:24:12.595413596+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609306758+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1720, - device_id: 48, - size: 0, - links: 0, - extended_attributes: [], - }, - content: None, - subtree: Some( - TreeId( - 7e616ff2cea902a2864da30f0ff98ea5b8050299bf4dc46e776025cab7b46478, - ), - ), - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - ], - }: Node { - name: "9", - node_type: Dir, - meta: Metadata { - mode: Some( - 2147484141, - ), - mtime: Some( - 2025-10-22T22:24:12.609105638+02:00, - ), - atime: Some( - 2025-10-22T22:24:12.609105638+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609306758+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1721, - device_id: 48, - size: 0, - links: 0, - extended_attributes: [], - }, - content: None, - subtree: Some( - TreeId( - ddc76bc20bcb185e355b1b1608d75bad879305909ec3a1f81d27864bca438472, - ), - ), - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 48, - ], - }: Node { - name: "0", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595413596+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1722, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - aa6da76dc4b3bd8d8cd9d42c5ed6db2e9c3e43cecef7e7ee925a3accab96ad79, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - ], - }: Node { - name: "1", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.607515957+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1758, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - bd15c2d9117aa305a0cf99c9cc30359b66ee454b00b897fb1a925c56025f4556, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 48, - ], - }: Node { - name: "10", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608340687+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1774, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 753d116d34c772059a9a4a2ca33f6ea9ac618470acc13687d08fff7eb5ad3c72, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 49, - ], - }: Node { - name: "11", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595960860+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1736, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - efe42dd90fd602df1c5b3b7675cd845e23befba069eac0222a9997fea2a9852f, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 50, - ], - }: Node { - name: "12", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608615622+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1780, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 032ad465747269b41dbdfc87accad1fd49e8dc574ae4bfaf49f036150ec92e57, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 51, - ], - }: Node { - name: "13", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595919909+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1735, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - b0b5afc29b6391722bd60a9a74c386c856cb130a219954f34944a4f19bbecc41, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 52, - ], - }: Node { - name: "14", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596636353+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1751, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - b19a49a2e711d5547e1fb247170c58d2da17f9b0bbf75eb84507097a301f1c6d, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 53, - ], - }: Node { - name: "15", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595505664+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1723, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - f11e72e0290dc75261ee48c39fd42597bdcd80e110ffd0651f71d54ea22030e5, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 54, - ], - }: Node { - name: "16", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595825633+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1732, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 71935a6f85198c65584ed5c4ff5e34cf93cb79756023e82ecc85153663be2b00, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 55, - ], - }: Node { - name: "17", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609105638+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1790, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - b81f6dd6ad54fbed57ffac74bcecf9f1a56ce8948edc92153ee34ed415ed68d1, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 56, - ], - }: Node { - name: "18", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609008998+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1788, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 1fad45c4bf21695fae9e100626f42d35d8c2c48cb90f6a6256973c262608879d, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 49, - 57, - ], - }: Node { - name: "19", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608418596+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1776, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 92c4aa4fa67ede99b9ab1b694bc32b4f3d127f406a7e6583ecbe55b1bb919928, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - ], - }: Node { - name: "2", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596278897+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1745, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - f5a3517234ef2f213afe617cdab9d25364e1c8c9e61198bf387b157542ee7dbb, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 48, - ], - }: Node { - name: "20", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608200399+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1771, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 737c19313675671c589bfc613b0b736b95a58218486d09d3a7a649f324d81ef7, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 49, - ], - }: Node { - name: "21", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596060385+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1739, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - ee25130a4e74b1306087799914d8b92aed2c385b06256705b18266ff088524f4, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 50, - ], - }: Node { - name: "22", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596315581+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1746, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 3492c28f8ddbb9186e483ef9eb1cc7042c5adf871a4d15d2edf8ee7b2b04069b, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 51, - ], - }: Node { - name: "23", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608012312+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1767, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - b321dedc16124312ed08cb7f9c67006bd6a4f946ca04bc178323e6147b081943, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 52, - ], - }: Node { - name: "24", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608799815+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1784, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 7020db85ffe6a3fd5f34971ff37a20fa5d32947181692bfbcca62cfc4a9221d0, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 53, - ], - }: Node { - name: "25", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609051604+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1789, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 75e7274ef54e91c2857ba0de4c869388df3f9c657d776a3e90eaf3f3f619536d, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 54, - ], - }: Node { - name: "26", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596174507+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1742, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 418b691988d9bc7357b99c5baf31e83436315d1bcc887482dd83a8537183af17, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 55, - ], - }: Node { - name: "27", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595576317+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1725, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 06a5749af5715af19fde59d1caaa542df00c56aa0058bf99b67dec5216c27df6, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 56, - ], - }: Node { - name: "28", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.607854465+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1764, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 61e8ffda5a11d743d7dc4bcfd1e18e8b6179a53f8b28b25adf55d3acb0b10eff, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 50, - 57, - ], - }: Node { - name: "29", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608688401+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1781, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 60c1c2f3a0b8e5b1c4e2b7f1453661170395c5c26f2e2d241e81eb929cfc930f, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - ], - }: Node { - name: "3", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.607682609+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1761, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - f0ad279385b5b5c862e1e2fdd599616115729e0b8085833d7b31de68c8326b85, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 48, - ], - }: Node { - name: "30", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608945209+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1787, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 790c34f1d4d95a6067f2406a78aa0dcca2fc2aa1db932fbd865ce9cafd7baa23, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 49, - ], - }: Node { - name: "31", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608768028+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1783, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 8e53f45db9249373828c588774471d34c9c1e436b83b1603a49d675de810db7e, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 50, - ], - }: Node { - name: "32", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596958041+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1754, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - c4eccf95ba2409d21283981297bf9919ec15c2cc0dadef84aafaad49a0c79130, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 51, - ], - }: Node { - name: "33", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595605532+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1726, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - da8b8b2d79bba6a3d4784233e4421293cea0cf27fd03521e11ce0c7c941f6dbb, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 52, - ], - }: Node { - name: "34", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608539886+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1778, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - a1413ef1018e7a4f3df7f19aad942a611cbda5bdd45d4269cb54d5f9fa58cd01, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 53, - ], - }: Node { - name: "35", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596432194+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1749, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - f534a7fe839d440a0fdd1d45254921601b2a90ff5b61edcdc631b722f84a0dbb, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 54, - ], - }: Node { - name: "36", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596136613+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1741, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - d56e2a8c47f4ae36886784c852916f36036f3daffa547a5922dda5814ecc5a86, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 55, - ], - }: Node { - name: "37", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608094635+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1769, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 5800df73dcfdbd261800f2474c8631fe0727fbc1788cfd70bd206ab2f50b0707, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 56, - ], - }: Node { - name: "38", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608385895+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1775, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - e7c987bd8d9257bea1e44f882bc075c84ee7f103d68eec8677e37b49d2854d90, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 51, - 57, - ], - }: Node { - name: "39", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596203681+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1743, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 03a1411035add5b10ed183b2ca7d8db8deee2402f45ee445f54de50896c3e26f, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - ], - }: Node { - name: "4", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.607766434+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1763, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 917aae16764fc905abf0514e965133ed80e2b66c426fb09be24eabd452d4438d, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 48, - ], - }: Node { - name: "40", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596105649+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1740, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 1adcd19cf770f7d8ab3bb4b3996fb6187f59c468953848ec4f119c9cef484f28, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 49, - ], - }: Node { - name: "41", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595891536+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1734, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - b5debda793f22adb93204dc81f107d03636b7431192fc8497f5c9a340e43460c, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 50, - ], - }: Node { - name: "42", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596785460+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1753, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - e6e9b35f3ca77afbd0043bc42085b814ceea8cfb93a0bcba9696dee5034ffead, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 51, - ], - }: Node { - name: "43", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596700291+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1752, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 31d87fd77a88ca00e9c5612f578c0b3243347ba328a6daa09742fdc8c7cec6f4, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 52, - ], - }: Node { - name: "44", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595854468+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1733, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 76aec0f2d829d01eff332099ebd5628f61adc72ff44e7d6b82beabc28fa44f1f, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 53, - ], - }: Node { - name: "45", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595644291+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1727, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 121242341ff94a05074a31cb2a0f566093e5c052e64dcde4c0c9b686e58daf87, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 54, - ], - }: Node { - name: "46", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595680037+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1728, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - a69efe0ab923672b5aa1a2113f54e80684a7c126c1114af4fac3953be18bea74, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 55, - ], - }: Node { - name: "47", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596031722+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1738, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - a50a7ab010d0d349b213576d1e2d2d2b345334be1be6f62798d250fc1e5cc51f, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 56, - ], - }: Node { - name: "48", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596997979+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1755, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 32715f839edd796c5b6f2d48c88748857ed439867c2dc4b2b65c5f96c6d9ef73, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 52, - 57, - ], - }: Node { - name: "49", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.607558482+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1759, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - d3fdbaa72299c1abfe47bc26bdf201cc5cb66efc031e517111d413c38bfea882, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - ], - }: Node { - name: "5", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595993941+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1737, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - de68adab4fe82b31fe5b14abe5d17faecf4f7c8d93d74374cbf3793701290ede, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 48, - ], - }: Node { - name: "50", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608726416+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1782, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 939e25879149a8b87ecb6b44ded96f6b029d5463d38fe5525005984d6d42c34a, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 49, - ], - }: Node { - name: "51", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595750657+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1730, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 958a554f813d199f9f897c9f5c4d90571dfe7f67d8a3733a64598906a8e7208d, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 50, - ], - }: Node { - name: "52", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595713600+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1729, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 2e8799330876d03e95428975593175ea5dd75ed3d5f3d83b4d9115dd2c6d3b71, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 51, - ], - }: Node { - name: "53", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.607898677+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1765, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - f476577492f325d8e617814bde3ecc83eb9e8662023d2e057c6c958ce77511e5, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 52, - ], - }: Node { - name: "54", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608273779+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1773, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - b03ca92f97233541fcc95c22455cadf63c5a25089117af4b433ffc1ed9a8991f, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 53, - ], - }: Node { - name: "55", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608046859+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1768, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 63d9f467b844e7d5a48120b236408e381b160c9c8355b0e3113cd407ad694ab4, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 54, - ], - }: Node { - name: "56", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596242308+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1744, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - c93bcbbab1c234a01574f913bc49cc2857ccc17e4d5d8c9dc9b9b720729ed3f7, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 55, - ], - }: Node { - name: "57", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608870720+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1785, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 86f85e1d9af7cac33a60fae72c05f0f410ec0c9dfd917ad18a7e2308a272e1fb, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 56, - ], - }: Node { - name: "58", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596344884+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1747, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 858f7dc0f2b2e7532fec3b2a2daa551855ebaf27e29b60a90807e3f770d60815, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 53, - 57, - ], - }: Node { - name: "59", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608904154+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1786, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 6047339cfc27b694b82bfc1621888997bf0e954bc5459a7ed51c424d7bd92583, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - ], - }: Node { - name: "6", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.606978417+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1757, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - f6eca86a06925045b3b3a09f38999144ace49570b7e3a203ca7f24644fe35f20, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - 48, - ], - }: Node { - name: "60", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608233150+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1772, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - fcd9ec0c99f7992c184666e3040831b919f3375157bd563a2b65cde1c6789847, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - 49, - ], - }: Node { - name: "61", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.606978417+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1756, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - f5738472e3ab1372588f9048c3198bb9e05338acc94c33aca3c34277c675116c, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - 50, - ], - }: Node { - name: "62", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608128200+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1770, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 7d07ba1cacc97dc5ae31b7e49a8926bec040102f1211fda9e98f0ea70a6aecf9, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - 51, - ], - }: Node { - name: "63", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.607939727+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1766, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - c8d553f609367281f390160dd77113ca952dbb7a7fd073daa2ac4620a63148b4, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - 52, - ], - }: Node { - name: "64", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.607605703+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1760, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 5d2c26fc0563f43d161b5abc982af18696d7178ad1f7aeeae940c595415cc6df, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - 53, - ], - }: Node { - name: "65", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595779512+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1731, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 2888d521727e1ba03471bcea1d48927818b54d42331caef8115fac5be94406b6, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - 54, - ], - }: Node { - name: "66", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.607724157+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1762, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - a9ba60dcf9b908c3488d03d2f3ecf9aff2fa1b419f30f3d938b2eb3c319566cc, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - 55, - ], - }: Node { - name: "67", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596470336+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1750, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 7246d1d67000b94ac3e1928fcef3804263156e72aeb886b00b3f59ec1090c427, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 54, - 56, - ], - }: Node { - name: "68", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.595547746+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1724, - device_id: 48, - size: 11520, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - a8fafe2f1d514d1efc327d76fcc33916c20acd6838f524848a259de752862c41, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 55, - ], - }: Node { - name: "7", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608459202+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1777, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 6a1da85d5dccfdefb75bb607e1f3ded8f6068085a7359ceb41a1b9930d9442f3, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 56, - ], - }: Node { - name: "8", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.596399830+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1748, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 105068aefc2d42cce9acc224bb369002f53f0036da394632f4623170f3b0277e, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 48, - 47, - 57, - 47, - 57, - ], - }: Node { - name: "9", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-05T22:44:35+02:00, - ), - atime: Some( - 2014-08-05T22:44:35+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.608582798+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1779, - device_id: 48, - size: 16384, - links: 1, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 656039f259149cf97af18fb11ebee3194cfaa059ff72d6bc9a2b91b0889d65cc, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 116, - 101, - 115, - 116, - 115, - ], - }: Node { - name: "tests", - node_type: Dir, - meta: Metadata { - mode: Some( - 2147484141, - ), - mtime: Some( - 2025-10-22T22:24:12.609283517+02:00, - ), - atime: Some( - 2025-10-22T22:24:12.609283517+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609306758+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1791, - device_id: 48, - size: 0, - links: 0, - extended_attributes: [], - }, - content: None, - subtree: Some( - TreeId( - 2bccf0bb6407b030100d471dcde0cc0637fcb335628548b2424df9404e91dd5c, - ), - ), - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 116, - 101, - 115, - 116, - 115, - 47, - 101, - 109, - 112, - 116, - 121, - 45, - 102, - 105, - 108, - 101, - ], - }: Node { - name: "empty-file", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-11-30T16:03:11+01:00, - ), - atime: Some( - 2014-11-30T16:03:11+01:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609260244+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1793, - device_id: 48, - size: 0, - links: 1, - extended_attributes: [], - }, - content: Some( - [], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 116, - 101, - 115, - 116, - 115, - 47, - 116, - 101, - 115, - 116, - 102, - 105, - 108, - 101, - ], - }: Node { - name: "testfile", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-09T14:14:20+02:00, - ), - atime: Some( - 2014-08-09T14:14:20+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609105638+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1792, - device_id: 48, - size: 21, - links: 2, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 649b8b471e7d7bc175eec758a7006ac693c434c8297c07db15286788c837154a, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 116, - 101, - 115, - 116, - 115, - 47, - 116, - 101, - 115, - 116, - 102, - 105, - 108, - 101, - 45, - 104, - 97, - 114, - 100, - 108, - 105, - 110, - 107, - ], - }: Node { - name: "testfile-hardlink", - node_type: File, - meta: Metadata { - mode: Some( - 420, - ), - mtime: Some( - 2014-08-09T14:14:20+02:00, - ), - atime: Some( - 2014-08-09T14:14:20+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609105638+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1792, - device_id: 48, - size: 21, - links: 2, - extended_attributes: [], - }, - content: Some( - [ - DataId( - 649b8b471e7d7bc175eec758a7006ac693c434c8297c07db15286788c837154a, - ), - ], - ), - subtree: None, - }, - PathBuf { - _encoding: "unix", - inner: [ - 116, - 101, - 115, - 116, - 47, - 48, - 47, - 116, - 101, - 115, - 116, - 115, - 47, - 116, - 101, - 115, - 116, - 102, - 105, - 108, - 101, - 45, - 115, - 121, - 109, - 108, - 105, - 110, - 107, - ], - }: Node { - name: "testfile-symlink", - node_type: Symlink { - linktarget: "testfile", - linktarget_raw: None, - }, - meta: Metadata { - mode: Some( - 134218239, - ), - mtime: Some( - 2014-08-09T14:14:28+02:00, - ), - atime: Some( - 2014-08-09T14:14:28+02:00, - ), - ctime: Some( - 2025-10-22T22:24:12.609283517+02:00, - ), - uid: Some( - 1002, - ), - gid: Some( - 1002, - ), - user: Some( - "alex-dev", - ), - group: Some( - "alex-dev", - ), - inode: 1794, - device_id: 48, - size: 8, - links: 1, - extended_attributes: [], - }, - content: None, - subtree: None, - }, -}