From 4649608408d261029cb3718ab099a9c6ddbe7db3 Mon Sep 17 00:00:00 2001 From: Till Wegmueller Date: Sat, 26 Jul 2025 15:33:39 +0200 Subject: [PATCH] Replace `anyhow` with custom `RepositoryError` for improved error specificity and consistency. Remove `anyhow` dependency. --- .gitignore | 2 +- Cargo.lock | 847 ++------------------------ libips/Cargo.toml | 8 +- libips/src/actions/mod.rs | 38 +- libips/src/digest/mod.rs | 12 +- libips/src/fmri.rs | 32 +- libips/src/image/mod.rs | 17 +- libips/src/lib.rs | 1 + libips/src/payload/mod.rs | 13 +- libips/src/repository/catalog.rs | 77 ++- libips/src/repository/file_backend.rs | 86 ++- libips/src/repository/mod.rs | 164 ++++- libips/src/repository/rest_backend.rs | 13 +- libips/src/repository/tests.rs | 16 +- libips/src/test_json_manifest.rs | 60 ++ 15 files changed, 518 insertions(+), 868 deletions(-) create mode 100644 libips/src/test_json_manifest.rs diff --git a/.gitignore b/.gitignore index eb799b5..bd0f2c0 100644 --- a/.gitignore +++ b/.gitignore @@ -88,7 +88,7 @@ Cargo.lock **/*.rs.bk # Prototype directory -sample_data/**/build/prototype/i386 +sample_data .vagrant /.output.txt diff --git a/Cargo.lock b/Cargo.lock index 554df2c..1b3bdc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,21 +17,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "android-tzdata" version = "0.1.1" @@ -103,23 +88,6 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - -[[package]] -name = "async-trait" -version = "0.1.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "atty" version = "0.2.14" @@ -143,12 +111,6 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - [[package]] name = "bitflags" version = "1.3.2" @@ -161,15 +123,6 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" -[[package]] -name = "bitpacking" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1d3e2bfd8d06048a179f7b17afc3188effa10385e7b00dc65af6aae732ea92" -dependencies = [ - "crunchy", -] - [[package]] name = "block-buffer" version = "0.9.0" @@ -195,43 +148,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" -[[package]] -name = "bon" -version = "3.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d9ef19ae5263a138da9a86871eca537478ab0332a7770bac7e3f08b801f89f" -dependencies = [ - "bon-macros", - "rustversion", -] - -[[package]] -name = "bon-macros" -version = "3.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "577ae008f2ca11ca7641bd44601002ee5ab49ef0af64846ce1ab6057218a5cc1" -dependencies = [ - "darling", - "ident_case", - "prettyplease", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.104", -] - [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.4.0" @@ -244,17 +166,9 @@ version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ - "jobserver", - "libc", "shlex", ] -[[package]] -name = "census" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0" - [[package]] name = "cfg-if" version = "1.0.0" @@ -394,46 +308,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - [[package]] name = "crypto-common" version = "0.1.6" @@ -444,51 +318,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "darling" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a79c4acb1fd5fa3d9304be4c76e031c54d2e92d172a393e24b19a14fe8532fe9" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74875de90daf30eb59609910b84d4d368103aaec4c924824c6799b28f77d6a1d" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.11.1", - "syn 2.0.104", -] - -[[package]] -name = "darling_macro" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79f8e61677d5df9167cd85265f8e5f64b215cdea3fb55eebc3e622e44c7a146" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "deranged" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" -dependencies = [ - "powerfmt", - "serde", -] - [[package]] name = "diff-struct" version = "0.5.3" @@ -550,12 +379,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "downcast-rs" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf" - [[package]] name = "either" version = "1.8.1" @@ -571,12 +394,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "errno" version = "0.3.13" @@ -587,18 +404,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "expry" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9952454f8822e6e09b17119fc4dce4531c00febd96e7f770cf45dfdd587e36c0" - -[[package]] -name = "fastdivide" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afc2bd4d5a73106dd53d10d73d3401c2f32730ba2c0b93ddb888a8983680471" - [[package]] name = "fastrand" version = "2.3.0" @@ -621,12 +426,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "foreign-types" version = "0.3.2" @@ -651,16 +450,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs4" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e180ac76c23b45e767bd7ae9579bc0bb458618c4bc71835926e098e61d15f8" -dependencies = [ - "rustix 0.38.44", - "windows-sys 0.52.0", -] - [[package]] name = "futures-channel" version = "0.3.28" @@ -682,17 +471,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "futures-sink" version = "0.3.28" @@ -713,7 +491,6 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", - "futures-macro", "futures-task", "memchr", "pin-project-lite", @@ -779,17 +556,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - [[package]] name = "heck" version = "0.4.1" @@ -820,12 +586,6 @@ dependencies = [ "libc", ] -[[package]] -name = "htmlescape" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" - [[package]] name = "http" version = "0.2.9" @@ -910,15 +670,6 @@ dependencies = [ "tokio-native-tls", ] -[[package]] -name = "hyperloglogplus" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "621debdf94dcac33e50475fdd76d34d5ea9c0362a834b9db08c3024696c1fbe3" -dependencies = [ - "serde", -] - [[package]] name = "iana-time-zone" version = "0.1.63" @@ -943,12 +694,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.3.0" @@ -966,7 +711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown 0.12.3", + "hashbrown", ] [[package]] @@ -981,31 +726,12 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" -[[package]] -name = "jobserver" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" -dependencies = [ - "getrandom 0.3.3", - "libc", -] - [[package]] name = "js-sys" version = "0.3.77" @@ -1031,12 +757,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "levenshtein_automata" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" - [[package]] name = "libc" version = "0.2.174" @@ -1047,39 +767,27 @@ checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" name = "libips" version = "0.1.2" dependencies = [ - "anyhow", "chrono", "diff-struct", "flate2", "lz4", "maplit 0.1.6", + "miette", "object", "pest", "pest_derive", "regex", - "searchy", "semver", "serde", "serde_json", "sha2 0.9.9", "sha3", "strum", - "tantivy", - "thiserror 1.0.40", + "tempfile", + "thiserror 1.0.69", + "tracing", ] -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -1095,15 +803,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.4", -] - [[package]] name = "lz4" version = "1.28.1" @@ -1123,12 +822,6 @@ dependencies = [ "libc", ] -[[package]] -name = "lz4_flex" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" - [[package]] name = "maplit" version = "0.1.6" @@ -1141,15 +834,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "measure_time" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51c55d61e72fc3ab704396c5fa16f4c184db37978ae4e94ca8959693a235fc0e" -dependencies = [ - "log", -] - [[package]] name = "memchr" version = "2.5.0" @@ -1157,12 +841,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "memmap2" -version = "0.9.7" +name = "miette" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ - "libc", + "cfg-if", + "miette-derive", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] @@ -1171,12 +868,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1198,12 +889,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "murmurhash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b" - [[package]] name = "native-tls" version = "0.2.11" @@ -1222,16 +907,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "num" version = "0.4.3" @@ -1265,12 +940,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" @@ -1309,7 +978,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -1344,12 +1012,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" -[[package]] -name = "oneshot" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea" - [[package]] name = "opaque-debug" version = "0.3.0" @@ -1416,15 +1078,6 @@ version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" -[[package]] -name = "ownedbytes" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fbd56f7631767e61784dc43f8580f403f4475bd4aaa4da003e6295e1bab4a7e" -dependencies = [ - "stable_deref_trait", -] - [[package]] name = "percent-encoding" version = "2.2.0" @@ -1437,7 +1090,7 @@ version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" dependencies = [ - "thiserror 1.0.40", + "thiserror 1.0.69", "ucd-trie", ] @@ -1543,31 +1196,6 @@ dependencies = [ "which", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" -dependencies = [ - "proc-macro2", - "syn 2.0.104", -] - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1616,66 +1244,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.9", -] - -[[package]] -name = "rand_distr" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.2.16" @@ -1693,7 +1261,7 @@ checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.9", "redox_syscall", - "thiserror 1.0.40", + "thiserror 1.0.69", ] [[package]] @@ -1702,9 +1270,9 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ - "aho-corasick 0.7.20", + "aho-corasick", "memchr", - "regex-syntax 0.6.29", + "regex-syntax", ] [[package]] @@ -1713,19 +1281,13 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - [[package]] name = "reqwest" version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ - "base64 0.21.0", + "base64", "bytes", "encoding_rs", "futures-core", @@ -1776,35 +1338,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rust-stemmers" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" -dependencies = [ - "serde", - "serde_derive", -] - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.9.1", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - [[package]] name = "rustix" version = "1.0.8" @@ -1814,7 +1347,7 @@ dependencies = [ "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "windows-sys 0.59.0", ] @@ -1836,7 +1369,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.21.0", + "base64", ] [[package]] @@ -1870,16 +1403,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "searchy" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671b679ad59959dcf9843fc74a2e9c7ee39adac5401edb942a4619d976a3cee7" -dependencies = [ - "expry", - "smol_str", -] - [[package]] name = "security-framework" version = "2.8.2" @@ -2007,15 +1530,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "sketches-ddsketch" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e9a774a6c28142ac54bb25d25562e6bcf957493a184f15ad4eebccb23e410a" -dependencies = [ - "serde", -] - [[package]] name = "slab" version = "0.4.8" @@ -2025,21 +1539,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "smol_str" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" -dependencies = [ - "serde", -] - [[package]] name = "socket2" version = "0.4.9" @@ -2066,12 +1565,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "strsim" version = "0.10.0" @@ -2128,152 +1621,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tantivy" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a966cb0e76e311f09cf18507c9af192f15d34886ee43d7ba7c7e3803660c43" -dependencies = [ - "aho-corasick 1.1.3", - "arc-swap", - "base64 0.22.1", - "bitpacking", - "bon", - "byteorder", - "census", - "crc32fast", - "crossbeam-channel", - "downcast-rs", - "fastdivide", - "fnv", - "fs4", - "htmlescape", - "hyperloglogplus", - "itertools", - "levenshtein_automata", - "log", - "lru", - "lz4_flex", - "measure_time", - "memmap2", - "once_cell", - "oneshot", - "rayon", - "regex", - "rust-stemmers", - "rustc-hash", - "serde", - "serde_json", - "sketches-ddsketch", - "smallvec", - "tantivy-bitpacker", - "tantivy-columnar", - "tantivy-common", - "tantivy-fst", - "tantivy-query-grammar", - "tantivy-stacker", - "tantivy-tokenizer-api", - "tempfile", - "thiserror 2.0.12", - "time", - "uuid", - "winapi", -] - -[[package]] -name = "tantivy-bitpacker" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adc286a39e089ae9938935cd488d7d34f14502544a36607effd2239ff0e2494" -dependencies = [ - "bitpacking", -] - -[[package]] -name = "tantivy-columnar" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6300428e0c104c4f7db6f95b466a6f5c1b9aece094ec57cdd365337908dc7344" -dependencies = [ - "downcast-rs", - "fastdivide", - "itertools", - "serde", - "tantivy-bitpacker", - "tantivy-common", - "tantivy-sstable", - "tantivy-stacker", -] - -[[package]] -name = "tantivy-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b6ea6090ce03dc72c27d0619e77185d26cc3b20775966c346c6d4f7e99d7f" -dependencies = [ - "async-trait", - "byteorder", - "ownedbytes", - "serde", - "time", -] - -[[package]] -name = "tantivy-fst" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d60769b80ad7953d8a7b2c70cdfe722bbcdcac6bccc8ac934c40c034d866fc18" -dependencies = [ - "byteorder", - "regex-syntax 0.8.5", - "utf8-ranges", -] - -[[package]] -name = "tantivy-query-grammar" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e810cdeeebca57fc3f7bfec5f85fdbea9031b2ac9b990eb5ff49b371d52bbe6a" -dependencies = [ - "nom", - "serde", - "serde_json", -] - -[[package]] -name = "tantivy-sstable" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709f22c08a4c90e1b36711c1c6cad5ae21b20b093e535b69b18783dd2cb99416" -dependencies = [ - "futures-util", - "itertools", - "tantivy-bitpacker", - "tantivy-common", - "tantivy-fst", - "zstd", -] - -[[package]] -name = "tantivy-stacker" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bcdebb267671311d1e8891fd9d1301803fdb8ad21ba22e0a30d0cab49ba59c1" -dependencies = [ - "murmurhash32", - "rand_distr", - "tantivy-common", -] - -[[package]] -name = "tantivy-tokenizer-api" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfa942fcee81e213e09715bbce8734ae2180070b97b33839a795ba1de201547d" -dependencies = [ - "serde", -] - [[package]] name = "tempfile" version = "3.20.0" @@ -2283,7 +1630,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.8", + "rustix", "windows-sys 0.59.0", ] @@ -2304,11 +1651,11 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.40", + "thiserror-impl 1.0.69", ] [[package]] @@ -2322,9 +1669,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -2342,37 +1689,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "time" -version = "0.3.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" - -[[package]] -name = "time-macros" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -2453,9 +1769,21 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "tracing-core" version = "0.1.30" @@ -2504,6 +1832,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "untrusted" version = "0.7.1" @@ -2537,32 +1871,16 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 1.0.40", + "thiserror 1.0.69", "url", ] -[[package]] -name = "utf8-ranges" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" - [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "uuid" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" -dependencies = [ - "getrandom 0.3.3", - "serde", -] - [[package]] name = "vcpkg" version = "0.2.15" @@ -2830,15 +2148,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.59.0" @@ -2994,51 +2303,3 @@ dependencies = [ "anyhow", "clap 4.5.41", ] - -[[package]] -name = "zerocopy" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/libips/Cargo.toml b/libips/Cargo.toml index 271321c..847e3a9 100644 --- a/libips/Cargo.toml +++ b/libips/Cargo.toml @@ -17,8 +17,9 @@ keywords = ["packaging", "illumos"] [dependencies] regex = "1.5.5" -anyhow = "1.0.56" -thiserror = "1.0.30" +thiserror = "1.0.50" +miette = "7.6.0" +tracing = "0.1.37" maplit = "0.1.6" object = "0.23.0" sha2 = "0.9.3" @@ -32,6 +33,5 @@ flate2 = "1.0.28" lz4 = "1.24.0" semver = { version = "1.0.20", features = ["serde"] } diff-struct = "0.5.3" -searchy = "0.5.0" -tantivy = { version = "0.24.2", features = ["mmap"] } chrono = "0.4.41" +tempfile = "3.20.0" diff --git a/libips/src/actions/mod.rs b/libips/src/actions/mod.rs index acebcc1..8374c17 100644 --- a/libips/src/actions/mod.rs +++ b/libips/src/actions/mod.rs @@ -9,6 +9,7 @@ use crate::digest::Digest; use crate::fmri::Fmri; use crate::payload::{Payload, PayloadError}; use diff::Diff; +use miette::Diagnostic; use pest::Parser; use pest_derive::Parser; use serde::{Deserialize, Serialize}; @@ -22,21 +23,29 @@ use thiserror::Error; type Result = StdResult; -#[derive(Debug, Error)] +#[derive(Debug, Error, Diagnostic)] pub enum ActionError { #[error(transparent)] + #[diagnostic(transparent)] PayloadError(#[from] PayloadError), #[error(transparent)] + #[diagnostic(transparent)] FileError(#[from] FileError), #[error("value {0} is not a boolean")] + #[diagnostic( + code(ips::action_error::invalid_boolean), + help("Boolean values must be 'true', 'false', 't', or 'f'.") + )] NotBooleanValue(String), #[error(transparent)] + #[diagnostic(code(ips::action_error::io))] IOError(#[from] std::io::Error), #[error(transparent)] + #[diagnostic(code(ips::action_error::parser))] ParserError(#[from] pest::error::Error), } @@ -256,9 +265,13 @@ impl FacetedAction for File { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, Diagnostic)] pub enum FileError { #[error("file path is not a string")] + #[diagnostic( + code(ips::action_error::file::invalid_path), + help("The file path must be convertible to a valid string.") + )] FilePathIsNoStringError, } @@ -538,7 +551,15 @@ impl Manifest { pub fn parse_file>(f: P) -> Result { let content = read_to_string(f)?; - Manifest::parse_string(content) + + // Try to parse as JSON first + match serde_json::from_str::(&content) { + Ok(manifest) => Ok(manifest), + Err(_) => { + // If JSON parsing fails, fall back to string format + Manifest::parse_string(content) + } + } } pub fn parse_string(content: String) -> Result { @@ -630,11 +651,20 @@ impl Default for ActionKind { } //TODO Multierror and no failure for these cases -#[derive(Debug, Error)] +#[derive(Debug, Error, Diagnostic)] pub enum ManifestError { #[error("unknown action {action:?} at line {line:?}")] + #[diagnostic( + code(ips::action_error::manifest::unknown_action), + help("Check the action name and make sure it's one of the supported action types.") + )] UnknownAction { line: usize, action: String }, + #[error("action string \"{action:?}\" at line {line:?} is invalid: {message:?}")] + #[diagnostic( + code(ips::action_error::manifest::invalid_action), + help("Check the action format and fix the syntax error.") + )] InvalidAction { line: usize, action: String, diff --git a/libips/src/digest/mod.rs b/libips/src/digest/mod.rs index dc0d750..99a7083 100644 --- a/libips/src/digest/mod.rs +++ b/libips/src/digest/mod.rs @@ -4,6 +4,7 @@ // obtain one at https://mozilla.org/MPL/2.0/. use diff::Diff; +use miette::Diagnostic; use serde::{Deserialize, Serialize}; use sha2::Digest as Sha2Digest; #[allow(unused_imports)] @@ -146,10 +147,19 @@ impl Display for Digest { } } -#[derive(Debug, Error)] +#[derive(Debug, Error, Diagnostic)] pub enum DigestError { #[error("hashing algorithm {algorithm:?} is not known by this library")] + #[diagnostic( + code(ips::digest_error::unknown_algorithm), + help("Use one of the supported algorithms: sha1, sha256t, sha512t, sha512t_256, sha3256t, sha3512t_256, sha3512t") + )] UnknownAlgorithm { algorithm: String }, + #[error("digest {digest:?} is not formatted properly: {details:?}")] + #[diagnostic( + code(ips::digest_error::invalid_format), + help("Digest should be in the format: source:algorithm:hash") + )] InvalidDigestFormat { digest: String, details: String }, } diff --git a/libips/src/fmri.rs b/libips/src/fmri.rs index fd349a7..aece6ff 100644 --- a/libips/src/fmri.rs +++ b/libips/src/fmri.rs @@ -58,25 +58,55 @@ //! ``` use diff::Diff; +use miette::Diagnostic; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; use thiserror::Error; /// Errors that can occur when parsing an FMRI -#[derive(Debug, Error, PartialEq)] +#[derive(Debug, Error, Diagnostic, PartialEq)] pub enum FmriError { #[error("invalid FMRI format")] + #[diagnostic( + code(ips::fmri_error::invalid_format), + help("FMRI should be in the format: [scheme://][publisher/]name[@version]") + )] InvalidFormat, + #[error("invalid version format")] + #[diagnostic( + code(ips::fmri_error::invalid_version_format), + help("Version should be in the format: release[,branch][-build][:timestamp]") + )] InvalidVersionFormat, + #[error("invalid release format")] + #[diagnostic( + code(ips::fmri_error::invalid_release_format), + help("Release should be a dot-separated vector of digits (e.g., 5.11)") + )] InvalidReleaseFormat, + #[error("invalid branch format")] + #[diagnostic( + code(ips::fmri_error::invalid_branch_format), + help("Branch should be a dot-separated vector of digits (e.g., 1)") + )] InvalidBranchFormat, + #[error("invalid build format")] + #[diagnostic( + code(ips::fmri_error::invalid_build_format), + help("Build should be a dot-separated vector of digits (e.g., 2020.0.1.0)") + )] InvalidBuildFormat, + #[error("invalid timestamp format")] + #[diagnostic( + code(ips::fmri_error::invalid_timestamp_format), + help("Timestamp should be a hexadecimal string (e.g., 20200421T195136Z)") + )] InvalidTimestampFormat, } diff --git a/libips/src/image/mod.rs b/libips/src/image/mod.rs index 9fd9310..3337b46 100644 --- a/libips/src/image/mod.rs +++ b/libips/src/image/mod.rs @@ -1,18 +1,27 @@ mod properties; use properties::*; +use miette::Diagnostic; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs::File; use std::path::{Path, PathBuf}; use thiserror::Error; -#[derive(Debug, Error)] +#[derive(Debug, Error, Diagnostic)] pub enum ImageError { - //Implement derives for IO error and serde_json error - #[error(transparent)] + #[error("I/O error: {0}")] + #[diagnostic( + code(ips::image_error::io), + help("Check system resources and permissions") + )] IO(#[from] std::io::Error), - #[error(transparent)] + + #[error("JSON error: {0}")] + #[diagnostic( + code(ips::image_error::json), + help("Check the JSON format and try again") + )] Json(#[from] serde_json::Error), } diff --git a/libips/src/lib.rs b/libips/src/lib.rs index d30f190..ff1c1c9 100644 --- a/libips/src/lib.rs +++ b/libips/src/lib.rs @@ -10,6 +10,7 @@ pub mod fmri; pub mod image; pub mod payload; pub mod repository; +mod test_json_manifest; #[cfg(test)] mod tests { diff --git a/libips/src/payload/mod.rs b/libips/src/payload/mod.rs index e96264a..39a97b7 100644 --- a/libips/src/payload/mod.rs +++ b/libips/src/payload/mod.rs @@ -5,6 +5,7 @@ use crate::digest::{Digest, DigestAlgorithm, DigestError, DigestSource}; use diff::Diff; +use miette::Diagnostic; use object::Object; use serde::{Deserialize, Serialize}; use std::io::Error as IOError; @@ -14,11 +15,17 @@ use thiserror::Error; type Result = StdResult; -#[derive(Debug, Error)] +#[derive(Debug, Error, Diagnostic)] pub enum PayloadError { - #[error("io error: {0}")] + #[error("I/O error: {0}")] + #[diagnostic( + code(ips::payload_error::io), + help("Check system resources and permissions") + )] IOError(#[from] IOError), - #[error("digest error: {0}")] + + #[error(transparent)] + #[diagnostic(transparent)] DigestError(#[from] DigestError), } diff --git a/libips/src/repository/catalog.rs b/libips/src/repository/catalog.rs index b788649..ff9a714 100644 --- a/libips/src/repository/catalog.rs +++ b/libips/src/repository/catalog.rs @@ -3,15 +3,74 @@ // MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use anyhow::Result; +use miette::Diagnostic; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; +use std::io; use std::path::{Path, PathBuf}; use std::time::SystemTime; +use thiserror::Error; use crate::fmri::Fmri; +/// Errors that can occur in catalog operations +#[derive(Debug, Error, Diagnostic)] +pub enum CatalogError { + #[error("catalog part does not exist: {name}")] + #[diagnostic( + code(ips::repository_error::catalog::part_not_found), + help("Check that the catalog part exists and is accessible") + )] + CatalogPartNotFound { + name: String, + }, + + #[error("catalog part not loaded: {name}")] + #[diagnostic( + code(ips::repository_error::catalog::part_not_loaded), + help("Load the catalog part before attempting to save it") + )] + CatalogPartNotLoaded { + name: String, + }, + + #[error("update log not loaded: {name}")] + #[diagnostic( + code(ips::repository_error::catalog::update_log_not_loaded), + help("Load the update log before attempting to save it") + )] + UpdateLogNotLoaded { + name: String, + }, + + #[error("update log does not exist: {name}")] + #[diagnostic( + code(ips::repository_error::catalog::update_log_not_found), + help("Check that the update log exists and is accessible") + )] + UpdateLogNotFound { + name: String, + }, + + #[error("failed to serialize JSON: {0}")] + #[diagnostic( + code(ips::repository_error::catalog::json_serialize), + help("This is likely a bug in the code") + )] + JsonSerializationError(#[from] serde_json::Error), + + #[error("I/O error: {0}")] + #[diagnostic( + code(ips::repository_error::catalog::io), + help("Check system resources and permissions") + )] + IoError(#[from] io::Error), +} + +/// Result type for catalog operations +pub type Result = std::result::Result; + /// Format a SystemTime as an ISO-8601 'basic format' date in UTC fn format_iso8601_basic(time: &SystemTime) -> String { let datetime = convert_system_time_to_datetime(time); @@ -393,7 +452,9 @@ impl CatalogManager { self.parts.insert(name.to_string(), part); Ok(()) } else { - Err(anyhow::anyhow!("Catalog part does not exist: {}", name)) + Err(CatalogError::CatalogPartNotFound { + name: name.to_string(), + }) } } @@ -404,7 +465,9 @@ impl CatalogManager { part.save(&part_path)?; Ok(()) } else { - Err(anyhow::anyhow!("Catalog part not loaded: {}", name)) + Err(CatalogError::CatalogPartNotLoaded { + name: name.to_string(), + }) } } @@ -453,7 +516,9 @@ impl CatalogManager { Ok(()) } else { - Err(anyhow::anyhow!("Update log not loaded: {}", name)) + Err(CatalogError::UpdateLogNotLoaded { + name: name.to_string(), + }) } } @@ -465,7 +530,9 @@ impl CatalogManager { self.update_logs.insert(name.to_string(), log); Ok(()) } else { - Err(anyhow::anyhow!("Update log does not exist: {}", name)) + Err(CatalogError::UpdateLogNotFound { + name: name.to_string(), + }) } } diff --git a/libips/src/repository/file_backend.rs b/libips/src/repository/file_backend.rs index 0d030bd..e2cd515 100644 --- a/libips/src/repository/file_backend.rs +++ b/libips/src/repository/file_backend.rs @@ -3,7 +3,7 @@ // MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use anyhow::{anyhow, Result}; +use super::{Result, RepositoryError}; use flate2::write::GzEncoder; use flate2::Compression as GzipCompression; use lz4::EncoderBuilder; @@ -315,12 +315,12 @@ impl Transaction { if temp_file_path.exists() { // If it exists, remove it to avoid any issues with existing content fs::remove_file(&temp_file_path) - .map_err(|e| anyhow!("Failed to remove existing temp file: {}", e))?; + .map_err(|e| RepositoryError::FileWriteError(format!("Failed to remove existing temp file: {}", e)))?; } // Read the file content let file_content = fs::read(file_path) - .map_err(|e| anyhow!("Failed to read file {}: {}", file_path.display(), e))?; + .map_err(|e| RepositoryError::FileReadError(format!("Failed to read file {}: {}", file_path.display(), e)))?; // Create a payload with the hash information if it doesn't exist let mut updated_file_action = file_action; @@ -338,16 +338,16 @@ impl Transaction { // Write the file content to the encoder encoder .write_all(&file_content) - .map_err(|e| anyhow!("Failed to write data to Gzip encoder: {}", e))?; + .map_err(|e| RepositoryError::Other(format!("Failed to write data to Gzip encoder: {}", e)))?; // Finish the compression and get the compressed data let compressed_data = encoder .finish() - .map_err(|e| anyhow!("Failed to finish Gzip compression: {}", e))?; + .map_err(|e| RepositoryError::Other(format!("Failed to finish Gzip compression: {}", e)))?; // Write the compressed data to the temp file fs::write(&temp_file_path, &compressed_data) - .map_err(|e| anyhow!("Failed to write compressed data to temp file: {}", e))?; + .map_err(|e| RepositoryError::FileWriteError(format!("Failed to write compressed data to temp file: {}", e)))?; // Calculate hash of the compressed data let mut hasher = Sha256::new(); @@ -358,19 +358,19 @@ impl Transaction { // Create an LZ4 encoder with default compression level let mut encoder = EncoderBuilder::new() .build(Vec::new()) - .map_err(|e| anyhow!("Failed to create LZ4 encoder: {}", e))?; + .map_err(|e| RepositoryError::Other(format!("Failed to create LZ4 encoder: {}", e)))?; // Write the file content to the encoder encoder .write_all(&file_content) - .map_err(|e| anyhow!("Failed to write data to LZ4 encoder: {}", e))?; + .map_err(|e| RepositoryError::Other(format!("Failed to write data to LZ4 encoder: {}", e)))?; // Finish the compression and get the compressed data let (compressed_data, _) = encoder.finish(); // Write the compressed data to the temp file fs::write(&temp_file_path, &compressed_data).map_err(|e| { - anyhow!("Failed to write LZ4 compressed data to temp file: {}", e) + RepositoryError::FileWriteError(format!("Failed to write LZ4 compressed data to temp file: {}", e)) })?; // Calculate hash of the compressed data @@ -510,7 +510,7 @@ impl ReadableRepository for FileBackend { // Check if the repository directory exists if !path.exists() { - return Err(anyhow!("Repository does not exist: {}", path.display())); + return Err(RepositoryError::NotFound(path.display().to_string())); } // Load the repository configuration @@ -595,7 +595,7 @@ impl ReadableRepository for FileBackend { // 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)); + return Err(RepositoryError::PublisherNotFound(pub_name.to_string())); } vec![pub_name.to_string()] } else { @@ -611,9 +611,8 @@ impl ReadableRepository for FileBackend { if publisher_pkg_dir.exists() { // Verify that the publisher is in the config if !self.config.publishers.contains(&pub_name) { - return Err(anyhow!( - "Publisher directory exists but is not in the repository configuration: {}", - pub_name + return Err(RepositoryError::Other( + format!("Publisher directory exists but is not in the repository configuration: {}", pub_name) )); } @@ -746,7 +745,7 @@ impl ReadableRepository for FileBackend { // 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)); + return Err(RepositoryError::PublisherNotFound(pub_name.to_string())); } vec![pub_name.to_string()] } else { @@ -987,8 +986,13 @@ impl ReadableRepository for FileBackend { publisher: Option<&str>, limit: Option, ) -> Result> { + println!("Searching for packages with query: {}", query); + println!("Publisher: {:?}", publisher); + println!("Limit: {:?}", limit); + // If no publisher is specified, use the default publisher if available let publisher = publisher.or_else(|| self.config.default_publisher.as_deref()); + println!("Effective publisher: {:?}", publisher); // If still no publisher, we need to search all publishers let publishers = if let Some(pub_name) = publisher { @@ -996,34 +1000,55 @@ impl ReadableRepository for FileBackend { } else { self.config.publishers.clone() }; + println!("Publishers to search: {:?}", publishers); let mut results = Vec::new(); // For each publisher, search the index for pub_name in publishers { + println!("Searching publisher: {}", pub_name); + // Check if the index exists + let index_path = self.path.join("index").join(&pub_name).join("search.json"); + println!("Index path: {}", index_path.display()); + println!("Index exists: {}", index_path.exists()); + if let Ok(Some(index)) = self.get_search_index(&pub_name) { + println!("Got search index for publisher: {}", pub_name); + println!("Index terms: {:?}", index.terms.keys().collect::>()); + // Search the index let fmris = index.search(query, limit); + println!("Search results (FMRIs): {:?}", fmris); // Convert FMRIs to PackageInfo for fmri_str in fmris { if let Ok(fmri) = Fmri::parse(&fmri_str) { + println!("Adding package to results: {}", fmri); results.push(PackageInfo { fmri }); + } else { + println!("Failed to parse FMRI: {}", fmri_str); } } } else { + println!("No search index found for publisher: {}", pub_name); + println!("Falling back to simple search"); + // If the index doesn't exist, fall back to the simple search let all_packages = self.list_packages(Some(&pub_name), None)?; + println!("All packages: {:?}", all_packages); // Filter packages by the query string let matching_packages: Vec = all_packages .into_iter() .filter(|pkg| { // Match against package name - pkg.fmri.stem().contains(query) + let matches = pkg.fmri.stem().contains(query); + println!("Package: {}, Matches: {}", pkg.fmri.stem(), matches); + matches }) .collect(); + println!("Matching packages: {:?}", matching_packages); // Add matching packages to the results results.extend(matching_packages); @@ -1035,6 +1060,7 @@ impl ReadableRepository for FileBackend { results.truncate(max_results); } + println!("Final search results: {:?}", results); Ok(results) } } @@ -1111,13 +1137,13 @@ impl WritableRepository for FileBackend { // Remove the catalog directory if it exists if catalog_dir.exists() { fs::remove_dir_all(&catalog_dir) - .map_err(|e| anyhow!("Failed to remove catalog directory: {}", e))?; + .map_err(|e| RepositoryError::Other(format!("Failed to remove catalog directory: {}", e)))?; } // Remove the package directory if it exists if pkg_dir.exists() { fs::remove_dir_all(&pkg_dir) - .map_err(|e| anyhow!("Failed to remove package directory: {}", e))?; + .map_err(|e| RepositoryError::Other(format!("Failed to remove package directory: {}", e)))?; } // Save the updated configuration @@ -1146,7 +1172,7 @@ impl WritableRepository for FileBackend { ) -> Result<()> { // Check if the publisher exists if !self.config.publishers.contains(&publisher.to_string()) { - return Err(anyhow!("Publisher does not exist: {}", publisher)); + return Err(RepositoryError::PublisherNotFound(publisher.to_string())); } // Create the property key in the format "publisher/property" @@ -1166,7 +1192,7 @@ impl WritableRepository for FileBackend { // 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)); + return Err(RepositoryError::PublisherNotFound(pub_name.to_string())); } vec![pub_name.to_string()] } else { @@ -1196,7 +1222,7 @@ impl WritableRepository for FileBackend { // 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)); + return Err(RepositoryError::PublisherNotFound(pub_name.to_string())); } vec![pub_name.to_string()] } else { @@ -1235,7 +1261,7 @@ impl WritableRepository for FileBackend { fn set_default_publisher(&mut self, publisher: &str) -> Result<()> { // Check if the publisher exists if !self.config.publishers.contains(&publisher.to_string()) { - return Err(anyhow!("Publisher does not exist: {}", publisher)); + return Err(RepositoryError::PublisherNotFound(publisher.to_string())); } // Set the default publisher @@ -1729,10 +1755,10 @@ impl FileBackend { let actual_path = &transaction.manifest.files[0].path; if actual_path != expected_path { - return Err(anyhow!( - "Path in FileAction is incorrect. Expected: {}, Actual: {}", + return Err(RepositoryError::Other( + format!("Path in FileAction is incorrect. Expected: {}, Actual: {}", expected_path, - actual_path + actual_path) )); } @@ -1744,14 +1770,14 @@ impl FileBackend { let stored_file_path = self.path.join("file").join(&hash); if !stored_file_path.exists() { - return Err(anyhow!("File was not stored correctly")); + return Err(RepositoryError::Other("File was not stored correctly".to_string())); } // 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")); + return Err(RepositoryError::Other("Manifest was not created".to_string())); } println!("File publishing test passed!"); @@ -1770,15 +1796,15 @@ impl FileBackend { // Check if the prototype directory exists if !proto_dir.exists() { - return Err(anyhow!( + return Err(RepositoryError::NotFound(format!( "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)); + return Err(RepositoryError::PublisherNotFound(publisher.to_string())); } // Begin a transaction diff --git a/libips/src/repository/mod.rs b/libips/src/repository/mod.rs index 89ca764..6714546 100644 --- a/libips/src/repository/mod.rs +++ b/libips/src/repository/mod.rs @@ -3,9 +3,159 @@ // MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use anyhow::Result; use std::collections::HashMap; -use std::path::Path; +use std::io; +use std::path::{Path, StripPrefixError}; +use miette::Diagnostic; +use thiserror::Error; + +/// Result type for repository operations +pub type Result = std::result::Result; + +/// Errors that can occur in repository operations +#[derive(Debug, Error, Diagnostic)] +pub enum RepositoryError { + #[error("unsupported repository version: {0}")] + #[diagnostic( + code(ips::repository_error::unsupported_version), + help("Supported repository versions: 4") + )] + UnsupportedVersion(u32), + + #[error("repository not found at {0}")] + #[diagnostic( + code(ips::repository_error::not_found), + help("Check that the repository path exists and is accessible") + )] + NotFound(String), + + #[error("publisher {0} not found")] + #[diagnostic( + code(ips::repository_error::publisher_not_found), + help("Check that the publisher name is correct and exists in the repository") + )] + PublisherNotFound(String), + + #[error("publisher {0} already exists")] + #[diagnostic( + code(ips::repository_error::publisher_exists), + help("Use a different publisher name or remove the existing publisher first") + )] + PublisherExists(String), + + #[error("failed to read repository configuration: {0}")] + #[diagnostic( + code(ips::repository_error::config_read), + help("Check that the repository configuration file exists and is valid") + )] + ConfigReadError(String), + + #[error("failed to write repository configuration: {0}")] + #[diagnostic( + code(ips::repository_error::config_write), + help("Check that the repository directory is writable") + )] + ConfigWriteError(String), + + #[error("failed to create directory: {0}")] + #[diagnostic( + code(ips::repository_error::directory_create), + help("Check that the parent directory exists and is writable") + )] + DirectoryCreateError(String), + + #[error("failed to read file: {0}")] + #[diagnostic( + code(ips::repository_error::file_read), + help("Check that the file exists and is readable") + )] + FileReadError(String), + + #[error("failed to write file: {0}")] + #[diagnostic( + code(ips::repository_error::file_write), + help("Check that the directory is writable") + )] + FileWriteError(String), + + #[error("failed to parse JSON: {0}")] + #[diagnostic( + code(ips::repository_error::json_parse), + help("Check that the JSON file is valid") + )] + JsonParseError(String), + + #[error("failed to serialize JSON: {0}")] + #[diagnostic( + code(ips::repository_error::json_serialize), + help("This is likely a bug in the code") + )] + JsonSerializeError(String), + + #[error("I/O error: {0}")] + #[diagnostic( + code(ips::repository_error::io), + help("Check system resources and permissions") + )] + IoError(#[from] io::Error), + + #[error("other error: {0}")] + #[diagnostic( + code(ips::repository_error::other), + help("See error message for details") + )] + Other(String), + + #[error("JSON error: {0}")] + #[diagnostic( + code(ips::repository_error::json_error), + help("Check the JSON format and try again") + )] + JsonError(String), + + #[error("digest error: {0}")] + #[diagnostic( + code(ips::repository_error::digest_error), + help("Check the digest format and try again") + )] + DigestError(String), + + #[error(transparent)] + #[diagnostic(transparent)] + ActionError(#[from] ActionError), + + #[error(transparent)] + #[diagnostic(transparent)] + CatalogError(#[from] catalog::CatalogError), + + #[error("path prefix error: {0}")] + #[diagnostic( + code(ips::repository_error::path_prefix), + help("Check that the path is valid and within the expected directory") + )] + PathPrefixError(String), +} + +// Implement From for common error types +impl From for RepositoryError { + fn from(err: serde_json::Error) -> Self { + RepositoryError::JsonError(err.to_string()) + } +} + +impl From for RepositoryError { + fn from(err: DigestError) -> Self { + RepositoryError::DigestError(err.to_string()) + } +} + + +impl From for RepositoryError { + fn from(err: StripPrefixError) -> Self { + RepositoryError::PathPrefixError(err.to_string()) + } +} + mod catalog; mod file_backend; @@ -13,9 +163,11 @@ mod rest_backend; #[cfg(test)] mod tests; -pub use catalog::{CatalogAttrs, CatalogManager, CatalogOperationType, CatalogPart, UpdateLog}; +pub use catalog::{CatalogAttrs, CatalogError, CatalogManager, CatalogOperationType, CatalogPart, UpdateLog}; pub use file_backend::FileBackend; pub use rest_backend::RestBackend; +use crate::actions::ActionError; +use crate::digest::DigestError; /// Repository configuration filename pub const REPOSITORY_CONFIG_FILENAME: &str = "pkg6.repository"; @@ -77,12 +229,12 @@ impl Default for RepositoryVersion { } impl std::convert::TryFrom for RepositoryVersion { - type Error = anyhow::Error; + type Error = RepositoryError; - fn try_from(value: u32) -> Result { + fn try_from(value: u32) -> std::result::Result { match value { 4 => Ok(RepositoryVersion::V4), - _ => Err(anyhow::anyhow!("Unsupported repository version: {}", value)), + _ => Err(RepositoryError::UnsupportedVersion(value)), } } } diff --git a/libips/src/repository/rest_backend.rs b/libips/src/repository/rest_backend.rs index 0556d32..0d38d03 100644 --- a/libips/src/repository/rest_backend.rs +++ b/libips/src/repository/rest_backend.rs @@ -3,12 +3,11 @@ // MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use anyhow::{anyhow, Result}; use std::path::{Path, PathBuf}; use super::{ PackageContents, PackageInfo, PublisherInfo, ReadableRepository, RepositoryConfig, - RepositoryInfo, RepositoryVersion, WritableRepository, + RepositoryError, RepositoryInfo, RepositoryVersion, Result, WritableRepository, }; /// Repository implementation that uses a REST API @@ -97,7 +96,7 @@ impl WritableRepository for RestBackend { // 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)); + return Err(RepositoryError::PublisherNotFound(pub_name.to_string())); } vec![pub_name.to_string()] } else { @@ -130,7 +129,7 @@ impl WritableRepository for RestBackend { // 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)); + return Err(RepositoryError::PublisherNotFound(pub_name.to_string())); } vec![pub_name.to_string()] } else { @@ -162,7 +161,7 @@ impl WritableRepository for RestBackend { // Check if the publisher exists if !self.config.publishers.contains(&publisher.to_string()) { - return Err(anyhow!("Publisher does not exist: {}", publisher)); + return Err(RepositoryError::PublisherNotFound(publisher.to_string())); } // Set the default publisher @@ -199,7 +198,7 @@ impl WritableRepository for RestBackend { // Check if the publisher exists if !self.config.publishers.contains(&publisher.to_string()) { - return Err(anyhow!("Publisher does not exist: {}", publisher)); + return Err(RepositoryError::PublisherNotFound(publisher.to_string())); } // Create the property key in the format "publisher/property" @@ -274,7 +273,7 @@ impl ReadableRepository for RestBackend { // 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)); + return Err(RepositoryError::PublisherNotFound(pub_name.to_string())); } vec![pub_name.to_string()] } else { diff --git a/libips/src/repository/tests.rs b/libips/src/repository/tests.rs index eeb4325..ff24fb8 100644 --- a/libips/src/repository/tests.rs +++ b/libips/src/repository/tests.rs @@ -8,8 +8,8 @@ mod tests { use crate::actions::Manifest; use crate::fmri::Fmri; use crate::repository::{ - CatalogManager, FileBackend, ReadableRepository, RepositoryVersion, WritableRepository, - REPOSITORY_CONFIG_FILENAME, + CatalogManager, FileBackend, ReadableRepository, RepositoryError, RepositoryVersion, Result, + WritableRepository, REPOSITORY_CONFIG_FILENAME, }; use std::fs; use std::path::PathBuf; @@ -72,7 +72,7 @@ mod tests { manifest_path: &PathBuf, prototype_dir: &PathBuf, publisher: &str, - ) -> Result<(), anyhow::Error> { + ) -> Result<()> { println!( "Publishing package from manifest: {}", manifest_path.display() @@ -83,18 +83,16 @@ mod tests { // Check if the manifest file exists if !manifest_path.exists() { println!("Error: Manifest file does not exist"); - return Err(anyhow::anyhow!( - "Manifest file does not exist: {}", - manifest_path.display() + return Err(RepositoryError::FileReadError( + format!("Manifest file does not exist: {}", manifest_path.display()) )); } // Check if the prototype directory exists if !prototype_dir.exists() { println!("Error: Prototype directory does not exist"); - return Err(anyhow::anyhow!( - "Prototype directory does not exist: {}", - prototype_dir.display() + return Err(RepositoryError::NotFound( + format!("Prototype directory does not exist: {}", prototype_dir.display()) )); } diff --git a/libips/src/test_json_manifest.rs b/libips/src/test_json_manifest.rs new file mode 100644 index 0000000..4f1efcf --- /dev/null +++ b/libips/src/test_json_manifest.rs @@ -0,0 +1,60 @@ +#[cfg(test)] +mod tests { + use crate::actions::Manifest; + use std::fs::File; + use std::io::Write; + use tempfile::tempdir; + + #[test] + fn test_parse_json_manifest() { + // Create a temporary directory for the test + let temp_dir = tempdir().unwrap(); + let manifest_path = temp_dir.path().join("test_manifest.json"); + + // Create a simple manifest + let mut manifest = Manifest::new(); + + // Add some attributes + let mut attr = crate::actions::Attr::default(); + attr.key = "pkg.fmri".to_string(); + attr.values = vec!["pkg://test/example@1.0.0".to_string()]; + manifest.attributes.push(attr); + + // Serialize the manifest to JSON + let manifest_json = serde_json::to_string_pretty(&manifest).unwrap(); + + // Write the JSON to a file + let mut file = File::create(&manifest_path).unwrap(); + file.write_all(manifest_json.as_bytes()).unwrap(); + + // Parse the JSON manifest + let parsed_manifest = Manifest::parse_file(&manifest_path).unwrap(); + + // Verify that the parsed manifest matches the original + assert_eq!(parsed_manifest.attributes.len(), 1); + assert_eq!(parsed_manifest.attributes[0].key, "pkg.fmri"); + assert_eq!(parsed_manifest.attributes[0].values[0], "pkg://test/example@1.0.0"); + } + + #[test] + fn test_parse_string_manifest() { + // Create a temporary directory for the test + let temp_dir = tempdir().unwrap(); + let manifest_path = temp_dir.path().join("test_manifest.p5m"); + + // Create a simple string-formatted manifest + let manifest_string = "set name=pkg.fmri value=pkg://test/example@1.0.0\n"; + + // Write the string to a file + let mut file = File::create(&manifest_path).unwrap(); + file.write_all(manifest_string.as_bytes()).unwrap(); + + // Parse the string manifest + let parsed_manifest = Manifest::parse_file(&manifest_path).unwrap(); + + // Verify that the parsed manifest has the expected attributes + assert_eq!(parsed_manifest.attributes.len(), 1); + assert_eq!(parsed_manifest.attributes[0].key, "name"); + assert_eq!(parsed_manifest.attributes[0].values[0], "pkg.fmri"); + } +} \ No newline at end of file