mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 13:20:42 +00:00
Add repository module and update dependencies
Signed-off-by: Till Wegmueller <toasterson@gmail.com>
This commit is contained in:
parent
bef5442afd
commit
1a5e0e053d
11 changed files with 2099 additions and 118 deletions
358
Cargo.lock
generated
358
Cargo.lock
generated
|
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
|
|
@ -17,6 +17,56 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
|
|
@ -52,6 +102,12 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
|
|
@ -108,29 +164,63 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"bitflags 1.3.2",
|
||||
"clap_derive 3.2.18",
|
||||
"clap_lex 0.2.4",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"strsim 0.10.0",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive 4.5.41",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex 0.7.5",
|
||||
"strsim 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.4.1",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
|
|
@ -140,6 +230,18 @@ dependencies = [
|
|||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
|
|
@ -262,33 +364,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
|
|
@ -396,7 +484,19 @@ checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -430,6 +530,12 @@ version = "0.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
|
|
@ -448,12 +554,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.9"
|
||||
|
|
@ -558,32 +658,18 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.6"
|
||||
|
|
@ -616,9 +702,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.141"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libips"
|
||||
|
|
@ -641,9 +727,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
|
|
@ -695,7 +781,7 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
|
|
@ -812,9 +898,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
|
|
@ -828,7 +920,7 @@ version = "0.10.55"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
|
|
@ -956,6 +1048,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"diff-struct",
|
||||
"libips",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -967,7 +1060,7 @@ name = "pkg6dev"
|
|||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"clap 3.2.23",
|
||||
"libips",
|
||||
"userland",
|
||||
]
|
||||
|
|
@ -975,13 +1068,21 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pkg6repo"
|
||||
version = "0.0.1-placeholder"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.5.41",
|
||||
"libips",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ports"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"clap 3.2.23",
|
||||
"libips",
|
||||
"reqwest",
|
||||
"shellexpand",
|
||||
|
|
@ -1033,22 +1134,19 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1057,8 +1155,8 @@ version = "0.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall 0.2.16",
|
||||
"getrandom 0.2.9",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
|
@ -1138,16 +1236,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.11"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.9.1",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1208,7 +1305,7 @@ version = "2.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
|
|
@ -1361,6 +1458,12 @@ version = "0.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.24.1"
|
||||
|
|
@ -1376,7 +1479,7 @@ version = "0.24.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
|
|
@ -1407,15 +1510,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.5.0"
|
||||
version = "3.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall 0.3.5",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1621,6 +1724,12 @@ dependencies = [
|
|||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
|
@ -1649,6 +1758,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.84"
|
||||
|
|
@ -1818,11 +1936,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1842,17 +1960,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.0",
|
||||
"windows_aarch64_msvc 0.48.0",
|
||||
"windows_i686_gnu 0.48.0",
|
||||
"windows_i686_msvc 0.48.0",
|
||||
"windows_x86_64_gnu 0.48.0",
|
||||
"windows_x86_64_gnullvm 0.48.0",
|
||||
"windows_x86_64_msvc 0.48.0",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1863,9 +1982,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
|
|
@ -1875,9 +1994,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
|
|
@ -1887,9 +2006,15 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
|
|
@ -1899,9 +2024,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
|
|
@ -1911,9 +2036,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
|
|
@ -1923,9 +2048,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
|
|
@ -1935,9 +2060,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
|
|
@ -1947,3 +2072,12 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
|||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@ edition = "2021"
|
|||
[dependencies]
|
||||
libips = { version = "0.1.2", path = "../../libips" }
|
||||
diff-struct = "0.5.3"
|
||||
serde = { version = "1.0.207", features = ["derive"] }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,95 @@
|
|||
fn main() {
|
||||
use diff::Diff;
|
||||
use libips::actions::File;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use libips::payload::Payload;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Diff)]
|
||||
#[diff(attr(
|
||||
#[derive(Debug, PartialEq)]
|
||||
))]
|
||||
struct Manifest {
|
||||
files: HashMap<String, File>
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
let base = Manifest{files: HashMap::from([
|
||||
("0dh5".to_string(), File{
|
||||
payload: None,
|
||||
path: "var/file".to_string(),
|
||||
group: "bin".to_string(),
|
||||
owner: "root".to_string(),
|
||||
mode: "0755".to_string(),
|
||||
preserve: false,
|
||||
overlay: false,
|
||||
original_name: "".to_string(),
|
||||
revert_tag: "".to_string(),
|
||||
sys_attr: "".to_string(),
|
||||
properties: vec![],
|
||||
facets: Default::default(),
|
||||
}),
|
||||
("12ds3".to_string(), File{
|
||||
payload: None,
|
||||
path: "var/file1".to_string(),
|
||||
group: "bin".to_string(),
|
||||
owner: "root".to_string(),
|
||||
mode: "0755".to_string(),
|
||||
preserve: false,
|
||||
overlay: false,
|
||||
original_name: "".to_string(),
|
||||
revert_tag: "".to_string(),
|
||||
sys_attr: "".to_string(),
|
||||
properties: vec![],
|
||||
facets: Default::default(),
|
||||
}),
|
||||
("654".to_string(), File{
|
||||
payload: None,
|
||||
path: "var/file1".to_string(),
|
||||
group: "bin".to_string(),
|
||||
owner: "root".to_string(),
|
||||
mode: "0755".to_string(),
|
||||
preserve: false,
|
||||
overlay: false,
|
||||
original_name: "".to_string(),
|
||||
revert_tag: "".to_string(),
|
||||
sys_attr: "".to_string(),
|
||||
properties: vec![],
|
||||
facets: Default::default(),
|
||||
})
|
||||
])};
|
||||
|
||||
let new_set = Manifest{files: HashMap::from([
|
||||
("0dh5".to_string(), File{
|
||||
payload: None,
|
||||
path: "var/file".to_string(),
|
||||
group: "bin".to_string(),
|
||||
owner: "root".to_string(),
|
||||
mode: "0755".to_string(),
|
||||
preserve: false,
|
||||
overlay: false,
|
||||
original_name: "".to_string(),
|
||||
revert_tag: "".to_string(),
|
||||
sys_attr: "".to_string(),
|
||||
properties: vec![],
|
||||
facets: Default::default(),
|
||||
}),
|
||||
("654".to_string(), File{
|
||||
payload: None,
|
||||
path: "var/file1".to_string(),
|
||||
group: "bin".to_string(),
|
||||
owner: "root".to_string(),
|
||||
mode: "0755".to_string(),
|
||||
preserve: false,
|
||||
overlay: false,
|
||||
original_name: "".to_string(),
|
||||
revert_tag: "".to_string(),
|
||||
sys_attr: "".to_string(),
|
||||
properties: vec![],
|
||||
facets: Default::default(),
|
||||
})
|
||||
])};
|
||||
let d = base.diff(&new_set);
|
||||
println!("{:#?}", d);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ pub mod actions;
|
|||
pub mod digest;
|
||||
pub mod payload;
|
||||
pub mod image;
|
||||
pub mod repository;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
|
|||
609
libips/src/repository/file_backend.rs
Normal file
609
libips/src/repository/file_backend.rs
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of the
|
||||
// MPL was not distributed with this file, You can
|
||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use std::str::FromStr;
|
||||
use sha2::{Sha256, Digest as Sha2Digest};
|
||||
use std::fs::File;
|
||||
|
||||
use crate::actions::{Manifest, File as FileAction};
|
||||
use crate::digest::Digest;
|
||||
use crate::payload::{Payload, PayloadCompressionAlgorithm};
|
||||
|
||||
use super::{Repository, RepositoryConfig, RepositoryVersion, REPOSITORY_CONFIG_FILENAME};
|
||||
|
||||
/// Repository implementation that uses the local filesystem
|
||||
pub struct FileBackend {
|
||||
pub path: PathBuf,
|
||||
pub config: RepositoryConfig,
|
||||
}
|
||||
|
||||
/// Transaction for publishing packages
|
||||
pub struct Transaction {
|
||||
/// Unique ID for the transaction
|
||||
id: String,
|
||||
/// Path to the transaction directory
|
||||
path: PathBuf,
|
||||
/// Manifest being updated
|
||||
manifest: Manifest,
|
||||
/// Files to be published
|
||||
files: Vec<(PathBuf, String)>, // (source_path, sha256)
|
||||
/// Repository reference
|
||||
repo: PathBuf,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
/// Create a new transaction
|
||||
pub fn new(repo_path: PathBuf) -> Result<Self> {
|
||||
// Generate a unique ID based on timestamp
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
let id = format!("trans_{}", timestamp);
|
||||
|
||||
// Create transaction directory
|
||||
let trans_path = repo_path.join("trans").join(&id);
|
||||
fs::create_dir_all(&trans_path)?;
|
||||
|
||||
Ok(Transaction {
|
||||
id,
|
||||
path: trans_path,
|
||||
manifest: Manifest::new(),
|
||||
files: Vec::new(),
|
||||
repo: repo_path,
|
||||
})
|
||||
}
|
||||
|
||||
/// Process a file for the transaction
|
||||
///
|
||||
/// Takes a FileAction and a path to a file in a prototype directory.
|
||||
/// Calculates the file's checksum, copies and "compresses" the content into a temp file
|
||||
/// in the transactions directory, and updates the FileAction with the hash information.
|
||||
pub fn add_file(&mut self, file_action: FileAction, file_path: &Path) -> Result<()> {
|
||||
// Calculate SHA256 hash of the file (uncompressed)
|
||||
let hash = Self::calculate_file_hash(file_path)?;
|
||||
|
||||
// Create a temp file path in the transactions directory
|
||||
let temp_file_name = format!("temp_{}", hash);
|
||||
let temp_file_path = self.path.join(temp_file_name);
|
||||
|
||||
// Copy the file to the temp location (this is a placeholder for compression)
|
||||
// In a real implementation, we would compress the file here
|
||||
fs::copy(file_path, &temp_file_path)?;
|
||||
|
||||
// For now, we're using the same hash for both uncompressed and "compressed" versions
|
||||
// In a real implementation with compression, we would calculate the hash of the compressed file
|
||||
let compressed_hash = hash.clone();
|
||||
|
||||
// Add file to the list for later processing during commit
|
||||
self.files.push((file_path.to_path_buf(), hash.clone()));
|
||||
|
||||
// Create a new FileAction with the updated information if one wasn't provided
|
||||
let mut updated_file_action = file_action;
|
||||
|
||||
// Create a payload with the hash information if it doesn't exist
|
||||
let mut payload = updated_file_action.payload.unwrap_or_else(Payload::default);
|
||||
|
||||
// Set the primary identifier (uncompressed hash)
|
||||
payload.primary_identifier = Digest::from_str(&hash)?;
|
||||
|
||||
// Set the compression algorithm
|
||||
payload.compression_algorithm = PayloadCompressionAlgorithm::Gzip;
|
||||
|
||||
// Add the compressed hash as an additional identifier
|
||||
let compressed_digest = Digest::from_str(&compressed_hash)?;
|
||||
payload.additional_identifiers.push(compressed_digest);
|
||||
|
||||
// Update the FileAction with the payload
|
||||
updated_file_action.payload = Some(payload);
|
||||
|
||||
// Add the FileAction to the manifest
|
||||
self.manifest.add_file(updated_file_action);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Commit the transaction
|
||||
pub fn commit(self) -> Result<()> {
|
||||
// Save the manifest to the transaction directory
|
||||
let manifest_path = self.path.join("manifest");
|
||||
|
||||
// Serialize the manifest to JSON
|
||||
let manifest_json = serde_json::to_string_pretty(&self.manifest)?;
|
||||
fs::write(&manifest_path, manifest_json)?;
|
||||
|
||||
// Copy files to their final location
|
||||
for (source_path, hash) in self.files {
|
||||
// Create the destination path in the files directory
|
||||
let dest_path = self.repo.join("file").join(&hash);
|
||||
|
||||
// Copy the file if it doesn't already exist
|
||||
if !dest_path.exists() {
|
||||
fs::copy(source_path, &dest_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a timestamp for the manifest version
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
// Move the manifest to its final location in the repository
|
||||
// Store in both the pkg directory and the trans directory as required
|
||||
let pkg_manifest_path = self.repo.join("pkg").join("manifest");
|
||||
let trans_manifest_path = self.repo.join("trans").join(format!("manifest_{}", timestamp));
|
||||
|
||||
// Copy to pkg directory
|
||||
fs::copy(&manifest_path, &pkg_manifest_path)?;
|
||||
|
||||
// Move to trans directory
|
||||
fs::rename(manifest_path, trans_manifest_path)?;
|
||||
|
||||
// Clean up the transaction directory (except for the manifest which was moved)
|
||||
fs::remove_dir_all(self.path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Calculate SHA256 hash of a file
|
||||
fn calculate_file_hash(file_path: &Path) -> Result<String> {
|
||||
// Open the file
|
||||
let mut file = File::open(file_path)?;
|
||||
|
||||
// Create a SHA256 hasher
|
||||
let mut hasher = Sha256::new();
|
||||
|
||||
// Read the file in chunks and update the hasher
|
||||
let mut buffer = [0; 1024];
|
||||
loop {
|
||||
let bytes_read = file.read(&mut buffer)?;
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
hasher.update(&buffer[..bytes_read]);
|
||||
}
|
||||
|
||||
// Get the hash result
|
||||
let hash = hasher.finalize();
|
||||
|
||||
// Convert to hex string
|
||||
let hash_str = format!("{:x}", hash);
|
||||
|
||||
Ok(hash_str)
|
||||
}
|
||||
}
|
||||
|
||||
impl Repository for FileBackend {
|
||||
/// Create a new repository at the specified path
|
||||
fn create<P: AsRef<Path>>(path: P, version: RepositoryVersion) -> Result<Self> {
|
||||
let path = path.as_ref();
|
||||
|
||||
// Create the repository directory if it doesn't exist
|
||||
fs::create_dir_all(path)?;
|
||||
|
||||
// Create the repository configuration
|
||||
let config = RepositoryConfig {
|
||||
version,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Create the repository structure
|
||||
let repo = FileBackend {
|
||||
path: path.to_path_buf(),
|
||||
config,
|
||||
};
|
||||
|
||||
// Create the repository directories
|
||||
repo.create_directories()?;
|
||||
|
||||
// Save the repository configuration
|
||||
repo.save_config()?;
|
||||
|
||||
Ok(repo)
|
||||
}
|
||||
|
||||
/// Open an existing repository
|
||||
fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||
let path = path.as_ref();
|
||||
|
||||
// Check if the repository directory exists
|
||||
if !path.exists() {
|
||||
return Err(anyhow!("Repository does not exist: {}", path.display()));
|
||||
}
|
||||
|
||||
// Load the repository configuration
|
||||
let config_path = path.join(REPOSITORY_CONFIG_FILENAME);
|
||||
let config_data = fs::read_to_string(config_path)?;
|
||||
let config: RepositoryConfig = serde_json::from_str(&config_data)?;
|
||||
|
||||
Ok(FileBackend {
|
||||
path: path.to_path_buf(),
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
/// Save the repository configuration
|
||||
fn save_config(&self) -> Result<()> {
|
||||
let config_path = self.path.join(REPOSITORY_CONFIG_FILENAME);
|
||||
let config_data = serde_json::to_string_pretty(&self.config)?;
|
||||
fs::write(config_path, config_data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a publisher to the repository
|
||||
fn add_publisher(&mut self, publisher: &str) -> Result<()> {
|
||||
if !self.config.publishers.contains(&publisher.to_string()) {
|
||||
self.config.publishers.push(publisher.to_string());
|
||||
|
||||
// Create publisher-specific directories
|
||||
fs::create_dir_all(self.path.join("catalog").join(publisher))?;
|
||||
fs::create_dir_all(self.path.join("pkg").join(publisher))?;
|
||||
|
||||
// Save the updated configuration
|
||||
self.save_config()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a publisher from the repository
|
||||
fn remove_publisher(&mut self, publisher: &str, dry_run: bool) -> Result<()> {
|
||||
if let Some(pos) = self.config.publishers.iter().position(|p| p == publisher) {
|
||||
if !dry_run {
|
||||
self.config.publishers.remove(pos);
|
||||
|
||||
// Save the updated configuration
|
||||
self.save_config()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get repository information
|
||||
fn get_info(&self) -> Result<Vec<(String, usize, String, String)>> {
|
||||
let mut info = Vec::new();
|
||||
|
||||
for publisher in &self.config.publishers {
|
||||
// Count packages (this is a placeholder, actual implementation would count packages)
|
||||
let package_count = 0;
|
||||
|
||||
// Status is always "online" for now
|
||||
let status = "online".to_string();
|
||||
|
||||
// Updated timestamp (placeholder)
|
||||
let updated = "2025-07-21T18:46:00.000000Z".to_string();
|
||||
|
||||
info.push((publisher.clone(), package_count, status, updated));
|
||||
}
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
/// Set a repository property
|
||||
fn set_property(&mut self, property: &str, value: &str) -> Result<()> {
|
||||
self.config.properties.insert(property.to_string(), value.to_string());
|
||||
self.save_config()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set a publisher property
|
||||
fn set_publisher_property(&mut self, publisher: &str, property: &str, value: &str) -> Result<()> {
|
||||
// Check if the publisher exists
|
||||
if !self.config.publishers.contains(&publisher.to_string()) {
|
||||
return Err(anyhow!("Publisher does not exist: {}", publisher));
|
||||
}
|
||||
|
||||
// Create the property key in the format "publisher/property"
|
||||
let key = format!("{}/{}", publisher, property);
|
||||
|
||||
// Set the property
|
||||
self.config.properties.insert(key, value.to_string());
|
||||
|
||||
// Save the updated configuration
|
||||
self.save_config()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List packages in the repository
|
||||
fn list_packages(&self, publisher: Option<&str>, pattern: Option<&str>) -> Result<Vec<(String, String, String)>> {
|
||||
let mut packages = Vec::new();
|
||||
|
||||
// Filter publishers if specified
|
||||
let publishers = if let Some(pub_name) = publisher {
|
||||
if !self.config.publishers.contains(&pub_name.to_string()) {
|
||||
return Err(anyhow!("Publisher does not exist: {}", pub_name));
|
||||
}
|
||||
vec![pub_name.to_string()]
|
||||
} else {
|
||||
self.config.publishers.clone()
|
||||
};
|
||||
|
||||
// For each publisher, list packages
|
||||
for pub_name in publishers {
|
||||
// In a real implementation, we would scan the repository for packages
|
||||
// For now, we'll just return a placeholder
|
||||
|
||||
// Example package data (name, version, publisher)
|
||||
let example_packages = vec![
|
||||
("example/package1".to_string(), "1.0.0".to_string(), pub_name.clone()),
|
||||
("example/package2".to_string(), "2.0.0".to_string(), pub_name.clone()),
|
||||
];
|
||||
|
||||
// Filter by pattern if specified
|
||||
let filtered_packages = if let Some(pat) = pattern {
|
||||
example_packages.into_iter()
|
||||
.filter(|(name, _, _)| name.contains(pat))
|
||||
.collect()
|
||||
} else {
|
||||
example_packages
|
||||
};
|
||||
|
||||
packages.extend(filtered_packages);
|
||||
}
|
||||
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
/// Show the contents of packages
|
||||
fn show_contents(&self, publisher: Option<&str>, pattern: Option<&str>, action_types: Option<&[String]>) -> Result<Vec<(String, String, String)>> {
|
||||
// This is a placeholder implementation
|
||||
// In a real implementation, we would parse package manifests and extract contents
|
||||
|
||||
// Get the list of packages
|
||||
let packages = self.list_packages(publisher, pattern)?;
|
||||
|
||||
// For each package, list contents
|
||||
let mut contents = Vec::new();
|
||||
|
||||
for (pkg_name, pkg_version, pub_name) in packages {
|
||||
// Example content data (package, path, type)
|
||||
let example_contents = vec![
|
||||
(format!("{}@{}", pkg_name, pkg_version), "/usr/bin/example".to_string(), "file".to_string()),
|
||||
(format!("{}@{}", pkg_name, pkg_version), "/usr/share/doc/example".to_string(), "dir".to_string()),
|
||||
];
|
||||
|
||||
// Filter by action type if specified
|
||||
let filtered_contents = if let Some(types) = action_types {
|
||||
example_contents.into_iter()
|
||||
.filter(|(_, _, action_type)| types.contains(&action_type))
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
example_contents
|
||||
};
|
||||
|
||||
contents.extend(filtered_contents);
|
||||
}
|
||||
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
/// Rebuild repository metadata
|
||||
fn rebuild(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()> {
|
||||
// This is a placeholder implementation
|
||||
// In a real implementation, we would rebuild catalogs and search indexes
|
||||
|
||||
// Filter publishers if specified
|
||||
let publishers = if let Some(pub_name) = publisher {
|
||||
if !self.config.publishers.contains(&pub_name.to_string()) {
|
||||
return Err(anyhow!("Publisher does not exist: {}", pub_name));
|
||||
}
|
||||
vec![pub_name.to_string()]
|
||||
} else {
|
||||
self.config.publishers.clone()
|
||||
};
|
||||
|
||||
// For each publisher, rebuild metadata
|
||||
for pub_name in publishers {
|
||||
println!("Rebuilding metadata for publisher: {}", pub_name);
|
||||
|
||||
if !no_catalog {
|
||||
println!("Rebuilding catalog...");
|
||||
// In a real implementation, we would rebuild the catalog
|
||||
}
|
||||
|
||||
if !no_index {
|
||||
println!("Rebuilding search index...");
|
||||
// In a real implementation, we would rebuild the search index
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Refresh repository metadata
|
||||
fn refresh(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()> {
|
||||
// This is a placeholder implementation
|
||||
// In a real implementation, we would refresh catalogs and search indexes
|
||||
|
||||
// Filter publishers if specified
|
||||
let publishers = if let Some(pub_name) = publisher {
|
||||
if !self.config.publishers.contains(&pub_name.to_string()) {
|
||||
return Err(anyhow!("Publisher does not exist: {}", pub_name));
|
||||
}
|
||||
vec![pub_name.to_string()]
|
||||
} else {
|
||||
self.config.publishers.clone()
|
||||
};
|
||||
|
||||
// For each publisher, refresh metadata
|
||||
for pub_name in publishers {
|
||||
println!("Refreshing metadata for publisher: {}", pub_name);
|
||||
|
||||
if !no_catalog {
|
||||
println!("Refreshing catalog...");
|
||||
// In a real implementation, we would refresh the catalog
|
||||
}
|
||||
|
||||
if !no_index {
|
||||
println!("Refreshing search index...");
|
||||
// In a real implementation, we would refresh the search index
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileBackend {
|
||||
/// Create the repository directories
|
||||
fn create_directories(&self) -> Result<()> {
|
||||
// Create the main repository directories
|
||||
fs::create_dir_all(self.path.join("catalog"))?;
|
||||
fs::create_dir_all(self.path.join("file"))?;
|
||||
fs::create_dir_all(self.path.join("index"))?;
|
||||
fs::create_dir_all(self.path.join("pkg"))?;
|
||||
fs::create_dir_all(self.path.join("trans"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_publish_files(&mut self, test_dir: &Path) -> Result<()> {
|
||||
println!("Testing file publishing...");
|
||||
|
||||
// Create a test publisher
|
||||
self.add_publisher("test")?;
|
||||
|
||||
// Create a nested directory structure
|
||||
let nested_dir = test_dir.join("nested").join("dir");
|
||||
fs::create_dir_all(&nested_dir)?;
|
||||
|
||||
// Create a test file in the nested directory
|
||||
let test_file_path = nested_dir.join("test_file.txt");
|
||||
fs::write(&test_file_path, "This is a test file")?;
|
||||
|
||||
// Begin a transaction
|
||||
let mut transaction = self.begin_transaction()?;
|
||||
|
||||
// Create a FileAction from the test file path
|
||||
let mut file_action = FileAction::read_from_path(&test_file_path)?;
|
||||
|
||||
// Calculate the relative path from the test file path to the base directory
|
||||
let relative_path = test_file_path.strip_prefix(test_dir)?.to_string_lossy().to_string();
|
||||
|
||||
// Set the relative path in the FileAction
|
||||
file_action.path = relative_path;
|
||||
|
||||
// Add the test file to the transaction
|
||||
transaction.add_file(file_action, &test_file_path)?;
|
||||
|
||||
// Verify that the path in the FileAction is the relative path
|
||||
// The path should be "nested/dir/test_file.txt", not the full path
|
||||
let expected_path = "nested/dir/test_file.txt";
|
||||
let actual_path = &transaction.manifest.files[0].path;
|
||||
|
||||
if actual_path != expected_path {
|
||||
return Err(anyhow!("Path in FileAction is incorrect. Expected: {}, Actual: {}",
|
||||
expected_path, actual_path));
|
||||
}
|
||||
|
||||
// Commit the transaction
|
||||
transaction.commit()?;
|
||||
|
||||
// Verify the file was stored
|
||||
let hash = Transaction::calculate_file_hash(&test_file_path)?;
|
||||
let stored_file_path = self.path.join("file").join(&hash);
|
||||
|
||||
if !stored_file_path.exists() {
|
||||
return Err(anyhow!("File was not stored correctly"));
|
||||
}
|
||||
|
||||
// Verify the manifest was updated
|
||||
let manifest_path = self.path.join("pkg").join("manifest");
|
||||
|
||||
if !manifest_path.exists() {
|
||||
return Err(anyhow!("Manifest was not created"));
|
||||
}
|
||||
|
||||
println!("File publishing test passed!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Begin a new transaction for publishing
|
||||
pub fn begin_transaction(&self) -> Result<Transaction> {
|
||||
Transaction::new(self.path.clone())
|
||||
}
|
||||
|
||||
/// Publish files from a prototype directory
|
||||
pub fn publish_files<P: AsRef<Path>>(&self, proto_dir: P, publisher: &str) -> Result<()> {
|
||||
let proto_dir = proto_dir.as_ref();
|
||||
|
||||
// Check if the prototype directory exists
|
||||
if !proto_dir.exists() {
|
||||
return Err(anyhow!("Prototype directory does not exist: {}", proto_dir.display()));
|
||||
}
|
||||
|
||||
// Check if the publisher exists
|
||||
if !self.config.publishers.contains(&publisher.to_string()) {
|
||||
return Err(anyhow!("Publisher does not exist: {}", publisher));
|
||||
}
|
||||
|
||||
// Begin a transaction
|
||||
let mut transaction = self.begin_transaction()?;
|
||||
|
||||
// Walk the prototype directory and add files to the transaction
|
||||
self.add_files_to_transaction(&mut transaction, proto_dir, proto_dir)?;
|
||||
|
||||
// Commit the transaction
|
||||
transaction.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add files from a directory to a transaction
|
||||
fn add_files_to_transaction(&self, transaction: &mut Transaction, base_dir: &Path, dir: &Path) -> Result<()> {
|
||||
// Read the directory entries
|
||||
for entry in fs::read_dir(dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
if path.is_dir() {
|
||||
// Recursively add files from subdirectories
|
||||
self.add_files_to_transaction(transaction, base_dir, &path)?;
|
||||
} else {
|
||||
// Create a FileAction from the file path
|
||||
let mut file_action = FileAction::read_from_path(&path)?;
|
||||
|
||||
// Calculate the relative path from the file path to the base directory
|
||||
let relative_path = path.strip_prefix(base_dir)?.to_string_lossy().to_string();
|
||||
|
||||
// Set the relative path in the FileAction
|
||||
file_action.path = relative_path;
|
||||
|
||||
// Add the file to the transaction
|
||||
transaction.add_file(file_action, &path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Store a file in the repository
|
||||
pub fn store_file<P: AsRef<Path>>(&self, file_path: P) -> Result<String> {
|
||||
let file_path = file_path.as_ref();
|
||||
|
||||
// Calculate the SHA256 hash of the file
|
||||
let hash = Transaction::calculate_file_hash(file_path)?;
|
||||
|
||||
// Create the destination path in the files directory
|
||||
let dest_path = self.path.join("file").join(&hash);
|
||||
|
||||
// Copy the file if it doesn't already exist
|
||||
if !dest_path.exists() {
|
||||
fs::copy(file_path, &dest_path)?;
|
||||
}
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
}
|
||||
97
libips/src/repository/mod.rs
Normal file
97
libips/src/repository/mod.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of the
|
||||
// MPL was not distributed with this file, You can
|
||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
use anyhow::Result;
|
||||
use std::path::Path;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod file_backend;
|
||||
mod rest_backend;
|
||||
|
||||
pub use file_backend::FileBackend;
|
||||
pub use rest_backend::RestBackend;
|
||||
|
||||
/// Repository configuration filename
|
||||
pub const REPOSITORY_CONFIG_FILENAME: &str = "pkg6.repository";
|
||||
|
||||
/// Repository version
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum RepositoryVersion {
|
||||
V4 = 4,
|
||||
}
|
||||
|
||||
impl Default for RepositoryVersion {
|
||||
fn default() -> Self {
|
||||
RepositoryVersion::V4
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u32> for RepositoryVersion {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
4 => Ok(RepositoryVersion::V4),
|
||||
_ => Err(anyhow::anyhow!("Unsupported repository version: {}", value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Repository configuration
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RepositoryConfig {
|
||||
pub version: RepositoryVersion,
|
||||
pub publishers: Vec<String>,
|
||||
pub properties: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Default for RepositoryConfig {
|
||||
fn default() -> Self {
|
||||
RepositoryConfig {
|
||||
version: RepositoryVersion::default(),
|
||||
publishers: Vec::new(),
|
||||
properties: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Repository trait defining the interface for all repository backends
|
||||
pub trait Repository {
|
||||
/// Create a new repository at the specified path
|
||||
fn create<P: AsRef<Path>>(path: P, version: RepositoryVersion) -> Result<Self> where Self: Sized;
|
||||
|
||||
/// Open an existing repository
|
||||
fn open<P: AsRef<Path>>(path: P) -> Result<Self> where Self: Sized;
|
||||
|
||||
/// Save the repository configuration
|
||||
fn save_config(&self) -> Result<()>;
|
||||
|
||||
/// Add a publisher to the repository
|
||||
fn add_publisher(&mut self, publisher: &str) -> Result<()>;
|
||||
|
||||
/// Remove a publisher from the repository
|
||||
fn remove_publisher(&mut self, publisher: &str, dry_run: bool) -> Result<()>;
|
||||
|
||||
/// Get repository information
|
||||
fn get_info(&self) -> Result<Vec<(String, usize, String, String)>>;
|
||||
|
||||
/// Set a repository property
|
||||
fn set_property(&mut self, property: &str, value: &str) -> Result<()>;
|
||||
|
||||
/// Set a publisher property
|
||||
fn set_publisher_property(&mut self, publisher: &str, property: &str, value: &str) -> Result<()>;
|
||||
|
||||
/// List packages in the repository
|
||||
fn list_packages(&self, publisher: Option<&str>, pattern: Option<&str>) -> Result<Vec<(String, String, String)>>;
|
||||
|
||||
/// Show contents of packages
|
||||
fn show_contents(&self, publisher: Option<&str>, pattern: Option<&str>, action_types: Option<&[String]>) -> Result<Vec<(String, String, String)>>;
|
||||
|
||||
/// Rebuild repository metadata
|
||||
fn rebuild(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()>;
|
||||
|
||||
/// Refresh repository metadata
|
||||
fn refresh(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()>;
|
||||
}
|
||||
310
libips/src/repository/rest_backend.rs
Normal file
310
libips/src/repository/rest_backend.rs
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of the
|
||||
// MPL was not distributed with this file, You can
|
||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{Repository, RepositoryConfig, RepositoryVersion};
|
||||
|
||||
/// Repository implementation that uses a REST API
|
||||
pub struct RestBackend {
|
||||
pub uri: String,
|
||||
pub config: RepositoryConfig,
|
||||
pub local_cache_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Repository for RestBackend {
|
||||
/// Create a new repository at the specified URI
|
||||
fn create<P: AsRef<Path>>(uri: P, version: RepositoryVersion) -> Result<Self> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to create the repository
|
||||
|
||||
let uri_str = uri.as_ref().to_string_lossy().to_string();
|
||||
|
||||
// Create the repository configuration
|
||||
let config = RepositoryConfig {
|
||||
version,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Create the repository structure
|
||||
let repo = RestBackend {
|
||||
uri: uri_str,
|
||||
config,
|
||||
local_cache_path: None,
|
||||
};
|
||||
|
||||
// In a real implementation, we would make a REST API call to create the repository structure
|
||||
|
||||
Ok(repo)
|
||||
}
|
||||
|
||||
/// Open an existing repository
|
||||
fn open<P: AsRef<Path>>(uri: P) -> Result<Self> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to get the repository configuration
|
||||
|
||||
let uri_str = uri.as_ref().to_string_lossy().to_string();
|
||||
|
||||
// In a real implementation, we would fetch the repository configuration from the REST API
|
||||
// For now, we'll just create a default configuration
|
||||
let config = RepositoryConfig::default();
|
||||
|
||||
Ok(RestBackend {
|
||||
uri: uri_str,
|
||||
config,
|
||||
local_cache_path: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Save the repository configuration
|
||||
fn save_config(&self) -> Result<()> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to save the repository configuration
|
||||
|
||||
// For now, just return Ok
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a publisher to the repository
|
||||
fn add_publisher(&mut self, publisher: &str) -> Result<()> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to add the publisher
|
||||
|
||||
if !self.config.publishers.contains(&publisher.to_string()) {
|
||||
self.config.publishers.push(publisher.to_string());
|
||||
|
||||
// In a real implementation, we would make a REST API call to create publisher-specific resources
|
||||
|
||||
// Save the updated configuration
|
||||
self.save_config()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a publisher from the repository
|
||||
fn remove_publisher(&mut self, publisher: &str, dry_run: bool) -> Result<()> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to remove the publisher
|
||||
|
||||
if let Some(pos) = self.config.publishers.iter().position(|p| p == publisher) {
|
||||
if !dry_run {
|
||||
self.config.publishers.remove(pos);
|
||||
|
||||
// In a real implementation, we would make a REST API call to remove publisher-specific resources
|
||||
|
||||
// Save the updated configuration
|
||||
self.save_config()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get repository information
|
||||
fn get_info(&self) -> Result<Vec<(String, usize, String, String)>> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to get repository information
|
||||
|
||||
let mut info = Vec::new();
|
||||
|
||||
for publisher in &self.config.publishers {
|
||||
// In a real implementation, we would get this information from the REST API
|
||||
let package_count = 0;
|
||||
let status = "online".to_string();
|
||||
let updated = "2025-07-21T18:46:00.000000Z".to_string();
|
||||
|
||||
info.push((publisher.clone(), package_count, status, updated));
|
||||
}
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
/// Set a repository property
|
||||
fn set_property(&mut self, property: &str, value: &str) -> Result<()> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to set the property
|
||||
|
||||
self.config.properties.insert(property.to_string(), value.to_string());
|
||||
self.save_config()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set a publisher property
|
||||
fn set_publisher_property(&mut self, publisher: &str, property: &str, value: &str) -> Result<()> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to set the publisher property
|
||||
|
||||
// Check if the publisher exists
|
||||
if !self.config.publishers.contains(&publisher.to_string()) {
|
||||
return Err(anyhow!("Publisher does not exist: {}", publisher));
|
||||
}
|
||||
|
||||
// Create the property key in the format "publisher/property"
|
||||
let key = format!("{}/{}", publisher, property);
|
||||
|
||||
// Set the property
|
||||
self.config.properties.insert(key, value.to_string());
|
||||
|
||||
// Save the updated configuration
|
||||
self.save_config()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List packages in the repository
|
||||
fn list_packages(&self, publisher: Option<&str>, pattern: Option<&str>) -> Result<Vec<(String, String, String)>> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to list packages
|
||||
|
||||
let mut packages = Vec::new();
|
||||
|
||||
// Filter publishers if specified
|
||||
let publishers = if let Some(pub_name) = publisher {
|
||||
if !self.config.publishers.contains(&pub_name.to_string()) {
|
||||
return Err(anyhow!("Publisher does not exist: {}", pub_name));
|
||||
}
|
||||
vec![pub_name.to_string()]
|
||||
} else {
|
||||
self.config.publishers.clone()
|
||||
};
|
||||
|
||||
// For each publisher, list packages
|
||||
for pub_name in publishers {
|
||||
// In a real implementation, we would get this information from the REST API
|
||||
|
||||
// Example package data (name, version, publisher)
|
||||
let example_packages = vec![
|
||||
("example/package1".to_string(), "1.0.0".to_string(), pub_name.clone()),
|
||||
("example/package2".to_string(), "2.0.0".to_string(), pub_name.clone()),
|
||||
];
|
||||
|
||||
// Filter by pattern if specified
|
||||
let filtered_packages = if let Some(pat) = pattern {
|
||||
example_packages.into_iter()
|
||||
.filter(|(name, _, _)| name.contains(pat))
|
||||
.collect()
|
||||
} else {
|
||||
example_packages
|
||||
};
|
||||
|
||||
packages.extend(filtered_packages);
|
||||
}
|
||||
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
/// Show contents of packages
|
||||
fn show_contents(&self, publisher: Option<&str>, pattern: Option<&str>, action_types: Option<&[String]>) -> Result<Vec<(String, String, String)>> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to get package contents
|
||||
|
||||
// Get the list of packages
|
||||
let packages = self.list_packages(publisher, pattern)?;
|
||||
|
||||
// For each package, list contents
|
||||
let mut contents = Vec::new();
|
||||
|
||||
for (pkg_name, pkg_version, _) in packages {
|
||||
// In a real implementation, we would get this information from the REST API
|
||||
|
||||
// Example content data (package, path, type)
|
||||
let example_contents = vec![
|
||||
(format!("{}@{}", pkg_name, pkg_version), "/usr/bin/example".to_string(), "file".to_string()),
|
||||
(format!("{}@{}", pkg_name, pkg_version), "/usr/share/doc/example".to_string(), "dir".to_string()),
|
||||
];
|
||||
|
||||
// Filter by action type if specified
|
||||
let filtered_contents = if let Some(types) = action_types {
|
||||
example_contents.into_iter()
|
||||
.filter(|(_, _, action_type)| types.contains(&action_type))
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
example_contents
|
||||
};
|
||||
|
||||
contents.extend(filtered_contents);
|
||||
}
|
||||
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
/// Rebuild repository metadata
|
||||
fn rebuild(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to rebuild metadata
|
||||
|
||||
// Filter publishers if specified
|
||||
let publishers = if let Some(pub_name) = publisher {
|
||||
if !self.config.publishers.contains(&pub_name.to_string()) {
|
||||
return Err(anyhow!("Publisher does not exist: {}", pub_name));
|
||||
}
|
||||
vec![pub_name.to_string()]
|
||||
} else {
|
||||
self.config.publishers.clone()
|
||||
};
|
||||
|
||||
// For each publisher, rebuild metadata
|
||||
for pub_name in publishers {
|
||||
println!("Rebuilding metadata for publisher: {}", pub_name);
|
||||
|
||||
if !no_catalog {
|
||||
println!("Rebuilding catalog...");
|
||||
// In a real implementation, we would make a REST API call to rebuild the catalog
|
||||
}
|
||||
|
||||
if !no_index {
|
||||
println!("Rebuilding search index...");
|
||||
// In a real implementation, we would make a REST API call to rebuild the search index
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Refresh repository metadata
|
||||
fn refresh(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()> {
|
||||
// This is a stub implementation
|
||||
// In a real implementation, we would make a REST API call to refresh metadata
|
||||
|
||||
// Filter publishers if specified
|
||||
let publishers = if let Some(pub_name) = publisher {
|
||||
if !self.config.publishers.contains(&pub_name.to_string()) {
|
||||
return Err(anyhow!("Publisher does not exist: {}", pub_name));
|
||||
}
|
||||
vec![pub_name.to_string()]
|
||||
} else {
|
||||
self.config.publishers.clone()
|
||||
};
|
||||
|
||||
// For each publisher, refresh metadata
|
||||
for pub_name in publishers {
|
||||
println!("Refreshing metadata for publisher: {}", pub_name);
|
||||
|
||||
if !no_catalog {
|
||||
println!("Refreshing catalog...");
|
||||
// In a real implementation, we would make a REST API call to refresh the catalog
|
||||
}
|
||||
|
||||
if !no_index {
|
||||
println!("Refreshing search index...");
|
||||
// In a real implementation, we would make a REST API call to refresh the search index
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl RestBackend {
|
||||
/// Set the local cache path
|
||||
pub fn set_local_cache_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
|
||||
self.local_cache_path = Some(path.as_ref().to_path_buf());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ version = "0.0.1-placeholder"
|
|||
authors = ["Till Wegmueller <till.wegmueller@openflowlabs.com>"]
|
||||
edition = "2018"
|
||||
license-file = "LICENSE"
|
||||
description = "The repository server for IPS written in rust"
|
||||
description = "The repository management utility for IPS written in rust"
|
||||
repository = "https://github.com/OpenFlowLabs/pkg6dev"
|
||||
readme = "README.md"
|
||||
keywords = ["packaging", "illumos"]
|
||||
|
|
@ -12,3 +12,11 @@ keywords = ["packaging", "illumos"]
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
anyhow = "1.0"
|
||||
libips = { path = "../libips" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.8"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,163 @@
|
|||
# IPS Packaging utility.
|
||||
# pkg6repo
|
||||
|
||||
Work in progress
|
||||
pkg6repo is a Rust implementation of the Image Packaging System (IPS) repository management utility. It is designed to replace the pkgrepo command from the original IPS implementation.
|
||||
|
||||
## Installation
|
||||
|
||||
To build and install pkg6repo, you need to have Rust and Cargo installed. Then, you can build the project using:
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
The binary will be available at `target/release/pkg6repo`.
|
||||
|
||||
## Usage
|
||||
|
||||
pkg6repo provides several subcommands for managing package repositories:
|
||||
|
||||
### Create a Repository
|
||||
|
||||
Create a new package repository:
|
||||
|
||||
```bash
|
||||
pkg6repo create /path/to/repository
|
||||
```
|
||||
|
||||
You can specify the repository version (default is 4):
|
||||
|
||||
```bash
|
||||
pkg6repo create --version 4 /path/to/repository
|
||||
```
|
||||
|
||||
### Add Publishers
|
||||
|
||||
Add publishers to a repository:
|
||||
|
||||
```bash
|
||||
pkg6repo add-publisher -s /path/to/repository example.com
|
||||
```
|
||||
|
||||
### Remove Publishers
|
||||
|
||||
Remove publishers from a repository:
|
||||
|
||||
```bash
|
||||
pkg6repo remove-publisher -s /path/to/repository example.com
|
||||
```
|
||||
|
||||
You can perform a dry run to see what would be removed without actually removing anything:
|
||||
|
||||
```bash
|
||||
pkg6repo remove-publisher -n -s /path/to/repository example.com
|
||||
```
|
||||
|
||||
### Get Repository Properties
|
||||
|
||||
Get repository properties:
|
||||
|
||||
```bash
|
||||
pkg6repo get -s /path/to/repository
|
||||
```
|
||||
|
||||
You can specify specific properties to get:
|
||||
|
||||
```bash
|
||||
pkg6repo get -s /path/to/repository publisher/prefix
|
||||
```
|
||||
|
||||
### Set Repository Properties
|
||||
|
||||
Set repository properties:
|
||||
|
||||
```bash
|
||||
pkg6repo set -s /path/to/repository publisher/prefix=example.com
|
||||
```
|
||||
|
||||
You can set publisher-specific properties:
|
||||
|
||||
```bash
|
||||
pkg6repo set -s /path/to/repository -p example.com repository/origins=http://example.com/repository
|
||||
```
|
||||
|
||||
### Display Repository Information
|
||||
|
||||
Display information about a repository:
|
||||
|
||||
```bash
|
||||
pkg6repo info -s /path/to/repository
|
||||
```
|
||||
|
||||
### List Packages
|
||||
|
||||
List packages in a repository:
|
||||
|
||||
```bash
|
||||
pkg6repo list -s /path/to/repository
|
||||
```
|
||||
|
||||
You can filter by publisher:
|
||||
|
||||
```bash
|
||||
pkg6repo list -s /path/to/repository -p example.com
|
||||
```
|
||||
|
||||
You can also filter by package pattern:
|
||||
|
||||
```bash
|
||||
pkg6repo list -s /path/to/repository example/package
|
||||
```
|
||||
|
||||
### Show Package Contents
|
||||
|
||||
Show contents of packages in a repository:
|
||||
|
||||
```bash
|
||||
pkg6repo contents -s /path/to/repository
|
||||
```
|
||||
|
||||
You can filter by package pattern:
|
||||
|
||||
```bash
|
||||
pkg6repo contents -s /path/to/repository example/package
|
||||
```
|
||||
|
||||
You can also filter by action type:
|
||||
|
||||
```bash
|
||||
pkg6repo contents -s /path/to/repository -t file
|
||||
```
|
||||
|
||||
### Rebuild Repository Metadata
|
||||
|
||||
Rebuild repository metadata:
|
||||
|
||||
```bash
|
||||
pkg6repo rebuild -s /path/to/repository
|
||||
```
|
||||
|
||||
You can skip catalog or index rebuilding:
|
||||
|
||||
```bash
|
||||
pkg6repo rebuild -s /path/to/repository --no-catalog
|
||||
pkg6repo rebuild -s /path/to/repository --no-index
|
||||
```
|
||||
|
||||
### Refresh Repository Metadata
|
||||
|
||||
Refresh repository metadata:
|
||||
|
||||
```bash
|
||||
pkg6repo refresh -s /path/to/repository
|
||||
```
|
||||
|
||||
You can skip catalog or index refreshing:
|
||||
|
||||
```bash
|
||||
pkg6repo refresh -s /path/to/repository --no-catalog
|
||||
pkg6repo refresh -s /path/to/repository --no-index
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the same license as the original IPS implementation.
|
||||
|
|
@ -1,3 +1,492 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use clap::{Parser, Subcommand};
|
||||
use anyhow::{Result, anyhow};
|
||||
use std::path::PathBuf;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use libips::repository::{Repository, RepositoryVersion, FileBackend};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// pkg6repo - Image Packaging System repository management utility
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
#[clap(propagate_version = true)]
|
||||
struct App {
|
||||
#[clap(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Commands {
|
||||
/// Create a new package repository
|
||||
Create {
|
||||
/// Version of the repository to create
|
||||
#[clap(long, default_value = "4")]
|
||||
version: u32,
|
||||
|
||||
/// Path or URI of the repository to create
|
||||
uri_or_path: String,
|
||||
},
|
||||
|
||||
/// Add publishers to a repository
|
||||
AddPublisher {
|
||||
/// Path or URI of the repository
|
||||
#[clap(short = 's')]
|
||||
repo_uri_or_path: String,
|
||||
|
||||
/// Publishers to add
|
||||
publisher: Vec<String>,
|
||||
},
|
||||
|
||||
/// Remove publishers from a repository
|
||||
RemovePublisher {
|
||||
/// Path or URI of the repository
|
||||
#[clap(short = 's')]
|
||||
repo_uri_or_path: String,
|
||||
|
||||
/// Perform a dry run
|
||||
#[clap(short = 'n')]
|
||||
dry_run: bool,
|
||||
|
||||
/// Wait for operation to complete
|
||||
#[clap(long)]
|
||||
synchronous: bool,
|
||||
|
||||
/// Publishers to remove
|
||||
publisher: Vec<String>,
|
||||
},
|
||||
|
||||
/// Get repository properties
|
||||
Get {
|
||||
/// Path or URI of the repository
|
||||
#[clap(short = 's')]
|
||||
repo_uri_or_path: String,
|
||||
|
||||
/// Output format
|
||||
#[clap(short = 'F')]
|
||||
format: Option<String>,
|
||||
|
||||
/// Omit headers
|
||||
#[clap(short = 'H')]
|
||||
omit_headers: bool,
|
||||
|
||||
/// Publisher to get properties for
|
||||
#[clap(short = 'p')]
|
||||
publisher: Option<Vec<String>>,
|
||||
|
||||
/// SSL key file
|
||||
#[clap(long)]
|
||||
key: Option<PathBuf>,
|
||||
|
||||
/// SSL certificate file
|
||||
#[clap(long)]
|
||||
cert: Option<PathBuf>,
|
||||
|
||||
/// Properties to get (section/property)
|
||||
section_property: Option<Vec<String>>,
|
||||
},
|
||||
|
||||
/// Display repository information
|
||||
Info {
|
||||
/// Path or URI of the repository
|
||||
#[clap(short = 's')]
|
||||
repo_uri_or_path: String,
|
||||
|
||||
/// Output format
|
||||
#[clap(short = 'F')]
|
||||
format: Option<String>,
|
||||
|
||||
/// Omit headers
|
||||
#[clap(short = 'H')]
|
||||
omit_headers: bool,
|
||||
|
||||
/// Publisher to get information for
|
||||
#[clap(short = 'p')]
|
||||
publisher: Option<Vec<String>>,
|
||||
|
||||
/// SSL key file
|
||||
#[clap(long)]
|
||||
key: Option<PathBuf>,
|
||||
|
||||
/// SSL certificate file
|
||||
#[clap(long)]
|
||||
cert: Option<PathBuf>,
|
||||
},
|
||||
|
||||
/// List packages in a repository
|
||||
List {
|
||||
/// Path or URI of the repository
|
||||
#[clap(short = 's')]
|
||||
repo_uri_or_path: String,
|
||||
|
||||
/// Output format
|
||||
#[clap(short = 'F')]
|
||||
format: Option<String>,
|
||||
|
||||
/// Omit headers
|
||||
#[clap(short = 'H')]
|
||||
omit_headers: bool,
|
||||
|
||||
/// Publisher to list packages for
|
||||
#[clap(short = 'p')]
|
||||
publisher: Option<Vec<String>>,
|
||||
|
||||
/// SSL key file
|
||||
#[clap(long)]
|
||||
key: Option<PathBuf>,
|
||||
|
||||
/// SSL certificate file
|
||||
#[clap(long)]
|
||||
cert: Option<PathBuf>,
|
||||
|
||||
/// Package FMRI patterns to match
|
||||
pkg_fmri_pattern: Option<Vec<String>>,
|
||||
},
|
||||
|
||||
/// Show contents of packages in a repository
|
||||
Contents {
|
||||
/// Path or URI of the repository
|
||||
#[clap(short = 's')]
|
||||
repo_uri_or_path: String,
|
||||
|
||||
/// Show manifest contents
|
||||
#[clap(short = 'm')]
|
||||
manifest: bool,
|
||||
|
||||
/// Filter by action type
|
||||
#[clap(short = 't')]
|
||||
action_type: Option<Vec<String>>,
|
||||
|
||||
/// SSL key file
|
||||
#[clap(long)]
|
||||
key: Option<PathBuf>,
|
||||
|
||||
/// SSL certificate file
|
||||
#[clap(long)]
|
||||
cert: Option<PathBuf>,
|
||||
|
||||
/// Package FMRI patterns to match
|
||||
pkg_fmri_pattern: Option<Vec<String>>,
|
||||
},
|
||||
|
||||
/// Rebuild repository metadata
|
||||
Rebuild {
|
||||
/// Path or URI of the repository
|
||||
#[clap(short = 's')]
|
||||
repo_uri_or_path: String,
|
||||
|
||||
/// Publisher to rebuild metadata for
|
||||
#[clap(short = 'p')]
|
||||
publisher: Option<Vec<String>>,
|
||||
|
||||
/// SSL key file
|
||||
#[clap(long)]
|
||||
key: Option<PathBuf>,
|
||||
|
||||
/// SSL certificate file
|
||||
#[clap(long)]
|
||||
cert: Option<PathBuf>,
|
||||
|
||||
/// Skip catalog rebuild
|
||||
#[clap(long)]
|
||||
no_catalog: bool,
|
||||
|
||||
/// Skip index rebuild
|
||||
#[clap(long)]
|
||||
no_index: bool,
|
||||
},
|
||||
|
||||
/// Refresh repository metadata
|
||||
Refresh {
|
||||
/// Path or URI of the repository
|
||||
#[clap(short = 's')]
|
||||
repo_uri_or_path: String,
|
||||
|
||||
/// Publisher to refresh metadata for
|
||||
#[clap(short = 'p')]
|
||||
publisher: Option<Vec<String>>,
|
||||
|
||||
/// SSL key file
|
||||
#[clap(long)]
|
||||
key: Option<PathBuf>,
|
||||
|
||||
/// SSL certificate file
|
||||
#[clap(long)]
|
||||
cert: Option<PathBuf>,
|
||||
|
||||
/// Skip catalog refresh
|
||||
#[clap(long)]
|
||||
no_catalog: bool,
|
||||
|
||||
/// Skip index refresh
|
||||
#[clap(long)]
|
||||
no_index: bool,
|
||||
},
|
||||
|
||||
/// Set repository properties
|
||||
Set {
|
||||
/// Path or URI of the repository
|
||||
#[clap(short = 's')]
|
||||
repo_uri_or_path: String,
|
||||
|
||||
/// Publisher to set properties for
|
||||
#[clap(short = 'p')]
|
||||
publisher: Option<String>,
|
||||
|
||||
/// Properties to set (section/property=value)
|
||||
property_value: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let cli = App::parse();
|
||||
|
||||
match &cli.command {
|
||||
Commands::Create { version, uri_or_path } => {
|
||||
println!("Creating repository version {} at {}", version, uri_or_path);
|
||||
|
||||
// Convert version to RepositoryVersion
|
||||
let repo_version = RepositoryVersion::try_from(*version)?;
|
||||
|
||||
// Create the repository
|
||||
let repo = FileBackend::create(uri_or_path, repo_version)?;
|
||||
|
||||
println!("Repository created successfully at {}", repo.path.display());
|
||||
Ok(())
|
||||
},
|
||||
Commands::AddPublisher { repo_uri_or_path, publisher } => {
|
||||
println!("Adding publishers {:?} to repository {}", publisher, repo_uri_or_path);
|
||||
|
||||
// Open the repository
|
||||
let mut repo = FileBackend::open(repo_uri_or_path)?;
|
||||
|
||||
// Add each publisher
|
||||
for p in publisher {
|
||||
println!("Adding publisher: {}", p);
|
||||
repo.add_publisher(p)?;
|
||||
}
|
||||
|
||||
println!("Publishers added successfully");
|
||||
Ok(())
|
||||
},
|
||||
Commands::RemovePublisher { repo_uri_or_path, dry_run, synchronous, publisher } => {
|
||||
println!("Removing publishers {:?} from repository {}", publisher, repo_uri_or_path);
|
||||
println!("Dry run: {}, Synchronous: {}", dry_run, synchronous);
|
||||
|
||||
// Open the repository
|
||||
let mut repo = FileBackend::open(repo_uri_or_path)?;
|
||||
|
||||
// Remove each publisher
|
||||
for p in publisher {
|
||||
println!("Removing publisher: {}", p);
|
||||
repo.remove_publisher(p, *dry_run)?;
|
||||
}
|
||||
|
||||
if *dry_run {
|
||||
println!("Dry run completed. No changes were made.");
|
||||
} else {
|
||||
println!("Publishers removed successfully");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
Commands::Get { repo_uri_or_path, format, omit_headers, publisher, key, cert, section_property } => {
|
||||
println!("Getting properties from repository {}", repo_uri_or_path);
|
||||
|
||||
// Open the repository
|
||||
let repo = FileBackend::open(repo_uri_or_path)?;
|
||||
|
||||
// Print headers if not omitted
|
||||
if !omit_headers {
|
||||
println!("{:<10} {:<10} {:<20}", "SECTION", "PROPERTY", "VALUE");
|
||||
}
|
||||
|
||||
// Print repository properties
|
||||
for (key, value) in &repo.config.properties {
|
||||
let parts: Vec<&str> = key.split('/').collect();
|
||||
if parts.len() == 2 {
|
||||
println!("{:<10} {:<10} {:<20}", parts[0], parts[1], value);
|
||||
} else {
|
||||
println!("{:<10} {:<10} {:<20}", "", key, value);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
Commands::Info { repo_uri_or_path, format, omit_headers, publisher, key, cert } => {
|
||||
println!("Displaying info for repository {}", repo_uri_or_path);
|
||||
|
||||
// Open the repository
|
||||
let repo = FileBackend::open(repo_uri_or_path)?;
|
||||
|
||||
// Get repository info
|
||||
let info = repo.get_info()?;
|
||||
|
||||
// Print headers if not omitted
|
||||
if !omit_headers {
|
||||
println!("{:<10} {:<8} {:<6} {:<30}", "PUBLISHER", "PACKAGES", "STATUS", "UPDATED");
|
||||
}
|
||||
|
||||
// Print repository info
|
||||
for (pub_name, pkg_count, status, updated) in info {
|
||||
println!("{:<10} {:<8} {:<6} {:<30}", pub_name, pkg_count, status, updated);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
Commands::List { repo_uri_or_path, format, omit_headers, publisher, key, cert, pkg_fmri_pattern } => {
|
||||
println!("Listing packages in repository {}", repo_uri_or_path);
|
||||
|
||||
// Open the repository
|
||||
let repo = FileBackend::open(repo_uri_or_path)?;
|
||||
|
||||
// Get the publisher if specified
|
||||
let pub_option = if let Some(publishers) = publisher {
|
||||
if !publishers.is_empty() {
|
||||
Some(publishers[0].as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Get the pattern if specified
|
||||
let pattern_option = if let Some(patterns) = pkg_fmri_pattern {
|
||||
if !patterns.is_empty() {
|
||||
Some(patterns[0].as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// List packages
|
||||
let packages = repo.list_packages(pub_option, pattern_option)?;
|
||||
|
||||
// Print headers if not omitted
|
||||
if !omit_headers {
|
||||
println!("{:<30} {:<15} {:<10}", "NAME", "VERSION", "PUBLISHER");
|
||||
}
|
||||
|
||||
// Print packages
|
||||
for (name, version, publisher) in packages {
|
||||
println!("{:<30} {:<15} {:<10}", name, version, publisher);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
Commands::Contents { repo_uri_or_path, manifest, action_type, key, cert, pkg_fmri_pattern } => {
|
||||
println!("Showing contents in repository {}", repo_uri_or_path);
|
||||
|
||||
// Open the repository
|
||||
let repo = FileBackend::open(repo_uri_or_path)?;
|
||||
|
||||
// Get the pattern if specified
|
||||
let pattern_option = if let Some(patterns) = pkg_fmri_pattern {
|
||||
if !patterns.is_empty() {
|
||||
Some(patterns[0].as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Show contents
|
||||
let contents = repo.show_contents(None, pattern_option, action_type.as_deref())?;
|
||||
|
||||
// Print contents
|
||||
for (package, path, action_type) in contents {
|
||||
if *manifest {
|
||||
// If manifest option is specified, print in manifest format
|
||||
println!("{} path={} type={}", action_type, path, package);
|
||||
} else {
|
||||
// Otherwise, print in table format
|
||||
println!("{:<40} {:<30} {:<10}", package, path, action_type);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
Commands::Rebuild { repo_uri_or_path, publisher, key, cert, no_catalog, no_index } => {
|
||||
println!("Rebuilding repository {}", repo_uri_or_path);
|
||||
|
||||
// Open the repository
|
||||
let repo = FileBackend::open(repo_uri_or_path)?;
|
||||
|
||||
// Get the publisher if specified
|
||||
let pub_option = if let Some(publishers) = publisher {
|
||||
if !publishers.is_empty() {
|
||||
Some(publishers[0].as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Rebuild repository metadata
|
||||
repo.rebuild(pub_option, *no_catalog, *no_index)?;
|
||||
|
||||
println!("Repository rebuilt successfully");
|
||||
Ok(())
|
||||
},
|
||||
Commands::Refresh { repo_uri_or_path, publisher, key, cert, no_catalog, no_index } => {
|
||||
println!("Refreshing repository {}", repo_uri_or_path);
|
||||
|
||||
// Open the repository
|
||||
let repo = FileBackend::open(repo_uri_or_path)?;
|
||||
|
||||
// Get the publisher if specified
|
||||
let pub_option = if let Some(publishers) = publisher {
|
||||
if !publishers.is_empty() {
|
||||
Some(publishers[0].as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Refresh repository metadata
|
||||
repo.refresh(pub_option, *no_catalog, *no_index)?;
|
||||
|
||||
println!("Repository refreshed successfully");
|
||||
Ok(())
|
||||
},
|
||||
Commands::Set { repo_uri_or_path, publisher, property_value } => {
|
||||
println!("Setting properties for repository {}", repo_uri_or_path);
|
||||
|
||||
// Open the repository
|
||||
let mut repo = FileBackend::open(repo_uri_or_path)?;
|
||||
|
||||
// Process each property=value pair
|
||||
for prop_val in property_value {
|
||||
// Split the property=value string
|
||||
let parts: Vec<&str> = prop_val.split('=').collect();
|
||||
if parts.len() != 2 {
|
||||
return Err(anyhow!("Invalid property=value format: {}", prop_val));
|
||||
}
|
||||
|
||||
let property = parts[0];
|
||||
let value = parts[1];
|
||||
|
||||
// If a publisher is specified, set the publisher property
|
||||
if let Some(pub_name) = publisher {
|
||||
println!("Setting publisher property {}/{} = {}", pub_name, property, value);
|
||||
repo.set_publisher_property(pub_name, property, value)?;
|
||||
} else {
|
||||
// Otherwise, set the repository property
|
||||
println!("Setting repository property {} = {}", property, value);
|
||||
repo.set_property(property, value)?;
|
||||
}
|
||||
}
|
||||
|
||||
println!("Properties set successfully");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
80
pkg6repo/src/tests.rs
Normal file
80
pkg6repo/src/tests.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use libips::repository::{Repository, RepositoryVersion, FileBackend, REPOSITORY_CONFIG_FILENAME};
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_create_repository() {
|
||||
// Create a temporary directory for the test
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let repo_path = temp_dir.path().join("repo");
|
||||
|
||||
// Create a repository
|
||||
let _ = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||
|
||||
// Check that the repository was created
|
||||
assert!(repo_path.exists());
|
||||
assert!(repo_path.join("catalog").exists());
|
||||
assert!(repo_path.join("file").exists());
|
||||
assert!(repo_path.join("index").exists());
|
||||
assert!(repo_path.join("pkg").exists());
|
||||
assert!(repo_path.join("trans").exists());
|
||||
assert!(repo_path.join(REPOSITORY_CONFIG_FILENAME).exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_publisher() {
|
||||
// Create a temporary directory for the test
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let repo_path = temp_dir.path().join("repo");
|
||||
|
||||
// Create a repository
|
||||
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||
|
||||
// Add a publisher
|
||||
repo.add_publisher("example.com").unwrap();
|
||||
|
||||
// Check that the publisher was added
|
||||
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
||||
assert!(repo_path.join("catalog").join("example.com").exists());
|
||||
assert!(repo_path.join("pkg").join("example.com").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_publisher() {
|
||||
// Create a temporary directory for the test
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let repo_path = temp_dir.path().join("repo");
|
||||
|
||||
// Create a repository
|
||||
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||
|
||||
// Add a publisher
|
||||
repo.add_publisher("example.com").unwrap();
|
||||
|
||||
// Check that the publisher was added
|
||||
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
||||
|
||||
// Remove the publisher
|
||||
repo.remove_publisher("example.com", false).unwrap();
|
||||
|
||||
// Check that the publisher was removed
|
||||
assert!(!repo.config.publishers.contains(&"example.com".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_property() {
|
||||
// Create a temporary directory for the test
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let repo_path = temp_dir.path().join("repo");
|
||||
|
||||
// Create a repository
|
||||
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||
|
||||
// Set a property
|
||||
repo.set_property("publisher/prefix", "example.com").unwrap();
|
||||
|
||||
// Check that the property was set
|
||||
assert_eq!(repo.config.properties.get("publisher/prefix").unwrap(), "example.com");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue