diff --git a/.envrc b/.envrc
deleted file mode 100644
index 3550a30..0000000
--- a/.envrc
+++ /dev/null
@@ -1 +0,0 @@
-use flake
diff --git a/.gitignore b/.gitignore
deleted file mode 100755
index 6d5293e..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-/target
-.idea
-.direnv
-
-examples/workspace/*
-examples/repository/*
-!examples/workspace/mlc.toml
-!examples/repository/mlc.toml
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 5d7fb48..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-image: "rust:latest"
-
-cargo:version:
- script:
- - rustc --version && cargo --version
-
-cargo:clippy:
- before_script:
- - rustup component add clippy
- script:
- - cargo clippy --no-deps -- -D clippy::all
-
-cargo:fmt:
- before_script:
- - rustup component add rustfmt
- script:
- - cargo fmt --check
diff --git a/Buildfile b/Buildfile
new file mode 100644
index 0000000..25eb1bd
--- /dev/null
+++ b/Buildfile
@@ -0,0 +1,24 @@
+# Key:
+# {{ image }}: will be replaced with `base.image` from .mlc/config.toml
+# {{ pkg }} : will be replaced with the name of the package being built
+# Post-build, the contents of /out will be copied to the host at `repo.out` from .mlc/config.toml
+
+FROM {{ image }}
+
+RUN mkdir /out
+COPY {{ pkg }} /tmp/{{ pkg }}
+
+RUN pacman -Syu --noconfirm
+RUN pacman -S --noconfirm --needed base-devel
+
+RUN useradd -m -G wheel build-user
+RUN echo '%wheel ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
+RUN chown -R build-user /tmp/{{ pkg }}
+
+USER build-user
+WORKDIR /tmp/{{ pkg }}
+
+RUN makepkg -s {{ flags }} --noconfirm
+
+USER root
+RUN cp *.pkg.tar.* /out
diff --git a/Cargo.lock b/Cargo.lock
index db7d31e..0bea699 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,40 +4,96 @@ version = 3
[[package]]
name = "Malachite"
-version = "2.1.1"
+version = "3.0.0"
dependencies = [
+ "cargo_toml",
"clap",
- "colored",
- "crossterm",
+ "color-eyre",
+ "compress-tools",
+ "fs_extra",
+ "futures-util",
+ "glob",
+ "gpgme",
+ "i18n-embed",
+ "i18n-embed-fl",
+ "lazy_static",
"libc",
- "mimalloc",
- "regex",
- "rm_rf",
+ "liquid",
+ "names",
+ "podman-api",
+ "rust-embed",
"serde",
- "serde_derive",
- "spinoff",
"tabled",
- "toml",
+ "tar",
+ "thiserror",
+ "tokio",
+ "toml 0.7.2",
+]
+
+[[package]]
+name = "addr2line"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
]
[[package]]
name = "ansi-str"
-version = "0.3.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e50acdf02a3ac61856d5c8d576a8b5fb452a6549f667ca29fefaa18c2cd05135"
+checksum = "84252a7e1a0df81706ce70bbad85ed1e4916448a4093ccd52dd98c6a44a477cd"
dependencies = [
"ansitok",
]
[[package]]
name = "ansitok"
-version = "0.1.0"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2c6eb31f539d8fc1df948eb26452d6c781be4c9883663e7acb258644b71d5b1"
+checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83"
dependencies = [
"nom",
+ "vte",
]
+[[package]]
+name = "anymap2"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
+
+[[package]]
+name = "arc-swap"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
+
[[package]]
name = "arrayvec"
version = "0.5.2"
@@ -45,21 +101,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
-name = "atty"
-version = "0.2.14"
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
dependencies = [
- "hermit-abi",
+ "addr2line",
+ "cc",
+ "cfg-if",
"libc",
- "winapi",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
]
[[package]]
-name = "autocfg"
-version = "1.1.0"
+name = "base64"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bitflags"
@@ -67,17 +133,81 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "build-rs"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b00b8763668c99f8d9101b8a0dd82106f58265464531a79b2cef0d9a30c17dd2"
+
+[[package]]
+name = "bumpalo"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
+
[[package]]
name = "bytecount"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "bytes"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "cargo_toml"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f83bc2e401ed041b7057345ebc488c005efa0341d5541ce7004d30458d0090b"
+dependencies = [
+ "serde",
+ "toml 0.7.2",
+]
+
[[package]]
name = "cc"
-version = "1.0.78"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cfg-expr"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
+checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa"
+dependencies = [
+ "smallvec",
+]
[[package]]
name = "cfg-if"
@@ -85,28 +215,43 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "chrono"
+version = "0.4.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-integer",
+ "num-traits",
+ "serde",
+ "time 0.1.45",
+ "wasm-bindgen",
+ "winapi",
+]
+
[[package]]
name = "clap"
-version = "3.2.23"
+version = "4.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
+checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
dependencies = [
- "atty",
"bitflags",
"clap_derive",
"clap_lex",
- "indexmap",
+ "is-terminal",
"once_cell",
"strsim",
"termcolor",
- "textwrap",
+ "terminal_size",
]
[[package]]
name = "clap_derive"
-version = "3.2.18"
+version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
+checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
dependencies = [
"heck",
"proc-macro-error",
@@ -117,513 +262,2070 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.2.4"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
dependencies = [
"os_str_bytes",
]
[[package]]
-name = "colored"
-version = "2.0.0"
+name = "codespan-reporting"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
- "atty",
- "lazy_static",
- "winapi",
+ "termcolor",
+ "unicode-width",
]
[[package]]
-name = "crossterm"
-version = "0.25.0"
+name = "color-eyre"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
+checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
dependencies = [
- "bitflags",
- "crossterm_winapi",
- "libc",
- "mio",
- "parking_lot",
- "signal-hook",
- "signal-hook-mio",
- "winapi",
+ "backtrace",
+ "color-spantrace",
+ "eyre",
+ "indenter",
+ "once_cell",
+ "owo-colors",
+ "tracing-error",
+ "url",
]
[[package]]
-name = "crossterm_winapi"
-version = "0.9.0"
+name = "color-spantrace"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
+checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce"
dependencies = [
- "winapi",
+ "once_cell",
+ "owo-colors",
+ "tracing-core",
+ "tracing-error",
]
[[package]]
-name = "fnv"
-version = "1.0.7"
+name = "compress-tools"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+checksum = "3a3b1511783270e75d95749c851008a01fba662a8136e488d29d2b3a51a56e08"
+dependencies = [
+ "derive_more",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
[[package]]
-name = "hashbrown"
-version = "0.12.3"
+name = "containers-api"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+checksum = "56082e32f18a6d60f06c736b49e234deffb93b13cb87091a39e0dec053d03819"
+dependencies = [
+ "chrono",
+ "flate2",
+ "futures-util",
+ "http",
+ "hyper",
+ "hyperlocal",
+ "log",
+ "mime",
+ "paste",
+ "pin-project 1.0.12",
+ "serde",
+ "serde_json",
+ "tar",
+ "thiserror",
+ "tokio",
+ "url",
+]
[[package]]
-name = "heck"
-version = "0.4.0"
+name = "conv"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
+dependencies = [
+ "custom_derive",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
-name = "hermit-abi"
-version = "0.1.19"
+name = "cpufeatures"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
dependencies = [
"libc",
]
[[package]]
-name = "indexmap"
-version = "1.9.2"
+name = "crc32fast"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
- "autocfg",
- "hashbrown",
+ "cfg-if",
]
[[package]]
-name = "lazy_static"
-version = "1.4.0"
+name = "crypto-common"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
[[package]]
-name = "libc"
-version = "0.2.139"
+name = "cstr-argument"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
+checksum = "b6bd9c8e659a473bce955ae5c35b116af38af11a7acb0b480e01f3ed348aeb40"
+dependencies = [
+ "cfg-if",
+ "memchr",
+]
+
+[[package]]
+name = "custom_derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
[[package]]
-name = "libmimalloc-sys"
-version = "0.1.28"
+name = "cxx"
+version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04d1c67deb83e6b75fa4fe3309e09cfeade12e7721d95322af500d3814ea60c9"
+checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62"
dependencies = [
"cc",
- "libc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
]
[[package]]
-name = "lock_api"
-version = "0.4.9"
+name = "cxx-build"
+version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690"
dependencies = [
- "autocfg",
- "scopeguard",
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
]
[[package]]
-name = "log"
-version = "0.4.17"
+name = "cxxbridge-flags"
+version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "dashmap"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
dependencies = [
"cfg-if",
+ "hashbrown",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
]
[[package]]
-name = "maplit"
-version = "1.0.2"
+name = "derive_more"
+version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
[[package]]
-name = "memchr"
-version = "2.5.0"
+name = "digest"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
[[package]]
-name = "mimalloc"
-version = "0.1.32"
+name = "displaydoc"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b2374e2999959a7b583e1811a1ddbf1d3a4b9496eceb9746f1192a59d871eca"
+checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
dependencies = [
- "libmimalloc-sys",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "minimal-lexical"
-version = "0.2.1"
+name = "doc-comment"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
-name = "mio"
-version = "0.8.5"
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
+name = "errno"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
+ "errno-dragonfly",
"libc",
- "log",
- "wasi",
- "windows-sys",
+ "winapi",
]
[[package]]
-name = "nom"
-version = "7.1.1"
+name = "errno-dragonfly"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
- "memchr",
- "minimal-lexical",
+ "cc",
+ "libc",
]
[[package]]
-name = "once_cell"
-version = "1.16.0"
+name = "eyre"
+version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
+dependencies = [
+ "indenter",
+ "once_cell",
+]
[[package]]
-name = "os_str_bytes"
-version = "6.4.1"
+name = "filetime"
+version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
+checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "windows-sys 0.45.0",
+]
[[package]]
-name = "papergrid"
-version = "0.5.1"
+name = "find-crate"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453cf71f2a37af495a1a124bf30d4d7469cfbea58e9f2479be9d222396a518a2"
+checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2"
dependencies = [
- "ansi-str",
- "bytecount",
- "fnv",
- "strip-ansi-escapes",
- "unicode-width",
+ "toml 0.5.11",
]
[[package]]
-name = "parking_lot"
-version = "0.12.1"
+name = "flate2"
+version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
- "lock_api",
- "parking_lot_core",
+ "crc32fast",
+ "miniz_oxide",
]
[[package]]
-name = "parking_lot_core"
-version = "0.9.5"
+name = "fluent"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
+checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7"
dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
+ "fluent-bundle",
+ "unic-langid",
+]
+
+[[package]]
+name = "fluent-bundle"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd"
+dependencies = [
+ "fluent-langneg",
+ "fluent-syntax",
+ "intl-memoizer",
+ "intl_pluralrules",
+ "rustc-hash",
+ "self_cell",
"smallvec",
- "windows-sys",
+ "unic-langid",
+]
+
+[[package]]
+name = "fluent-langneg"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94"
+dependencies = [
+ "unic-langid",
+]
+
+[[package]]
+name = "fluent-syntax"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
+[[package]]
+name = "futures"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
+
+[[package]]
+name = "futures-task"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
+
+[[package]]
+name = "futures-util"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "futures_codec"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce54d63f8b0c75023ed920d46fd71d0cbbb830b0ee012726b5b4f506fb6dea5b"
+dependencies = [
+ "bytes 0.5.6",
+ "futures",
+ "memchr",
+ "pin-project 0.4.30",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "gimli"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "gpg-error"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89aaeddbfb92313378c58e98abadaaa34082b3855f1d455576eeeda08bd592c"
+dependencies = [
+ "libgpg-error-sys",
+]
+
+[[package]]
+name = "gpgme"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57539732fbe58eacdb984734b72b470ed0bca3ab7a49813271878567025ac44f"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "conv",
+ "cstr-argument",
+ "gpg-error",
+ "gpgme-sys",
+ "libc",
+ "memoffset",
+ "once_cell",
+ "smallvec",
+ "static_assertions",
+]
+
+[[package]]
+name = "gpgme-sys"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "509223d659c06e4a26229437d6ac917723f02d31917c86c6ecd50e8369741cf7"
+dependencies = [
+ "build-rs",
+ "libc",
+ "libgpg-error-sys",
+ "system-deps",
+ "winreg",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes 1.4.0",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes 1.4.0",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
+[[package]]
+name = "hyper"
+version = "0.14.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
+dependencies = [
+ "bytes 1.4.0",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyperlocal"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c"
+dependencies = [
+ "futures-util",
+ "hex",
+ "hyper",
+ "pin-project 1.0.12",
+ "tokio",
+]
+
+[[package]]
+name = "i18n-config"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d9f93ceee6543011739bc81699b5e0cf1f23f3a80364649b6d80de8636bc8df"
+dependencies = [
+ "log",
+ "serde",
+ "serde_derive",
+ "thiserror",
+ "toml 0.5.11",
+ "unic-langid",
+]
+
+[[package]]
+name = "i18n-embed"
+version = "0.13.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2653dd1a8be0726315603f1c180b29f90e5b2a58f8b943d949d5170d9ad81101"
+dependencies = [
+ "arc-swap",
+ "fluent",
+ "fluent-langneg",
+ "fluent-syntax",
+ "i18n-embed-impl",
+ "intl-memoizer",
+ "lazy_static",
+ "locale_config",
+ "log",
+ "parking_lot",
+ "rust-embed",
+ "thiserror",
+ "unic-langid",
+ "walkdir",
+]
+
+[[package]]
+name = "i18n-embed-fl"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a425b9bbdc2e4cd797a2a79528662cb61894bd36db582e48da2c56c28eb727cd"
+dependencies = [
+ "dashmap",
+ "find-crate",
+ "fluent",
+ "fluent-syntax",
+ "i18n-config",
+ "i18n-embed",
+ "lazy_static",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+ "unic-langid",
+]
+
+[[package]]
+name = "i18n-embed-impl"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0db2330e035808eb064afb67e6743ddce353763af3e0f2bdfc2476e00ce76136"
+dependencies = [
+ "find-crate",
+ "i18n-config",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
+[[package]]
+name = "idna"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "indenter"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
+
+[[package]]
+name = "indexmap"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "intl-memoizer"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f"
+dependencies = [
+ "type-map",
+ "unic-langid",
+]
+
+[[package]]
+name = "intl_pluralrules"
+version = "7.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972"
+dependencies = [
+ "unic-langid",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
+dependencies = [
+ "libc",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
+
+[[package]]
+name = "js-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "kstring"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747"
+dependencies = [
+ "serde",
+ "static_assertions",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.139"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
+
+[[package]]
+name = "libgpg-error-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c97079310f39c835d3bd73578379d040f779614bb331c7ffbb6630fee6420290"
+dependencies = [
+ "build-rs",
+ "system-deps",
+ "winreg",
+]
+
+[[package]]
+name = "link-cplusplus"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+
+[[package]]
+name = "liquid"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00f55b9db2305857de3b3ceaa0e75cb51a76aaec793875fe152e139cb8fed05c"
+dependencies = [
+ "doc-comment",
+ "liquid-core",
+ "liquid-derive",
+ "liquid-lib",
+ "serde",
+]
+
+[[package]]
+name = "liquid-core"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a93764837aeac37f14b74708cd88a44d82edfa9ad2b1bcd9a3b4d8802fdd9f98"
+dependencies = [
+ "anymap2",
+ "itertools",
+ "kstring",
+ "liquid-derive",
+ "num-traits",
+ "pest",
+ "pest_derive",
+ "regex",
+ "serde",
+ "time 0.3.19",
+]
+
+[[package]]
+name = "liquid-derive"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "926454345f103e8433833077acdbfaa7c3e4b90788d585a8358f02f0b8f5a469"
+dependencies = [
+ "proc-macro2",
+ "proc-quote",
+ "syn",
+]
+
+[[package]]
+name = "liquid-lib"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd06ca30ae026d26ee7fa8596f9590959e2d3726bc5a0f16a21ac4f050ec83c0"
+dependencies = [
+ "itertools",
+ "liquid-core",
+ "once_cell",
+ "percent-encoding",
+ "regex",
+ "time 0.3.19",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "locale_config"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934"
+dependencies = [
+ "lazy_static",
+ "objc",
+ "objc-foundation",
+ "regex",
+ "winapi",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "mime"
+version = "0.3.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
+
+[[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.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
+dependencies = [
+ "libc",
+ "log",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "names"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc"
+dependencies = [
+ "rand",
+]
+
+[[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 = "nom8"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+dependencies = [
+ "hermit-abi 0.2.6",
+ "libc",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "objc-foundation"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
+dependencies = [
+ "block",
+ "objc",
+ "objc_id",
+]
+
+[[package]]
+name = "objc_id"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
+dependencies = [
+ "objc",
+]
+
+[[package]]
+name = "object"
+version = "0.30.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
+
+[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+
+[[package]]
+name = "papergrid"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1526bb6aa9f10ec339fb10360f22c57edf81d5678d0278e93bc12a47ffbe4b01"
+dependencies = [
+ "ansi-str",
+ "ansitok",
+ "bytecount",
+ "fnv",
+ "unicode-width",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
+
+[[package]]
+name = "percent-encoding"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+
+[[package]]
+name = "pest"
+version = "2.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660"
+dependencies = [
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616"
+dependencies = [
+ "once_cell",
+ "pest",
+ "sha2",
+]
+
+[[package]]
+name = "pin-project"
+version = "0.4.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a"
+dependencies = [
+ "pin-project-internal 0.4.30",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
+dependencies = [
+ "pin-project-internal 1.0.12",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "0.4.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+
+[[package]]
+name = "podman-api"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64b8a428a3381f5e1fcfa1fb94092017f193c75f2d7959bc799ee82dadf0f72d"
+dependencies = [
+ "base64",
+ "byteorder",
+ "bytes 1.4.0",
+ "chrono",
+ "containers-api",
+ "flate2",
+ "futures-util",
+ "futures_codec",
+ "log",
+ "paste",
+ "podman-api-stubs",
+ "serde",
+ "serde_json",
+ "tar",
+ "thiserror",
+ "tokio",
+ "url",
+]
+
+[[package]]
+name = "podman-api-stubs"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd9a63943c0c6e69efcd252ad61f4914270b7e3fec170e6a04ffdb2fc8dcd6a9"
+dependencies = [
+ "chrono",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.20+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proc-quote"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e84ab161de78c915302ca325a19bee6df272800e2ae1a43fe3ef430bab2a100"
+dependencies = [
+ "proc-macro-hack",
+ "proc-macro2",
+ "proc-quote-impl",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "proc-quote-impl"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb3ec628b063cdbcf316e06a8b8c1a541d28fa6c0a8eacd2bfb2b7f49e88aa0"
+dependencies = [
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+dependencies = [
+ "proc-macro2",
+]
+
+[[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",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+
+[[package]]
+name = "rust-embed"
+version = "6.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1"
+dependencies = [
+ "rust-embed-impl",
+ "rust-embed-utils",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-impl"
+version = "6.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rust-embed-utils",
+ "syn",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-utils"
+version = "7.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054"
+dependencies = [
+ "sha2",
+ "walkdir",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustix"
+version = "0.36.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "scratch"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
+
+[[package]]
+name = "self_cell"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af"
+
+[[package]]
+name = "serde"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
+name = "socket2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml 0.5.11",
+ "version-compare",
+]
+
+[[package]]
+name = "tabled"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c3ee73732ffceaea7b8f6b719ce3bb17f253fa27461ffeaf568ebd0cdb4b85"
+dependencies = [
+ "ansi-str",
+ "papergrid",
+ "tabled_derive",
+ "unicode-width",
]
[[package]]
-name = "proc-macro-error"
-version = "1.0.4"
+name = "tabled_derive"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+checksum = "beca1b4eaceb4f2755df858b88d9b9315b7ccfd1ffd0d7a48a52602301f01a57"
dependencies = [
- "proc-macro-error-attr",
+ "heck",
+ "proc-macro-error",
"proc-macro2",
"quote",
"syn",
- "version_check",
]
[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
+name = "tar"
+version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
+ "filetime",
+ "libc",
+ "xattr",
]
[[package]]
-name = "proc-macro2"
-version = "1.0.49"
+name = "termcolor"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
- "unicode-ident",
+ "winapi-util",
]
[[package]]
-name = "psm"
-version = "0.1.21"
+name = "terminal_size"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874"
+checksum = "4c9afddd2cec1c0909f06b00ef33f94ab2cc0578c4a610aa208ddfec8aa2b43a"
dependencies = [
- "cc",
+ "rustix",
+ "windows-sys 0.45.0",
]
[[package]]
-name = "quote"
-version = "1.0.23"
+name = "thiserror"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
- "proc-macro2",
+ "thiserror-impl",
]
[[package]]
-name = "redox_syscall"
-version = "0.2.16"
+name = "thiserror-impl"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
- "bitflags",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "regex"
-version = "1.7.0"
+name = "thread_local"
+version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
- "regex-syntax",
+ "cfg-if",
+ "once_cell",
]
[[package]]
-name = "regex-syntax"
-version = "0.6.28"
+name = "time"
+version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
+dependencies = [
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi",
+]
[[package]]
-name = "rm_rf"
-version = "0.6.2"
+name = "time"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3443b7a35aa12ed2e99edfc0ecbefe6a53b4848305cc83e29981dfa1aea1f71e"
+checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2"
dependencies = [
- "stacker",
+ "itoa",
+ "serde",
+ "time-core",
+ "time-macros",
]
[[package]]
-name = "rustversion"
-version = "1.0.11"
+name = "time-core"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
+checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
-name = "scopeguard"
-version = "1.1.0"
+name = "time-macros"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c"
+dependencies = [
+ "time-core",
+]
[[package]]
-name = "serde"
-version = "1.0.151"
+name = "tinystr"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
+checksum = "7ac3f5b6856e931e15e07b478e98c8045239829a65f9156d4fa7e7788197a5ef"
+dependencies = [
+ "displaydoc",
+]
[[package]]
-name = "serde_derive"
-version = "1.0.151"
+name = "tinyvec"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "tinyvec_macros",
]
[[package]]
-name = "signal-hook"
-version = "0.3.14"
+name = "tinyvec_macros"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
-dependencies = [
- "libc",
- "signal-hook-registry",
-]
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
-name = "signal-hook-mio"
-version = "0.2.3"
+name = "tokio"
+version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
+checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
dependencies = [
+ "autocfg",
+ "bytes 1.4.0",
"libc",
+ "memchr",
"mio",
- "signal-hook",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.42.0",
]
[[package]]
-name = "signal-hook-registry"
-version = "1.4.0"
+name = "tokio-macros"
+version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
- "libc",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "smallvec"
-version = "1.10.0"
+name = "toml"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
[[package]]
-name = "spinoff"
-version = "0.5.4"
+name = "toml"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "812db6f40551bdcdb10e1d2070ec33f69805d2bfb7e59426c7d14e7e1b4194dd"
+checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
dependencies = [
- "colored",
- "maplit",
- "once_cell",
- "strum",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
]
[[package]]
-name = "stacker"
-version = "0.1.15"
+name = "toml_datetime"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
+checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
dependencies = [
- "cc",
- "cfg-if",
- "libc",
- "psm",
- "winapi",
+ "serde",
]
[[package]]
-name = "strip-ansi-escapes"
-version = "0.1.1"
+name = "toml_edit"
+version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8"
+checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5"
dependencies = [
- "vte",
+ "indexmap",
+ "nom8",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
]
[[package]]
-name = "strsim"
-version = "0.10.0"
+name = "tower-service"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
-name = "strum"
-version = "0.24.1"
+name = "tracing"
+version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
- "strum_macros",
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-core",
]
[[package]]
-name = "strum_macros"
-version = "0.24.3"
+name = "tracing-core"
+version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
+checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn",
+ "once_cell",
+ "valuable",
]
[[package]]
-name = "syn"
-version = "1.0.107"
+name = "tracing-error"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
+ "tracing",
+ "tracing-subscriber",
]
[[package]]
-name = "tabled"
-version = "0.8.0"
+name = "tracing-subscriber"
+version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5b2f8c37d26d87d2252187b0a45ea3cbf42baca10377c7e7eaaa2800fa9bf97"
+checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
dependencies = [
- "ansi-str",
- "papergrid",
- "tabled_derive",
- "unicode-width",
+ "sharded-slab",
+ "thread_local",
+ "tracing-core",
]
[[package]]
-name = "tabled_derive"
+name = "try-lock"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
+
+[[package]]
+name = "type-map"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9ee618502f497abf593e1c5c9577f34775b111480009ffccd7ad70d23fcaba8"
+checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46"
dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
+ "rustc-hash",
]
[[package]]
-name = "termcolor"
-version = "1.1.3"
+name = "typenum"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
-dependencies = [
- "winapi-util",
-]
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
-name = "textwrap"
-version = "0.16.0"
+name = "ucd-trie"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
[[package]]
-name = "toml"
-version = "0.5.10"
+name = "unic-langid"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f"
+dependencies = [
+ "unic-langid-impl",
+]
+
+[[package]]
+name = "unic-langid-impl"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
+checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff"
dependencies = [
"serde",
+ "tinystr",
]
+[[package]]
+name = "unicode-bidi"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
+
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+[[package]]
+name = "url"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
[[package]]
name = "utf8parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version-compare"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
+
[[package]]
name = "version_check"
version = "0.9.4"
@@ -651,12 +2353,93 @@ dependencies = [
"quote",
]
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
+dependencies = [
+ "log",
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
+
[[package]]
name = "winapi"
version = "0.3.9"
@@ -703,44 +2486,86 @@ dependencies = [
"windows_x86_64_msvc",
]
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
[[package]]
name = "windows_i686_gnu"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
[[package]]
name = "windows_i686_msvc"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.42.0"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
+
+[[package]]
+name = "winreg"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "xattr"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
+dependencies = [
+ "libc",
+]
diff --git a/Cargo.toml b/Cargo.toml
old mode 100755
new mode 100644
index 5b72686..d1e3bdd
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,40 +1,46 @@
[package]
name = "Malachite"
-version = "2.1.1"
-authors = ["michal "]
+version = "3.0.0"
edition = "2021"
-description = "Packaging tool for pacman repositories"
-repository = "https://github.com/crystal-linux/malachite"
-license-file = "LICENSE"
-keywords = ["pacman", "repository", "packaging"]
-categories = ["filesystem", "development-tools"]
+
+[package.metadata]
+codename = "Mi Goreng"
[[bin]]
name = "mlc"
path = "src/main.rs"
-[profile.release]
-incremental = true
-debug = false
-lto = "fat"
-codegen-units = 1
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-
-clap = { version = "3.2.8", features = ["derive", "suggestions"] }
-toml = { version = "0.5.9", default-features = false }
-serde = { version = "1.0.139", default-features = false }
-serde_derive = { version = "1.0.139", default-features = false }
-libc = { version = "0.2.126", default-features = false }
-colored = { version = "2.0.0", default-features = false }
-tabled = { version = "0.8.0", default-features = false, features = [
+libc = "0.2.137"
+clap = { version = "4.0.18", features = ["derive", "wrap_help"] }
+rust-embed = "6.4.2"
+thiserror = "1.0.37"
+i18n-embed = { version = "0.13.4", features = [
+ "fluent-system",
+ "desktop-requester",
+] }
+i18n-embed-fl = "0.6.4"
+color-eyre = { version = "0.6.2", features = ["issue-url", "url"] }
+lazy_static = "1.4.0"
+toml = "0.7.2"
+serde = { version = "1.0.147", default-features = false, features = [
"derive",
- "color",
+ "serde_derive",
] }
-crossterm = { version = "0.25.0", default-features = false }
-regex = { version = "1.6.0", default-features = false, features = ["std"] }
-spinoff = { version = "0.5.4", default-features = false }
-rm_rf = { version = "0.6.2", default-features = false }
+podman-api = "0.8.0"
+names = { version = "0.14.0", default-features = false }
+futures-util = "0.3.25"
+tokio = { version = "1.21.2", features = ["full"] }
+fs_extra = "1.2.0"
+tar = "0.4.38"
+glob = "0.3.0"
+compress-tools = "0.14.0"
+gpgme = "0.11.0"
+tabled = { version = "0.10.0", features = ["color"] }
+liquid = "0.26.0"
-[target.'cfg(target_os = "linux")'.dependencies]
-mimalloc = { version = "0.1.29" }
+[build-dependencies]
+cargo_toml = "0.15.2"
+serde = { version = "1.0.147", default-features = false, features = ["derive"] }
diff --git a/Generatefile b/Generatefile
new file mode 100644
index 0000000..162917a
--- /dev/null
+++ b/Generatefile
@@ -0,0 +1,25 @@
+# Key:
+# {{ image }}: will be replaced with `base.image` from .mlc/config.toml
+# {{ name }} : will be replaced with `repo.name` from .mlc/config.toml
+# Post-build, the contents of /repo will be copied to the host at `repo.repo` from .mlc/config.toml
+# If `repo.security` is set to true, all resultant *.pkg.tar.* files will be GPG signed by the host
+
+FROM {{ image }}
+
+RUN mkdir /{{ repo }}
+COPY out /tmp/{{ name }}
+
+RUN pacman -Syu --noconfirm
+RUN pacman -S --noconfirm --needed pacman-contrib
+
+RUN useradd -m -G wheel generate-user
+RUN echo '%wheel ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
+RUN chown -R generate-user /tmp/{{ name }}
+
+USER generate-user
+WORKDIR /tmp/{{ name }}
+
+RUN repo-add {{ name }}.db.tar.gz *.pkg.tar.*
+
+USER root
+RUN cp /tmp/{{ name }}/* /{{ repo }}
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index f288702..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
diff --git a/README.md b/README.md
deleted file mode 100644
index 5e7f134..0000000
--- a/README.md
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-Malachite
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Malachite is a simple yet useful workspace and local repository management tool, made for packagers of Arch Linux based distributions.
-
-
-
-
-
-### Basic Usage Guide
-
-| Action | Command |
-|--------------------------------------------------------|-------------------------------------------|
-| Build a package | mlc build \ [all if left empty] |
-| Generate local repository | mlc repo-gen |
-| Update local repos/PKGBUILDs | mlc pull/update [all if left empty] |
-| Create and/or open config file | mlc conf |
-| Initialises repo/workspace based on config in mlc.toml | mlc clone/init |
-| Displays information about a Malachite repository | mlc info/status |
-
-### Pacman Repository Creation
-
-- `mlc config` to create the config (and also populate it)
-- `mlc init` to build repository base from config file
-- `mlc build ` to either build individual packages, or don't specify package names to build all packages in mlc.toml
- - `build` typically automatically updates the repository unless `--no-regen` is passed, if so:
-- `mlc repo-gen` to generate functional pacman repository at \/\.db from built packages
-
-
-## How to build:
-
-Tested on latest Cargo (1.60.0-nightly)
-
-### Debug/development builds
-
-- `cargo build`
-
-### Optimised/release builds
-
-- `cargo build --release`
-
-### AUR
-
-- https://aur.archlinux.org/packages/malachite
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..f22924f
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,20 @@
+use serde::Deserialize;
+use std::path::PathBuf;
+
+use cargo_toml::Manifest;
+
+#[derive(Clone, Debug, Deserialize)]
+struct Metadata {
+ codename: String,
+}
+
+fn main() {
+ let manifest = Manifest::::from_path_with_metadata(PathBuf::from("Cargo.toml"))
+ .expect("Failed to read manifest (Cargo.toml)");
+
+ if let Some(package) = manifest.package {
+ if let Some(metadata) = package.metadata {
+ println!("cargo:rustc-env=MALACHITE_CODENAME={}", metadata.codename);
+ }
+ }
+}
diff --git a/docs/COMMON_FEATURES.md b/docs/COMMON_FEATURES.md
deleted file mode 100644
index 2bf3d53..0000000
--- a/docs/COMMON_FEATURES.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# Common Features Between Modes
-As [mode]us, shared of between uh… repositories… or something.
-
-### What you need to know
-Malachite is fairly fleshed out in Repository mode, and not so much in Workspace mode.
-
-This isn't of course because I'm lazy and hate Workspace mode or anything, there's just not
-a lot *to add*.
-
-Without further ado, let's take a look at this example config file.
-
-```toml
-# mlc.toml
-
-[base]
-mode = "workspace"
-smart_pull = true
-
-[mode.workspace]
-git_info = true
-colorblind = true
-
-[repositories]
-repos = [
- "foo:repo1:2",
- "foo:repo2/testing",
- "bar:baz!",
- "bar:qux/testing!:1",
-]
-
-[repositories.urls]
-foo = "https://example.org/{}.git"
-bar = "https://example.org/other/{}.git"
-```
-
-Now, this is going to look really confusing at first, but bear with me.
-
-In this document, we'll cover only what is required to know for **both** modes.
-More specialized tutorials will be linked for each mode at the bottom of this page.
-
-Let's start with the base(ics).
-
-
-### Base Config
-The base config defines a few common parameters between all the Malachite modes.
-
-```toml
-[base]
-mode = "workspace"
-smart_pull = true
-```
-
-In this snippet, we define `mode` to be `"workspace"`.
-
-`base.mode` in Malachite can only ever be one of `"workspace"` or `"repository"`, and defines, drumroll…
-The mode in which it operates. If it is set to anything but those 2 modes, it crashes.
-
-Also defined in this snippet is `smart_pull`, which controls whether to pull… smartly.
-
-What that actually means is that instead of just performing a simple `git pull` in each repository, Malachite
-will:
-
-- First run `git remote update` to fetch new remotes from origin
-- Then run `git status` and parse the output to see if the current branch is behind
-- If the current branch is behind, it runs a regular `git pull`, which will take advantage of the remotes
- already being updated.
-
-Theoretically, this only actually speeds things up by a minute amount (think milliseconds, really). Where this feature shines however is in repository mode,
-where it enables helpful automation features such as `build_on_update`.
-
-Regardless, it's recommended to keep this enabled for the slight speed-up, and only disable it if it causes issues.
-I've never personally had issues with it in the past, but who knows what could happen. This is Git we're talking about.
-
-
-### Repositories Config
-
-The repositories config is realistically what makes Malachite churn repo butter internally. It's the whole
-purpose of what it does, and because of that we've tried to come up with a neat little system to help
-facilitate many packages without having to type each URL out a million times.
-
-```toml
-[repositories]
-repos = [
- "foo:repo1:2",
- "foo:repo2/testing",
- "bar:baz!",
- "bar:qux/testing!:1",
-]
-
-[repositories.urls]
-foo = "https://example.org/{}.git"
-bar = "https://example.org/other/{}.git"
-```
-
-The way this works is simple:
-- We have 2 URLs in the `repositories.urls` key.
-- Each `repo` in the `repositories.repos` key is prefixed with an identifier.
-- If the number is `foo`, it'll insert the URL with the id `foo`.
- - Specifically, in the URL, it'll insert the defined `repo`'s name in place of the `%repo%` substring.
-
-#### Hang on, what are the special symbols????
-
-I'm glad you asked!
-- If you want to clone a specific branch, simply use the `/` delimiter. To clone repository `foo` on branch `bar`, use `id:foo/bar`.
-- If you want a specific package to build first, use instances of `!` to set priority. This is explained later in the [Repository Mode](REPOSITORY_MODE.md) page
-
-The last `:` delimiter is entirely optional, and behaves differently depending on the mode:
-- In Repository mode, it defines the desired commit hash/rev/tag to checkout on repository clone
-- In Workspace mode, it defines the desired depth to clone the repository, useful with large git repositories, such as `nixpkgs`.
-
-That's literally it!
-
-
-### Mode-Specific Config
-
-For mode-specific config, avert your eyes to the following links!
-
-- [Workspace Mode](WORKSPACE_MODE.md)
-- [Repository Mode](REPOSITORY_MODE.md)
-
-### Examples
-
-Functioning config examples for both modes are available in the [examples](../examples) directory!
-
-### Usage
-
-Alternatively, you can look at the [Usage](USAGE.md) guide!
-
diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md
deleted file mode 100644
index 6bd3462..0000000
--- a/docs/GETTING_STARTED.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Getting Started With Malachite
-Baby's first Malachite repository!
-
-### What you need to know
-
-Malachite is:
-- A pacman repository manager
-- A workspace manager
-- ~~Awesome~~
-
-Malachite isn't:
-- The end-all solution for all pacman repositories
-- Perfect
-
-
-### With that out of the way
-
-Hi! My name is Michal, and I wrote this tool pretty much on my own for [Crystal Linux](https://getcryst.al);
-but it is not at all exclusive to Crystal. This tool should and will work on and for any pacman-based
-distribution (so long as it packages all of Malachite's dependencies, of course).
-
-Throughout this tutorial, I'll explain each little feature of Malachite in what I hope to be bite-sized and
-programmatic chunks.
-
-Without further ado, let's begin with the first, most important question:
-
-
-### Modes
-
-What mode are you using malachite in?
-
-Currently, malachite supports 2 modes:
-
-#### Repository Mode
-- Allows the user to configure and manage a remote (or local) pacman-based package repository
-- Allows for customisability in repository name, signing preferences, signing key etc.
-- Allows for basic levels of automation, by using features such as build_on_update
-
-#### Workspace Mode
-- The most basic functionality of Malachite
-- Just clones git directories into a "Workspace" directory for easier management
-- Allows for basic pulling operations to keep your repositories up-to-date
-
-These modes essentially dictate everything about how Malachite functions, so much so that I now need to
-split this page off before it gets too long!
-
-For more info, get started with the [Common Features](COMMON_FEATURES.md) page!
diff --git a/docs/REPOSITORY_MODE.md b/docs/REPOSITORY_MODE.md
deleted file mode 100644
index 60a580c..0000000
--- a/docs/REPOSITORY_MODE.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Repository Mode
-PacManage your repositories in style!
-
-### Repository Config
-
-As opposed to the rather barren Workspace mode, the Repository mode config is rather fleshed out;
-and we have a few options to choose from.
-
-Let's take an example section from a Repository mode config,
-
-```toml
-[mode.repository]
-name = "example"
-build_on_update = true
-
-[mode.repository.signing]
-enabled = true
-key = "you@example.org"
-on_gen = true
-```
-
-### Basic Repository Config
-
-To start with, there are 2 main config keys to Repository mode:
-- `name`: Defines what pacman calls your repository.
-- `build_on_update`: In conjunction with `smart_pull`, defines whether to rebuild packages automatically when an update is detected.
-
-### Signing
-
-Malachite also supports, and encourages, the signing of packages.
-GPG Signing packages ensures that the user receives exactly what you packaged, without any chance of tampering.
-
-Calling back to the example above, we can see 3 config keys:
-
-- `enabled`: Defines whether to sign packages (heavily encouraged).
-- `key`: Defines the GPG key ID to use for signing.
-- `on_gen`: Defines whether to sign packages when they are built, or all at once on repository generation (this is also recommended).
-
----
-
-You can return to [Getting Started](GETTING_STARTED.md) page here!
diff --git a/docs/USAGE.md b/docs/USAGE.md
deleted file mode 100644
index 549e6a5..0000000
--- a/docs/USAGE.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Detailed Usage
-Work it harder, make it better!
-
-### Global Flags
-
-| Flag | Description |
-|-------------------|----------------------------------------------------------------------------------------------------------------------------------------|
-| `--verbose`, `-v` | Prints lots of debug information to `stderr`. If something doesn't go right, sending us the output with this enabled will help greatly |
-| `--exclude`, `-x` | Excludes the supplied package from the current operation. Can be used multiple times. |
-
-### Basic Commands
-
-| Action | Command | Extra Flags |
-|-----------------------------------------------------------------------------------------|-------------------------------------------|------------------------------------------------------------------------------------------------------------------|
-| Build a package/packages. | `mlc build ` [all if left empty] | `--no-regen`: Doesn't regenerate repository after build |
-| Generate pacman repository | `mlc repo-gen` | |
-| Update local repos/PKGBUILDs | `mlc pull/update` [all if left empty] | `--no-regen`: If `mode.repository.build_on_update` is `true`, Do not regenerate repository after package rebuild |
-| Create and/or open config file | `mlc conf` | |
-| Initialises repo/workspace based on config in mlc.toml | `mlc clone/init` | |
-| Displays an info panel/overview of the current repo | `mlc info/status` | |
-| Resets Malachite repository by deleting all directories, omitting `mlc.toml` and `.git` | `mlc clean/reset` | `--force`: Remove dirty directories (unstaged, untracked, etc) |
-
-### Exit Codes
-
-| AppExitCode (named Enum) | Exit code (i32) | Error Description |
-|--------------------------|-----------------|--------------------------------------------------------------------------------------------------------|
-| `RunAsRoot` | `1` | Malachite was run as root. This is highly discouraged. So highly, in fact, that it will refuse to run. |
-| `PkgsNotFound` | `2` | No packages were specified/found for the desired operation |
-| `DirNotEmpty` | `3` | The creation of a Malachite repository was attempted in a non-empty directory |
-| `ConfigParseError` | `4` | The config file could not be parsed |
-| `RepoParseError` | `5` | The repository info could not be parsed |
-| `RepoNotClean` | `6` | The git repository is not clean and cannot be removed without `--force` |
diff --git a/docs/WORKSPACE_MODE.md b/docs/WORKSPACE_MODE.md
deleted file mode 100644
index 14efb54..0000000
--- a/docs/WORKSPACE_MODE.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Workspace Mode
-You'll never have to work(space) another day in your life!
-
-### Workspace Config
-
-Taking an example section from the Workspace mode config,
-
-```toml
-[mode.workspace]
-git_info = true
-colorblind = true
-```
-
-Currently, Workspace mode only has 2 options, both pertaining to the display of information. (`mlc info`)
-
-The first key is `git_info`, which is a boolean value. If it is true, the git information will be displayed alongside repository information.
-
-This information will be formatted as so: `D Pl Ps `
-
-The key for the values is as follows:
-- D: Whether the repository is dirty or not (unstaged changes)
-- Pl: Whether there are unpulled changes at the remote
-- Ps: Whether there are unpushed changes in your local repository
-
-These will be typically displayed in either Green (Clean) or Red (Dirty)
-
-However, if `colorblind` is set to true, the colors will instead be set to Blue (Clean) or Dark Red (Dirty), to be more discernible to colorblind users.
-
----
-
-You can return to [Getting Started](GETTING_STARTED.md) page here!
diff --git a/examples/repository/mlc.toml b/examples/repository/mlc.toml
deleted file mode 100644
index a8aa808..0000000
--- a/examples/repository/mlc.toml
+++ /dev/null
@@ -1,26 +0,0 @@
-[base]
-mode = "repository"
-smart_pull = true
-
-[mode.repository]
-name = "repository-test"
-build_on_update = true
-
-[mode.repository.signing]
-enabled = true
-key = "michal@tar.black"
-on_gen = true
-
-[repositories]
-repos = [
- "crs:malachite/development:0a5bdc9", # Note, in this example, these two
- "mic:apod:v.1.1.2", # will fail to build.
- "pkg:pfetch!",
- "nms:rpass" # This too
-]
-
-[repositories.urls]
-crs = "https://github.com/crystal-linux/{}"
-pkg = "https://github.com/crystal-linux/pkgbuild.{}"
-mic = "https://git.tar.black/michal/{}"
-nms = "https://github.com/not-my-segfault/{}"
diff --git a/examples/workspace/mlc.toml b/examples/workspace/mlc.toml
deleted file mode 100644
index 27d2bed..0000000
--- a/examples/workspace/mlc.toml
+++ /dev/null
@@ -1,20 +0,0 @@
-[base]
-mode = "workspace"
-smart_pull = true
-
-[mode.workspace]
-git_info = true
-colorblind = false
-
-[repositories]
-repos = [
- "crs:amethyst",
- "crs:malachite/development!",
- "aur:notop-git",
- "nix:nixpkgs/nixos-unstable:1",
-]
-
-[repositories.urls]
-crs = "https://github.com/crystal-linux/{}"
-aur = "https://aur.archlinux.org/{}"
-nix = "https://github.com/nixos/{}"
diff --git a/flake.lock b/flake.lock
index 36e9580..55c2fe4 100644
--- a/flake.lock
+++ b/flake.lock
@@ -7,11 +7,11 @@
]
},
"locked": {
- "lastModified": 1655042882,
- "narHash": "sha256-9BX8Fuez5YJlN7cdPO63InoyBy7dm3VlJkkmTt6fS1A=",
+ "lastModified": 1671096816,
+ "narHash": "sha256-ezQCsNgmpUHdZANDCILm3RvtO1xH8uujk/+EqNvzIOg=",
"owner": "nix-community",
"repo": "naersk",
- "rev": "cddffb5aa211f50c4b8750adbec0bbbdfb26bb9f",
+ "rev": "d998160d6a076cfe8f9741e56aeec7e267e3e114",
"type": "github"
},
"original": {
@@ -22,11 +22,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1664281702,
- "narHash": "sha256-haixZ4TJLu1Dciow54wrHrHvlGDVr5sW6MTeAV/ZLuI=",
+ "lastModified": 1676912548,
+ "narHash": "sha256-5KH+YpMju3Zj6PU4wV3qaEznRvyt0y7Ei4hS7jsEh2c=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "7e52b35fe98481a279d89f9c145f8076d049d2b9",
+ "rev": "20c135b191fd84f556cc5eb37b8d9d683a580b1e",
"type": "github"
},
"original": {
@@ -44,11 +44,11 @@
},
"utils": {
"locked": {
- "lastModified": 1656928814,
- "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
+ "lastModified": 1676283394,
+ "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
+ "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index 801478d..68e5b93 100644
--- a/flake.nix
+++ b/flake.nix
@@ -46,6 +46,9 @@
pkg-config
pacman
openssl
+ libarchive
+ libgpg-error
+ gpgme
];
# For rust-analyzer
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
@@ -53,4 +56,4 @@
formatter = pkgs.alejandra;
});
-}
\ No newline at end of file
+}
diff --git a/i18n.toml b/i18n.toml
new file mode 100644
index 0000000..05c50ba
--- /dev/null
+++ b/i18n.toml
@@ -0,0 +1,4 @@
+fallback_language = "en"
+
+[fluent]
+assets_dir = "i18n"
\ No newline at end of file
diff --git a/i18n/en/Malachite.ftl b/i18n/en/Malachite.ftl
new file mode 100644
index 0000000..b85a45a
--- /dev/null
+++ b/i18n/en/Malachite.ftl
@@ -0,0 +1,23 @@
+# src/args.rs
+
+about = A scriptable and declarative Pacman repository management tool
+
+help-verbose = Set amount of logging output
+help-exclude = Exclude given repositories from the operation
+
+help-init = Initializes empty Malachite repository in current directory
+
+help-pull = Clone and/or update Git repositories
+help-pull-rebuild = Rebuild packages if new commits are found
+help-pull-concurrent = Number of concurrent rebuilds
+
+help-build = Build given package(s)
+help-build-packages = Package(s) to build
+help-build-concurrent = Number of concurrent builds
+
+help-clean = Remove Git repositories no longer present in the configuration
+help-clean-prune = Keep X latest versions of each package, removing any that are older
+
+help-info = Display information about the current Malachite repository
+
+help-generate = Generate Pacman repository from built packages
\ No newline at end of file
diff --git a/justfile b/justfile
new file mode 100755
index 0000000..ba347ea
--- /dev/null
+++ b/justfile
@@ -0,0 +1,14 @@
+#!/usr/bin/env just --justfile
+
+release:
+ cargo build --release
+
+lint:
+ cargo clippy --all-targets --all-features -- -D warnings
+ cargo fmt -- --check
+
+test:
+ if [ -d example ]; then rm -rf example; fi
+ mkdir example
+
+ pushd example && cargo run -- init && cargo run -- pull && popd
diff --git a/src/args.rs b/src/args.rs
index 25e61c7..d0ff800 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -1,75 +1,75 @@
use clap::{ArgAction, Parser, Subcommand};
+use crate::fl;
+
+static VERSION: &str = concat!(
+ env!("CARGO_PKG_VERSION"),
+ " (",
+ env!("MALACHITE_CODENAME"),
+ ")"
+);
+
#[derive(Debug, Clone, Parser)]
-#[clap(name = "Malachite", version = env ! ("CARGO_PKG_VERSION"), about = env ! ("CARGO_PKG_DESCRIPTION"))]
+#[command(bin_name = "mlc", name = "Malachite", version = VERSION, about = fl!("about"), infer_subcommands = true)]
pub struct Args {
- #[clap(subcommand)]
- pub subcommand: Option,
+ #[command(subcommand)]
+ pub subcommand: Operation,
- /// Sets the level of verbosity
- #[clap(long, short, global(true), action = ArgAction::SetTrue)]
- pub verbose: bool,
+ #[arg(long, short, action = ArgAction::Count, global = true, help = fl!("help-verbose"))]
+ pub verbose: u8,
- /// Excludes packages from given operation, if applicable
- #[clap(short = 'x', long = "exclude", action = ArgAction::Append, takes_value = true)]
+ #[arg(long, short = 'x', action = ArgAction::Append, global = true, help = fl!("help-exclude"))]
pub exclude: Vec,
}
#[derive(Debug, Clone, Subcommand)]
pub enum Operation {
- /// Builds the given packages
- #[clap(name = "build", aliases = & ["b"])]
- Build {
- /// The packages to operate on
- #[clap(name = "package(s)", action = ArgAction::Append, index = 1)]
- packages: Vec,
+ #[command(bin_name = "mlc", name = "init", short_flag = 'I', about = fl!("help-init"))]
+ Init,
- /// Does not regenerate repository after building given package(s)
- #[clap(short = 'n', long = "no-regen", action = ArgAction::SetTrue)]
- no_regen: bool,
+ #[command(bin_name = "mlc", name = "pull", short_flag = 'P', about = fl!("help-pull"))]
+ Pull {
+ #[arg(long, short, action = ArgAction::SetTrue, help = fl!("help-pull-rebuild"))]
+ rebuild: bool,
+
+ #[arg(long, short, help = fl!("help-pull-concurrent"))]
+ concurrent: Option,
},
- /// Generates Pacman repository from built packages
- #[clap(name = "repo-gen", aliases = & ["repo", "r"])]
- RepoGen,
+ #[command(bin_name = "mlc", name = "build", short_flag = 'B', about = fl!("help-build"))]
+ Build {
+ #[arg(required = true, help = fl!("help-build-packages"))]
+ packages: Vec,
- /// Clones all git repositories from mlc.toml branching from current directory
- #[clap(name = "clone", aliases = & ["init", "i", "c"])]
- Clone,
+ #[arg(long, short, help = fl!("help-build-concurrent"))]
+ concurrent: Option,
+ },
- /// Removes everything in directory except for mlc.toml
- #[clap(name = "clean", aliases = & ["clean", "cl", "reset"])]
+ #[command(bin_name = "mlc", name = "clean", short_flag = 'C', about = fl!("help-clean"))]
Clean {
- /// Force removes everything, even if git directory is dirty or has unpushed changes or changes at remote
- #[clap(short = 'f', long = "force", action = ArgAction::SetTrue)]
- force: bool,
+ #[arg(long, short, help = fl!("help-clean-prune"))]
+ prune: Option,
},
- /// Removes all but the latest 3 versions of each package in a repository
- #[clap(name = "prune", aliases = & ["prune", "p"])]
- Prune,
-
- /// Shows an info panel/overview about the current repository
- #[clap(name = "info", aliases = & ["status", "s", "i"])]
+ #[command(bin_name = "mlc", name = "info", short_flag = 'i', about = fl!("help-info"))]
Info,
- /// Pulls in git repositories from mlc.toml branching from current directory
- #[clap(name = "pull", aliases = & ["u"])]
- Pull {
- /// The packages to operate on
- #[clap(name = "package(s)", help = "The packages to operate on", action = ArgAction::Append, index = 1)]
- packages: Vec,
-
- /// Does not regenerate repository after pulling given package(s). This only applies if build_on_update is set to true in repository config
- #[clap(short = 'n', long = "no-regen", action = ArgAction::SetTrue)]
- no_regen: bool,
+ #[command(bin_name = "mlc", name = "generate", short_flag = 'G', about = fl!("help-generate"))]
+ Generate,
+}
- /// Will prompt for confirmation before rebuilding a package
- #[clap(long, action = ArgAction::SetTrue)]
- interactive: bool,
- },
+#[derive(Default, Clone, Debug)]
+pub struct GlobalArgs {
+ pub verbosity: u8,
+ pub exclude: Vec,
+}
- /// Create and/or open local config file
- #[clap(name = "config", aliases = & ["conf"])]
- Config,
+impl GlobalArgs {
+ pub fn new() -> Self {
+ let args: Args = Args::parse();
+ Self {
+ verbosity: args.verbose,
+ exclude: args.exclude,
+ }
+ }
}
diff --git a/src/build.rs b/src/build.rs
new file mode 100644
index 0000000..d118b3a
--- /dev/null
+++ b/src/build.rs
@@ -0,0 +1,188 @@
+use std::fs;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+use fs_extra::dir::{self, CopyOptions};
+use futures_util::StreamExt;
+use futures_util::TryStreamExt;
+use glob::glob;
+use names::{Generator, Name};
+use podman_api::opts::ContainerCreateOpts;
+use podman_api::opts::ImageBuildOpts;
+use podman_api::Podman;
+
+use crate::errors::AppResult;
+use crate::utils::{uid, ShellCommand};
+
+pub const BUILDFILE: &str = include_str!("../Buildfile");
+
+#[derive(Clone, Debug)]
+pub enum BuildKind {
+ Podman { image: String },
+ Host,
+}
+
+impl BuildKind {
+ pub fn image(&self) -> Option<&str> {
+ match self {
+ Self::Podman { image } => Some(image),
+ Self::Host => None,
+ }
+ }
+}
+
+pub struct Build {
+ pub kind: BuildKind,
+ pub name: String,
+ pub pkg: String,
+ pub out: PathBuf,
+ pub flags: Vec,
+}
+
+impl Build {
+ pub fn new>(
+ kind: BuildKind,
+ pkg: S,
+ out: P,
+ flags: Vec,
+ ) -> AppResult {
+ let mut generator = Generator::with_naming(Name::Numbered);
+
+ let pod = Self {
+ kind,
+ name: generator.next().unwrap(),
+ pkg: pkg.to_string(),
+ out: out.into(),
+ flags,
+ };
+
+ Ok(pod)
+ }
+
+ pub async fn build(&self) -> AppResult<()> {
+ fs::create_dir_all(&self.out)?;
+
+ match &self.kind {
+ BuildKind::Podman { .. } => self.podman_build().await?,
+ BuildKind::Host => self.host_build().await?,
+ }
+
+ Ok(())
+ }
+
+ async fn podman_build(&self) -> AppResult<()> {
+ let uid = uid();
+
+ let podman = Podman::new(format!("unix:///var/run/user/{}/podman/podman.sock", uid))?;
+ let image = self.kind.image().unwrap();
+
+ let buildfile = fs::read_to_string(".mlc/Buildfile")?;
+ let buildfile_template = liquid::ParserBuilder::with_stdlib()
+ .build()?
+ .parse(&buildfile)?;
+
+ let buildfile_values = liquid::object!({
+ "image": image,
+ "pkg": &self.pkg,
+ "flags": &self.flags.join(" "),
+ });
+
+ let buildfile = buildfile_template.render(&buildfile_values)?;
+
+ let build_path = std::env::temp_dir().join(&self.name);
+
+ fs::create_dir_all(&build_path)?;
+ fs::write(build_path.join("Buildfile"), buildfile)?;
+
+ dir::copy(&self.pkg, &build_path, &CopyOptions::new())?;
+ dir::copy(".mlc/store", &build_path, &CopyOptions::new())?;
+
+ let opts = &ImageBuildOpts::builder(build_path.to_string_lossy())
+ .dockerfile("Buildfile")
+ .tag(&self.name)
+ .build();
+
+ let mut log_file = File::create(&format!(".mlc/logs/{}-{}.log", &self.pkg, &self.name))?;
+
+ let images = podman.images();
+ match images.build(opts) {
+ Ok(mut build_stream) => {
+ while let Some(chunk) = build_stream.next().await {
+ match chunk {
+ Ok(chunk) => {
+ println!("{}", chunk.stream);
+ let _ = log_file.write(chunk.stream.as_bytes())?;
+ }
+ Err(e) => eprintln!("{}", e),
+ }
+ }
+ }
+ Err(e) => eprintln!("{}", e),
+ };
+
+ podman
+ .containers()
+ .create(
+ &ContainerCreateOpts::builder()
+ .image(&self.name)
+ .name(&self.name)
+ .build(),
+ )
+ .await?;
+
+ let bytes = podman
+ .containers()
+ .get(&self.name)
+ .copy_from("/out")
+ .try_concat()
+ .await?;
+
+ let mut archive = tar::Archive::new(&bytes[..]);
+ archive.unpack(self.out.parent().unwrap())?;
+
+ podman.images().get(&self.name).remove().await?;
+
+ fs::remove_dir_all(build_path)?;
+
+ Ok(())
+ }
+
+ async fn host_build(&self) -> AppResult<()> {
+ let build_path = std::env::temp_dir().join(&self.name);
+
+ fs::create_dir_all(&build_path)?;
+ dir::copy(&self.pkg, &build_path, &CopyOptions::new())?;
+ dir::copy(".mlc/store", &build_path, &CopyOptions::new())?;
+
+ ShellCommand::makepkg()
+ .args(["-s", "--noconfirm"])
+ .args(&self.flags)
+ .cwd(&build_path.join(&self.pkg))
+ .spawn()?
+ .wait()?;
+
+ let glob = glob(
+ &build_path
+ .join(&self.pkg)
+ .join("*.pkg.tar.*")
+ .to_string_lossy(),
+ )?;
+ for entry in glob {
+ match entry {
+ Ok(path) => {
+ fs_extra::copy_items(
+ &[path.to_string_lossy().to_string()],
+ &self.out,
+ &CopyOptions::new(),
+ )?;
+ }
+ Err(e) => eprintln!("{}", e),
+ }
+ }
+
+ fs::remove_dir_all(build_path)?;
+
+ Ok(())
+ }
+}
diff --git a/src/builders.rs b/src/builders.rs
new file mode 100644
index 0000000..f538691
--- /dev/null
+++ b/src/builders.rs
@@ -0,0 +1,182 @@
+use crate::errors::AppResult;
+use crate::utils::ShellCommand;
+use std::path::PathBuf;
+
+#[derive(Debug, Clone)]
+pub struct GitCloneBuilder {
+ pub url: String,
+ pub branch: Option,
+ pub path: PathBuf,
+}
+
+impl GitCloneBuilder {
+ pub fn new(url: S, path: P) -> Self
+ where
+ S: ToString,
+ P: Into,
+ {
+ Self {
+ url: url.to_string(),
+ branch: None,
+ path: path.into(),
+ }
+ }
+
+ pub fn branch(mut self, branch: String) -> Self {
+ self.branch = Some(branch);
+ self
+ }
+
+ pub fn clone(&self) -> AppResult {
+ let mut args = vec![
+ "clone".to_string(),
+ self.url.clone(),
+ self.path.to_string_lossy().to_string(),
+ ];
+
+ if let Some(branch) = &self.branch {
+ args.push("--branch".to_string());
+ args.push(branch.clone());
+ }
+
+ let command = ShellCommand::git().args(args);
+
+ Ok(command)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct GitPullBuilder {
+ pub path: PathBuf,
+}
+
+impl GitPullBuilder {
+ pub fn new>(path: P) -> Self {
+ Self { path: path.into() }
+ }
+
+ pub fn pull(&self) -> AppResult {
+ let command = ShellCommand::git().cwd(self.path.clone()).args(["pull"]);
+
+ Ok(command)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct GitCheckoutBuilder {
+ pub path: PathBuf,
+ pub branch: String,
+}
+
+impl GitCheckoutBuilder {
+ pub fn new(path: P, branch: S) -> Self
+ where
+ S: ToString,
+ P: Into,
+ {
+ Self {
+ path: path.into(),
+ branch: branch.to_string(),
+ }
+ }
+
+ pub fn checkout(&self) -> AppResult {
+ let command = ShellCommand::git()
+ .cwd(self.path.clone())
+ .arg("checkout")
+ .arg(self.branch.clone());
+
+ Ok(command)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct GitStatusBuilder {
+ pub path: PathBuf,
+}
+
+impl GitStatusBuilder {
+ pub fn new>(path: P) -> Self {
+ Self { path: path.into() }
+ }
+
+ pub fn status(&self) -> AppResult {
+ let command = ShellCommand::git().cwd(self.path.clone()).arg("status");
+
+ Ok(command)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct GitInitBuilder {
+ pub path: PathBuf,
+}
+
+impl GitInitBuilder {
+ pub fn new>(path: P) -> Self {
+ Self { path: path.into() }
+ }
+
+ pub fn init(&self) -> AppResult {
+ let command = ShellCommand::git().cwd(self.path.clone()).arg("init");
+
+ Ok(command)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct GitAddBuilder {
+ pub path: PathBuf,
+ pub refspecs: Vec,
+}
+
+impl GitAddBuilder {
+ pub fn new(path: PathBuf) -> Self {
+ Self {
+ path,
+ refspecs: vec![],
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn refspec(mut self, refspec: S) -> Self {
+ self.refspecs.push(refspec.to_string());
+ self
+ }
+
+ pub fn refspecs(mut self, refspecs: V) -> Self
+ where
+ S: ToString,
+ V: IntoIterator- ,
+ {
+ self.refspecs
+ .extend(refspecs.into_iter().map(|s| s.to_string()));
+ self
+ }
+
+ pub fn add(&self) -> AppResult {
+ let command = ShellCommand::git()
+ .cwd(self.path.clone())
+ .arg("add")
+ .args(self.refspecs.clone());
+
+ Ok(command)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct GitFetchBuilder {
+ pub path: PathBuf,
+}
+
+impl GitFetchBuilder {
+ pub fn new>(path: P) -> Self {
+ Self { path: path.into() }
+ }
+
+ pub fn fetch(&self) -> AppResult {
+ let command = ShellCommand::git().cwd(self.path.clone()).arg("fetch");
+
+ Ok(command)
+ }
+}
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..b03914f
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,192 @@
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::env;
+use std::ops::Not;
+use std::path::PathBuf;
+
+use serde::{Deserialize, Serialize};
+
+use crate::build::BUILDFILE;
+use crate::builders::{GitAddBuilder, GitInitBuilder};
+use crate::errors::ConfigError;
+use crate::generate::GENERATEFILE;
+use crate::lock::LOCKFILE_VERSION;
+use crate::AppError;
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Config {
+ pub base: BaseConfig,
+ pub repo: RepoConfig,
+ pub repositories: Repositories,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct BaseConfig {
+ pub src: PathBuf,
+ pub podman: bool,
+ pub image: String,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct RepoConfig {
+ pub name: String,
+ pub out: PathBuf,
+ pub repo: PathBuf,
+ pub security: SecurityConfig,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct SecurityConfig {
+ pub sign: bool,
+ pub sign_key: Option,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Repositories {
+ pub names: Vec,
+ pub keys: HashMap,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(into = "String", try_from = "String")]
+pub struct GitRepo {
+ pub key: String,
+ pub name: String,
+ pub rev: Option,
+}
+
+impl From for GitRepo {
+ fn from(s: String) -> Self {
+ let (key, repo) = s.split_once(':').unwrap();
+ let (name, rev) = repo.split_once('@').unwrap_or((repo, ""));
+
+ Self {
+ key: key.to_string(),
+ name: name.to_string(),
+ rev: rev.is_empty().not().then_some(rev.to_string()),
+ }
+ }
+}
+
+impl From for String {
+ fn from(repo: GitRepo) -> Self {
+ if let Some(rev) = repo.rev {
+ format!("{}:{}@{}", repo.key, repo.name, rev)
+ } else {
+ format!("{}:{}", repo.key, repo.name)
+ }
+ }
+}
+
+impl GitRepo {
+ pub fn expand(&self, config: &Config) -> Result {
+ let url = config
+ .repositories
+ .keys
+ .get(&self.key)
+ .ok_or_else(|| ConfigError::Expand(format!("Unknown key: {}", self.key)))?;
+
+ Ok(url.replace("{}", &self.name))
+ }
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ let pwd = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
+ let name = pwd
+ .file_name()
+ .map(|s| s.to_string_lossy())
+ .unwrap_or_else(|| Cow::from("mlc".to_string()));
+ Self {
+ base: BaseConfig {
+ src: pwd.clone(),
+ podman: false,
+ image: "archlinux:latest".to_string(),
+ },
+ repo: RepoConfig {
+ name: name.to_string(),
+ out: pwd.join("out"),
+ repo: pwd.join("repo"),
+ security: SecurityConfig {
+ sign: false,
+ sign_key: None,
+ },
+ },
+ repositories: Repositories {
+ names: vec![
+ GitRepo {
+ key: "crs".to_string(),
+ name: "malachite".to_string(),
+ rev: Some("mlc3-rewrite".to_string()),
+ },
+ GitRepo {
+ key: "aur".to_string(),
+ name: "ame".to_string(),
+ rev: None,
+ },
+ ],
+ keys: HashMap::from([
+ (
+ "crs".to_string(),
+ "https://git.getcryst.al/crystal/software/{}.git".to_string(),
+ ),
+ (
+ "aur".to_string(),
+ "https://aur.archlinux.org/{}.git".to_string(),
+ ),
+ ]),
+ },
+ }
+ }
+}
+
+impl Config {
+ pub fn init() -> Result<(), AppError> {
+ let pwd = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
+ let config = Config::default();
+
+ if pwd.read_dir()?.count() > 0 {
+ return Err(AppError::Config(ConfigError::Init(
+ "Directory is not empty".to_string(),
+ )));
+ }
+
+ std::fs::create_dir(".mlc")?;
+ std::fs::write(
+ ".mlc/config.toml",
+ toml::to_string_pretty(&config)
+ .map_err(|e| AppError::Config(ConfigError::Serialize(e)))?,
+ )?;
+ std::fs::write(".mlc/Buildfile", BUILDFILE)?;
+ std::fs::write(".mlc/Generatefile", GENERATEFILE)?;
+
+ std::fs::create_dir(".mlc/conf.d")?;
+ std::fs::create_dir(".mlc/store")?;
+ std::fs::create_dir(".mlc/logs")?;
+
+ std::fs::write(
+ ".mlc/mlc.lock",
+ format!("[lockfile]\nversion = '{}'\n\n[remote]\n", LOCKFILE_VERSION),
+ )?;
+ std::fs::write(".mlc/conf.d/.gitkeep", "\n")?;
+ std::fs::write(".mlc/store/.gitkeep", "\n")?;
+ std::fs::write(".mlc/logs/.gitkeep", "\n")?;
+ std::fs::write(".gitignore", "/*/\n!/.mlc")?;
+
+ GitInitBuilder::new(pwd.clone()).init()?.silent()?;
+
+ GitAddBuilder::new(pwd)
+ .refspecs([".gitignore", ".mlc/**"])
+ .add()?
+ .silent()?;
+
+ Ok(())
+ }
+
+ pub fn load() -> Result {
+ let pwd = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
+ let config = toml::from_str(&std::fs::read_to_string(pwd.join(".mlc/config.toml"))?)?;
+
+ Ok(config)
+ }
+}
diff --git a/src/errors.rs b/src/errors.rs
new file mode 100644
index 0000000..cd5feb8
--- /dev/null
+++ b/src/errors.rs
@@ -0,0 +1,93 @@
+use thiserror::Error;
+
+pub type AppResult = Result;
+
+#[derive(Error, Debug)]
+pub enum AppError {
+ #[error("Malachite should not be run as root")]
+ RunAsRoot,
+
+ #[error(transparent)]
+ Config(#[from] ConfigError),
+
+ #[error(transparent)]
+ Glob(#[from] glob::PatternError),
+
+ #[error(transparent)]
+ Git(#[from] GitError),
+
+ #[error(transparent)]
+ LibArchive(#[from] compress_tools::Error),
+
+ #[error(transparent)]
+ Gpg(#[from] gpgme::Error),
+
+ #[error(transparent)]
+ Utf8(#[from] std::string::FromUtf8Error),
+
+ #[error(transparent)]
+ Podman(#[from] podman_api::Error),
+
+ #[error(transparent)]
+ Io(#[from] IoError),
+
+ #[error(transparent)]
+ Liquid(#[from] liquid::Error),
+}
+
+#[derive(Error, Debug)]
+pub enum IoError {
+ #[error("IO Error: {0}")]
+ Io(#[from] std::io::Error),
+
+ #[error(transparent)]
+ FsExtra(#[from] fs_extra::error::Error),
+}
+
+#[derive(Error, Debug)]
+pub enum GitError {
+ #[error("Invalid Git repository: {0}")]
+ InvalidRepository(String),
+
+ #[error("Unable to merge: {0}")]
+ MergeError(String),
+}
+
+#[derive(Error, Debug)]
+pub enum ConfigError {
+ #[error("Failed to deserialize config: {0}")]
+ Deserialize(#[from] toml::de::Error),
+
+ #[error("Failed to serialize config: {0}")]
+ Serialize(#[from] toml::ser::Error),
+
+ #[error("Failed to expand repositories: {0}")]
+ Expand(String),
+
+ #[error("Failed to initialize config: {0}")]
+ Init(String),
+}
+
+impl From for AppError {
+ fn from(e: std::io::Error) -> Self {
+ AppError::Io(IoError::Io(e))
+ }
+}
+
+impl From for AppError {
+ fn from(e: fs_extra::error::Error) -> Self {
+ AppError::Io(IoError::FsExtra(e))
+ }
+}
+
+impl From for AppError {
+ fn from(e: toml::de::Error) -> Self {
+ AppError::Config(ConfigError::Deserialize(e))
+ }
+}
+
+impl From for AppError {
+ fn from(e: toml::ser::Error) -> Self {
+ AppError::Config(ConfigError::Serialize(e))
+ }
+}
diff --git a/src/generate.rs b/src/generate.rs
new file mode 100644
index 0000000..d41be01
--- /dev/null
+++ b/src/generate.rs
@@ -0,0 +1,211 @@
+use std::env::set_current_dir;
+use std::fs;
+use std::path::PathBuf;
+
+use fs_extra::dir;
+use futures_util::StreamExt;
+use futures_util::TryStreamExt;
+use glob::glob;
+use gpgme::{Context, Protocol, SignMode};
+use names::Generator;
+use podman_api::opts::ContainerCreateOpts;
+use podman_api::opts::ImageBuildOpts;
+use podman_api::Podman;
+
+use crate::config::SecurityConfig;
+use crate::errors::AppResult;
+use crate::utils::{uid, ShellCommand};
+
+pub const GENERATEFILE: &str = include_str!("../Generatefile");
+
+#[derive(Clone, Debug)]
+pub enum GenerateKind {
+ Podman { image: String },
+ Host,
+}
+
+impl GenerateKind {
+ pub fn image(&self) -> Option<&str> {
+ match self {
+ Self::Podman { image } => Some(image),
+ Self::Host => None,
+ }
+ }
+}
+
+pub struct PacmanRepository {
+ pub kind: GenerateKind,
+ pub name: String,
+ pub tempdir: String,
+ pub out: PathBuf,
+ pub repo: PathBuf,
+ pub security: SecurityConfig,
+}
+
+impl PacmanRepository {
+ pub fn new>(
+ kind: GenerateKind,
+ name: S,
+ out: P,
+ repo: P,
+ security: SecurityConfig,
+ ) -> Self {
+ let mut generator = Generator::with_naming(names::Name::Numbered);
+
+ Self {
+ kind,
+ name: name.to_string(),
+ tempdir: generator.next().unwrap(),
+ out: out.into(),
+ repo: repo.into(),
+ security,
+ }
+ }
+
+ pub async fn generate(&self) -> AppResult<()> {
+ fs::create_dir_all(&self.repo)?;
+
+ match &self.kind {
+ GenerateKind::Podman { .. } => self.podman_generate().await?,
+ GenerateKind::Host => self.host_generate()?,
+ }
+
+ if self.security.sign {
+ let packages: Vec = glob(&format!("{}/*.pkg.tar.*", self.repo.display()))?
+ .filter_map(Result::ok)
+ .collect();
+
+ let mut gpg_ctx = Context::from_protocol(Protocol::OpenPgp)?;
+ gpg_ctx.set_armor(true);
+
+ for package in packages {
+ let contents = fs::read(&package)?;
+ let mut buffer = Vec::new();
+
+ gpg_ctx.sign(SignMode::Detached, &contents, &mut buffer)?;
+
+ let sigfile = format!("{}.sig", package.display());
+ fs::write(sigfile, buffer)?;
+ }
+ }
+
+ Ok(())
+ }
+
+ pub fn host_generate(&self) -> AppResult<()> {
+ if self.repo.exists() {
+ fs::remove_dir_all(&self.repo)?;
+ }
+
+ fs::create_dir_all(&self.repo)?;
+
+ let generate_path = std::env::temp_dir().join(&self.tempdir);
+ fs::create_dir_all(&generate_path)?;
+
+ let copy_opts = dir::CopyOptions {
+ content_only: true,
+ ..Default::default()
+ };
+
+ dir::copy(&self.out, &generate_path, ©_opts)?;
+
+ let mlc_dir = std::env::current_dir()?;
+ set_current_dir(&generate_path)?;
+
+ let files: Vec = glob("*.pkg.tar.*")?
+ .filter_map(Result::ok)
+ .map(|p| p.display().to_string())
+ .collect();
+
+ ShellCommand::repo_add()
+ .arg(format!("{}.db.tar.gz", self.name))
+ .args(files)
+ .spawn()?
+ .wait()?;
+
+ set_current_dir(mlc_dir)?;
+
+ let copy_opts = dir::CopyOptions {
+ content_only: true,
+ ..Default::default()
+ };
+
+ dir::copy(&generate_path, &self.repo, ©_opts)?;
+
+ fs::remove_dir_all(&generate_path)?;
+
+ Ok(())
+ }
+ pub async fn podman_generate(&self) -> AppResult<()> {
+ let uid = uid();
+
+ let podman = Podman::new(format!("unix:///run/user/{}/podman/podman.sock", uid))?;
+ let image = self.kind.image().unwrap();
+
+ let generatefile = fs::read_to_string(".mlc/Generatefile")?;
+ let generatefile_template = liquid::ParserBuilder::with_stdlib()
+ .build()?
+ .parse(&generatefile)?;
+
+ let generatefile_values = liquid::object!({
+ "image": image,
+ "name": &self.name,
+ "repo": self.repo.file_name().unwrap().to_str().unwrap(),
+ });
+
+ let generatefile = generatefile_template.render(&generatefile_values)?;
+
+ let generate_path = std::env::temp_dir().join(&self.tempdir);
+ fs::create_dir_all(&generate_path)?;
+ fs::write(generate_path.join("Generatefile"), generatefile)?;
+
+ dir::copy(&self.out, &generate_path, &dir::CopyOptions::new())?;
+
+ let opts = &ImageBuildOpts::builder(generate_path.to_string_lossy())
+ .dockerfile("Generatefile")
+ .tag(&self.tempdir)
+ .build();
+
+ let images = podman.images();
+ match images.build(opts) {
+ Ok(mut generate_stream) => {
+ while let Some(chunk) = generate_stream.next().await {
+ match chunk {
+ Ok(chunk) => println!("{:?}", chunk),
+ Err(e) => eprintln!("{}", e),
+ }
+ }
+ }
+ Err(e) => eprintln!("{}", e),
+ };
+
+ podman
+ .containers()
+ .create(
+ &ContainerCreateOpts::builder()
+ .image(&self.tempdir)
+ .name(&self.tempdir)
+ .build(),
+ )
+ .await?;
+
+ let bytes = podman
+ .containers()
+ .get(&self.tempdir)
+ .copy_from(format!(
+ "/{}",
+ self.repo.file_name().unwrap().to_str().unwrap()
+ ))
+ .try_concat()
+ .await?;
+
+ let mut archive = tar::Archive::new(&bytes[..]);
+ archive.unpack(self.repo.parent().unwrap())?;
+
+ podman.images().get(&self.tempdir).remove().await?;
+
+ fs::remove_dir_all(&generate_path)?;
+
+ Ok(())
+ }
+}
diff --git a/src/git.rs b/src/git.rs
new file mode 100644
index 0000000..b9546fc
--- /dev/null
+++ b/src/git.rs
@@ -0,0 +1,93 @@
+use std::path::PathBuf;
+
+use crate::builders::{GitCheckoutBuilder, GitCloneBuilder, GitPullBuilder};
+use crate::errors::AppResult;
+use crate::utils::ShellCommand;
+
+pub struct GitRepository {
+ pub path: PathBuf,
+}
+
+impl GitRepository {
+ pub fn new(path: PathBuf) -> Self {
+ Self { path }
+ }
+
+ pub fn clone_repo(path: PathBuf, url: &str, branch: Option) -> AppResult<()> {
+ let mut builder = GitCloneBuilder::new(url, path);
+
+ if let Some(branch) = branch {
+ builder = builder.branch(branch);
+ }
+
+ builder.clone()?.silent()?;
+
+ Ok(())
+ }
+
+ pub fn pull(&self) -> AppResult<()> {
+ let builder = GitPullBuilder::new(self.path.clone());
+
+ builder.pull()?.silent()?;
+
+ Ok(())
+ }
+
+ pub fn update_origin(&self, url: &str) -> AppResult<()> {
+ ShellCommand::git()
+ .cwd(self.path.clone())
+ .args(["remote", "set-url", "origin"])
+ .arg(url)
+ .silent()?;
+
+ Ok(())
+ }
+
+ pub fn hash(&self) -> AppResult {
+ let output = ShellCommand::git()
+ .cwd(self.path.clone())
+ .args(["rev-parse", "--short", "HEAD"])
+ .output()?;
+
+ Ok(String::from_utf8(output.stdout)?.trim().to_string())
+ }
+
+ pub fn origin(&self) -> AppResult {
+ let output = ShellCommand::git()
+ .cwd(self.path.clone())
+ .args(["remote", "get-url", "origin"])
+ .output()?;
+
+ Ok(String::from_utf8(output.stdout)?.trim().to_string())
+ }
+
+ pub fn current_branch(&self) -> AppResult {
+ let output = ShellCommand::git()
+ .cwd(self.path.clone())
+ .args(["rev-parse", "--abbrev-ref", "HEAD"])
+ .output()?;
+
+ Ok(String::from_utf8(output.stdout)?.trim().to_string())
+ }
+
+ pub fn default_branch(&self) -> AppResult {
+ let output = ShellCommand::git()
+ .cwd(self.path.clone())
+ .args(["symbolic-ref", "refs/remotes/origin/HEAD", "--short"])
+ .output()?;
+
+ let stdout = String::from_utf8(output.stdout)?.trim().to_string();
+
+ let (_, branch) = stdout.split_once('/').unwrap();
+
+ Ok(branch.to_string())
+ }
+
+ pub fn checkout(&self, branch: &str) -> AppResult<()> {
+ GitCheckoutBuilder::new(self.path.clone(), branch)
+ .checkout()?
+ .silent()?;
+
+ Ok(())
+ }
+}
diff --git a/src/i18n.rs b/src/i18n.rs
new file mode 100644
index 0000000..8f7a372
--- /dev/null
+++ b/src/i18n.rs
@@ -0,0 +1,32 @@
+use i18n_embed::{
+ fluent::{fluent_language_loader, FluentLanguageLoader},
+ DesktopLanguageRequester,
+};
+use lazy_static::lazy_static;
+use rust_embed::RustEmbed;
+
+#[derive(RustEmbed)]
+#[folder = "i18n"]
+struct Localizations;
+
+fn read() -> FluentLanguageLoader {
+ let loader: FluentLanguageLoader = fluent_language_loader!();
+ let req_langs = DesktopLanguageRequester::requested_languages();
+ i18n_embed::select(&loader, &Localizations, &req_langs).unwrap();
+ loader
+}
+
+lazy_static! {
+ pub static ref LANG_LOADER: FluentLanguageLoader = read();
+}
+
+#[macro_export]
+macro_rules! fl {
+ ($message_id:literal) => {{
+ i18n_embed_fl::fl!($crate::i18n::LANG_LOADER, $message_id)
+ }};
+
+ ($message_id:literal, $($args:expr),*) => {{
+ i18n_embed_fl::fl!($crate::i18n::LANG_LOADER, $message_id, $($args), *)
+ }};
+}
diff --git a/src/info.rs b/src/info.rs
new file mode 100644
index 0000000..2d51c91
--- /dev/null
+++ b/src/info.rs
@@ -0,0 +1,137 @@
+use std::fmt::{Display, Formatter};
+use std::path::PathBuf;
+
+use crate::builders::{GitFetchBuilder, GitStatusBuilder};
+use tabled::Tabled;
+
+use crate::errors::AppResult;
+use crate::git::GitRepository;
+
+pub const BOLD: &str = "\x1b[1m";
+pub const CLEAN: &str = "\x1b[34m";
+pub const DIRTY: &str = "\x1b[33m";
+pub const UNKNOWN: &str = "\x1b[37m";
+pub const RESET: &str = "\x1b[0m";
+
+#[derive(Debug, Clone, Tabled)]
+pub enum GitStatus {
+ Clean,
+ Dirty,
+ Unknown,
+}
+
+impl GitStatus {
+ fn color(&self) -> String {
+ match self {
+ Self::Clean => format!("{}{}", BOLD, CLEAN),
+ Self::Dirty => format!("{}{}", BOLD, DIRTY),
+ Self::Unknown => format!("{}{}", BOLD, UNKNOWN),
+ }
+ }
+}
+
+impl Display for GitStatus {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Clean => write!(f, "✔"),
+ Self::Dirty => write!(f, "✘"),
+ Self::Unknown => write!(f, "?"),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct RichInfo {
+ output: String,
+ pub push: GitStatus,
+ pub pull: GitStatus,
+ pub dirty: GitStatus,
+}
+
+impl Display for RichInfo {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{}↑ {}↓ {}*{}",
+ self.push.color(),
+ self.pull.color(),
+ self.dirty.color(),
+ RESET
+ )
+ }
+}
+
+#[derive(Debug, Clone, Tabled)]
+pub struct GitInfo {
+ #[tabled(skip)]
+ pub path: PathBuf,
+ #[tabled(rename = "Repository")]
+ pub name: String,
+ #[tabled(rename = "Rich Info")]
+ pub rich: RichInfo,
+ #[tabled(rename = "Commit")]
+ pub commit: String,
+}
+
+impl GitInfo {
+ pub fn new(path: PathBuf) -> AppResult {
+ let output = GitStatusBuilder::new(path.clone()).status()?.output()?;
+ let output = String::from_utf8(output.stdout)?;
+
+ GitFetchBuilder::new(path.clone())
+ .fetch()?
+ .arg("--all")
+ .silent()?;
+
+ let mut repo = Self {
+ path: path.clone(),
+ name: path.file_name().unwrap().to_string_lossy().to_string(),
+ rich: RichInfo {
+ output,
+ push: GitStatus::Unknown,
+ pull: GitStatus::Unknown,
+ dirty: GitStatus::Unknown,
+ },
+ commit: GitRepository::new(path).hash()?,
+ };
+
+ repo.push()?;
+ repo.pull()?;
+ repo.dirty()?;
+
+ Ok(repo)
+ }
+
+ fn push(&mut self) -> AppResult<()> {
+ self.rich.push = if self.rich.output.contains("ahead") {
+ GitStatus::Dirty
+ } else {
+ GitStatus::Clean
+ };
+
+ Ok(())
+ }
+
+ fn pull(&mut self) -> AppResult<()> {
+ self.rich.pull = if self.rich.output.contains("behind") {
+ GitStatus::Dirty
+ } else {
+ GitStatus::Clean
+ };
+
+ Ok(())
+ }
+
+ fn dirty(&mut self) -> AppResult<()> {
+ self.rich.dirty = if self.rich.output.contains("Untracked files")
+ || self.rich.output.contains("Changes not staged for commit")
+ || self.rich.output.contains("Changes to be committed")
+ {
+ GitStatus::Dirty
+ } else {
+ GitStatus::Clean
+ };
+
+ Ok(())
+ }
+}
diff --git a/src/internal/exit_codes.rs b/src/internal/exit_codes.rs
deleted file mode 100644
index b850e1d..0000000
--- a/src/internal/exit_codes.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-pub enum AppExitCode {
- #[cfg(target_os = "linux")]
- RunAsRoot = 1,
- PkgsNotFound = 2,
- DirNotEmpty = 3,
- ConfigParseError = 4,
- RepoParseError = 5,
- RepoNotClean = 6,
-}
diff --git a/src/internal/mod.rs b/src/internal/mod.rs
deleted file mode 100755
index 799267c..0000000
--- a/src/internal/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-pub use exit_codes::*;
-pub use read::*;
-
-mod exit_codes;
-mod read;
-pub mod strings;
-pub mod structs;
diff --git a/src/internal/read.rs b/src/internal/read.rs
deleted file mode 100644
index e960060..0000000
--- a/src/internal/read.rs
+++ /dev/null
@@ -1,119 +0,0 @@
-use std::fs;
-use std::path::Path;
-
-use crate::internal::structs::{Config, Repo, SplitRepo, UnexpandedConfig};
-use crate::internal::AppExitCode;
-use crate::{crash, log};
-
-pub fn parse_cfg(verbose: bool) -> Config {
- // Crash if mlc.toml doesn't exist
- if !Path::exists("mlc.toml".as_ref()) {
- crash!(
- AppExitCode::ConfigParseError,
- "Config file not found (mlc.toml)"
- );
- }
-
- // Reading the config file to an UnexpandedConfig struct
- let file = fs::read_to_string("mlc.toml").unwrap();
- let config: UnexpandedConfig = toml::from_str(&file).unwrap_or_else(|e| {
- crash!(
- AppExitCode::ConfigParseError,
- "Error parsing config file: {}",
- e
- );
- // This is unreachable, but rustc complains about it otherwise
- std::process::exit(1);
- });
-
- log!(verbose, "Config file read: {:?}", config);
-
- // Crash if incorrect mode is set
- if config.base.mode != "workspace" && config.base.mode != "repository" {
- crash!(
- AppExitCode::ConfigParseError,
- "Invalid mode in mlc.toml, must be either \"repository\" or \"workspace\""
- );
- }
-
- let mut expanded_repos: Vec = vec![];
-
- // Parsing repos from the config file
- for x in config.repositories.repos {
- log!(verbose, "Parsing repo: {:?}", x);
- // Splits the repo name and index into a SplitRepo struct
- let split: Vec<&str> = x.split(':').collect();
- let split_struct = if split.len() > 2 {
- SplitRepo {
- id: split[0].parse().unwrap(),
- name: split[1].parse().unwrap(),
- extra: Some(split[2].parse().unwrap()),
- }
- } else {
- SplitRepo {
- id: split[0].parse().unwrap(),
- name: split[1].parse().unwrap(),
- extra: None,
- }
- };
- log!(verbose, "Split repo: {:?}", split_struct);
-
- // Parses all necessary values for expanding the repo to a Repo struct
- let id = split_struct.id;
-
- // If a branch is defined, parse it
- let branch = if split_struct.name.contains('/') {
- log!(verbose, "Branch defined: {}", split_struct.name);
- Some(
- split_struct.name.split('/').collect::>()[1]
- .to_string()
- .replace('!', ""),
- )
- } else {
- log!(verbose, "No branch defined");
- None
- };
-
- // Strip branch and priority info from the name, if present
- let name = if split_struct.name.contains('/') {
- split_struct.name.split('/').collect::>()[0].to_string()
- } else {
- split_struct.name.to_string().replace('!', "")
- };
-
- // Substitutes the name into the url
- let urls = &config.repositories.urls;
- let mut urls_vec = vec![];
- for (i, url) in urls {
- if i == &id {
- log!(verbose, "Substituting url: {:?}", url);
- urls_vec.push(url);
- }
- }
- let url = urls_vec[0].replace("{}", &name);
-
- // Counts instances of ! in the name, and totals a priority accordingly
- let priority = &split_struct.name.matches('!').count();
-
- // Creates and pushes Repo struct to expanded_repos
- let repo = Repo {
- name,
- url,
- branch,
- extra: split_struct.extra,
- priority: *priority,
- };
- log!(verbose, "Expanded repo: {:?}", repo);
- expanded_repos.push(repo);
- }
-
- // Returns parsed config file
- let conf = Config {
- base: config.base,
- mode: config.mode,
- repositories: expanded_repos,
- };
- log!(verbose, "Config: {:?}", conf);
-
- conf
-}
diff --git a/src/internal/strings.rs b/src/internal/strings.rs
deleted file mode 100755
index 390679f..0000000
--- a/src/internal/strings.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use colored::Colorize;
-use std::process::exit;
-use std::time::UNIX_EPOCH;
-
-use crate::internal::AppExitCode;
-
-const LOGO_SYMBOL: &str = "μ";
-const ERR_SYMBOL: &str = "❌";
-
-#[macro_export]
-macro_rules! info {
- ($($arg:tt)+) => {
- $crate::internal::strings::info_fn(&format!($($arg)+));
- }
-}
-
-#[macro_export]
-macro_rules! log {
- ($verbose:expr, $($arg:tt)+) => {
- $crate::internal::strings::log_fn(&format!("[{}:{}] {}", file!(), line!(), format!($($arg)+)), $verbose);
- }
-}
-
-#[macro_export]
-macro_rules! crash {
- ($exit_code:expr, $($arg:tt)+) => {
- $crate::internal::strings::crash_fn(&format!("[{}:{}] {}", file!(), line!(), format!($($arg)+)), $exit_code)
- }
-}
-
-#[macro_export]
-macro_rules! prompt {
- (default $default:expr, $($arg:tt)+) => {
- $crate::internal::strings::prompt_fn(&format!($($arg)+), $default)
- };
-}
-
-pub fn info_fn(msg: &str) {
- println!("{} {}", LOGO_SYMBOL.green(), msg.bold());
-}
-
-pub fn log_fn(msg: &str, verbose: bool) {
- if verbose {
- eprintln!(
- "{} {}",
- std::time::SystemTime::now()
- .duration_since(UNIX_EPOCH)
- .unwrap()
- .as_secs(),
- msg
- );
- }
-}
-
-pub fn crash_fn(msg: &str, exit_code: AppExitCode) {
- println!("{} {}", ERR_SYMBOL.red(), msg.bold());
- exit(exit_code as i32);
-}
-
-pub fn prompt_fn(msg: &str, default: bool) -> bool {
- let yn = if default { "[Y/n]" } else { "[y/N]" };
- print!("{} {} {}", "?".bold().green(), msg.bold(), yn);
- let mut input = String::new();
- std::io::stdin().read_line(&mut input).unwrap();
-
- let input = input.trim().to_lowercase();
-
- if input == "y" || input == "yes" {
- true
- } else if input == "n" || input == "no" {
- false
- } else {
- default
- }
-}
diff --git a/src/internal/structs.rs b/src/internal/structs.rs
deleted file mode 100755
index c023b12..0000000
--- a/src/internal/structs.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-use serde_derive::Deserialize;
-use std::collections::HashMap;
-
-//// Config structs
-#[derive(Debug, Deserialize)]
-pub struct Config {
- pub base: ConfigBase,
- pub mode: ConfigMode,
- pub repositories: Vec,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct UnexpandedConfig {
- pub base: ConfigBase,
- pub mode: ConfigMode,
- pub repositories: ConfigRepositories,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ConfigBase {
- pub mode: String,
- pub smart_pull: bool,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ConfigMode {
- pub repository: Option,
- pub workspace: Option,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ConfigModeRepository {
- pub name: String,
- pub build_on_update: bool,
- pub signing: ConfigModeRepositorySigning,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ConfigModeRepositorySigning {
- pub enabled: bool,
- pub key: Option,
- pub on_gen: Option,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ConfigModeWorkspace {
- pub git_info: bool,
- pub colorblind: bool,
- /* pub backup: bool,
- pub backup_dir: Option, TODO: Implement backup
- */
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ConfigRepositories {
- pub repos: Vec,
- pub urls: HashMap,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ConfigRepositoriesExpanded {
- pub repos: Vec,
-}
-
-//// Repository structs
-#[derive(Debug, Deserialize)]
-pub struct Repo {
- pub name: String,
- pub url: String,
- pub branch: Option,
- pub extra: Option,
- pub priority: usize,
-}
-
-#[derive(Debug)]
-pub struct SplitRepo {
- pub id: String,
- pub name: String,
- pub extra: Option,
-}
-
-//// Build operation structs
-#[derive(Debug)]
-pub struct ErroredPackage {
- pub name: String,
- pub code: i32,
-}
diff --git a/src/lock.rs b/src/lock.rs
new file mode 100644
index 0000000..398da7a
--- /dev/null
+++ b/src/lock.rs
@@ -0,0 +1,86 @@
+use std::collections::HashMap;
+
+use serde::{Deserialize, Serialize};
+
+use crate::config::Config;
+use crate::errors::{AppError, ConfigError};
+use crate::git::GitRepository;
+
+pub const LOCKFILE_VERSION: &str = "1.0";
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Lockfile {
+ pub lockfile: Meta,
+ pub remote: HashMap,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Meta {
+ pub version: String,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Remote {
+ pub origin: String,
+ pub commit: String,
+ pub branch: String,
+}
+
+impl Lockfile {
+ pub fn new(config: &Config) -> Result {
+ let path = config.base.src.join(".mlc").join("mlc.lock");
+
+ let lockfile = match std::fs::read_to_string(path) {
+ Ok(lockfile) => toml::from_str(&lockfile)?,
+ Err(_) => Self {
+ lockfile: Meta {
+ version: LOCKFILE_VERSION.to_string(),
+ },
+ remote: HashMap::new(),
+ },
+ };
+
+ Ok(lockfile)
+ }
+
+ pub fn update(&mut self, config: &Config) -> Result<&mut Self, AppError> {
+ for repo in &config.repositories.names {
+ let git = GitRepository::new(config.base.src.join(&repo.name));
+ let config_url = repo.expand(config)?;
+ let remote_url = git.origin()?;
+
+ if config_url != remote_url {
+ git.update_origin(&config_url)?;
+ git.pull()?;
+ }
+
+ if let Some(branch) = &repo.rev {
+ if branch != &git.current_branch()? {
+ git.checkout(branch)?;
+ }
+ }
+
+ if repo.rev.is_none() && git.current_branch()? != git.default_branch()? {
+ git.checkout(git.default_branch()?.as_str())?;
+ }
+
+ let remote = Remote {
+ origin: config_url,
+ commit: git.hash()?,
+ branch: git.current_branch()?,
+ };
+
+ self.remote.insert(repo.name.clone(), remote);
+ }
+
+ Ok(self)
+ }
+
+ pub fn save(&self) -> Result<(), AppError> {
+ let lockfile = toml::to_string_pretty(&self)
+ .map_err(|e| AppError::Config(ConfigError::Serialize(e)))?;
+ std::fs::write(".mlc/mlc.lock", lockfile)?;
+
+ Ok(())
+ }
+}
diff --git a/src/main.rs b/src/main.rs
old mode 100755
new mode 100644
index ea6879c..21c643b
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,88 +1,237 @@
-#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
-#![allow(clippy::too_many_lines)]
-
use clap::Parser;
+use futures_util::future;
+use lazy_static::lazy_static;
+use std::sync::Arc;
+use tokio::sync::Semaphore;
-use crate::args::{Args, Operation};
-use crate::internal::parse_cfg;
-use crate::internal::AppExitCode;
+use args::Args;
+use args::GlobalArgs;
+use args::Operation;
+use errors::AppError;
+use errors::AppResult;
+use git::GitRepository;
+use utils::uid;
-#[cfg(target_os = "linux")]
-#[global_allocator]
-static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
+use crate::build::{Build, BuildKind};
+use crate::config::Config;
+use crate::generate::GenerateKind;
+use crate::info::GitInfo;
+use crate::info::{BOLD, CLEAN, DIRTY, RESET, UNKNOWN};
+use crate::lock::Lockfile;
+use crate::prune::PackageFiles;
mod args;
-mod internal;
-mod operations;
-mod repository;
+mod build;
+mod builders;
+mod config;
+mod errors;
+mod generate;
+mod git;
+mod i18n;
+mod info;
+mod lock;
+mod prune;
+mod utils;
+
+lazy_static!(
+ #[derive(Clone, Debug)]
+ pub static ref GLOBAL_ARGS: GlobalArgs = GlobalArgs::new();
+);
-fn repository(verbose: bool) -> bool {
- // Parse config
- let config = parse_cfg(verbose);
- log!(verbose, "Config: {:?}", config);
+#[tokio::main]
+pub async fn main() -> AppResult<()> {
+ color_eyre::install().unwrap();
+ if uid() == 0 {
+ return Err(AppError::RunAsRoot);
+ }
- // Get repository mode status
- let repository = config.base.mode == "repository";
- log!(verbose, "Repository Mode: {:?}", repository);
+ let args: Args = Args::parse();
- // Return repository mode status
- repository
+ match args.subcommand {
+ Operation::Build {
+ packages,
+ concurrent,
+ } => cmd_build(packages, concurrent).await?,
+ Operation::Generate => cmd_generate().await?,
+ Operation::Init => cmd_init()?,
+ Operation::Pull {
+ rebuild,
+ concurrent,
+ } => cmd_pull(rebuild, concurrent).await?,
+ Operation::Clean { prune } => cmd_clean(prune).await?,
+ Operation::Info => cmd_info()?,
+ }
+
+ Ok(())
+}
+
+fn cmd_init() -> AppResult<()> {
+ Config::init()?;
+ Ok(())
}
-fn main() {
- #[cfg(target_os = "linux")]
- if unsafe { libc::geteuid() } == 0 {
- crash!(AppExitCode::RunAsRoot, "Running malachite as root is disallowed as it can lead to system breakage. Instead, malachite will prompt you when it needs superuser permissions");
+async fn cmd_pull(rebuild: bool, concurrent: Option) -> AppResult<()> {
+ let config = Config::load()?;
+
+ for repository in &config.repositories.names {
+ let expanded = repository.expand(&config)?;
+ let path = config.base.src.join(&repository.name);
+
+ if path.exists() {
+ GitRepository::new(path).pull()?;
+ } else {
+ GitRepository::clone_repo(path, &expanded, repository.rev.clone())?;
+ }
}
- // Get required variables
- let args: Args = Args::parse();
- let exclude = &args.exclude;
- let verbose = args.verbose;
- log!(verbose, "Args: {:?}", args);
- log!(verbose, "Exclude: {:?}", exclude);
- log!(verbose, "Verbose: You guess. :)");
-
- // Arg matching
- match args.subcommand.unwrap_or(Operation::Clone) {
- Operation::Clone => operations::clone(verbose),
- Operation::Build {
- packages, no_regen, ..
- } => {
- if !repository(verbose) {
- crash!(
- AppExitCode::ConfigParseError,
- "Cannot build packages in workspace mode"
- );
+ let mut lock = Lockfile::new(&config)?;
+ let old_lock = lock.clone();
+ lock.update(&config)?;
+
+ if rebuild {
+ let mut packages_to_rebuild = vec![];
+
+ for (name, remote) in &old_lock.remote {
+ if let Some(new_remote) = lock.remote.get(name) {
+ if remote.commit != new_remote.commit {
+ packages_to_rebuild.push(name.clone());
+ }
}
- operations::build(&packages, exclude.clone(), no_regen, verbose);
}
- Operation::Pull {
- packages,
- no_regen,
- interactive,
- ..
- } => operations::pull(packages, exclude, verbose, no_regen, interactive),
- Operation::RepoGen => {
- if !repository(verbose) {
- crash!(
- AppExitCode::ConfigParseError,
- "Cannot generate repository in workspace mode"
- );
- }
- repository::generate(verbose);
+
+ if !packages_to_rebuild.is_empty() {
+ cmd_build(packages_to_rebuild, concurrent).await?;
}
- Operation::Config => operations::config(verbose),
- Operation::Prune => {
- if !repository(verbose) {
- crash!(
- AppExitCode::ConfigParseError,
- "Cannot prune packages in workspace mode"
- );
+ }
+
+ lock.save()?;
+
+ Ok(())
+}
+
+async fn cmd_clean(prune: Option) -> AppResult<()> {
+ let config = Config::load()?;
+
+ let excluded = [
+ &config.repo.repo,
+ &config.repo.out,
+ &config.base.src.join(".mlc"),
+ &config.base.src.join(".git"),
+ ];
+
+ let mut to_delete = Vec::new();
+ for entry in std::fs::read_dir(&config.base.src)? {
+ let entry = entry?;
+ let path = entry.path();
+ if path.is_dir() {
+ let name = path.file_name().map(|s| s.to_string_lossy()).unwrap();
+ if !config.repositories.names.iter().any(|x| x.name == name)
+ && !excluded.contains(&&path)
+ {
+ to_delete.push(path);
}
- operations::prune(verbose);
}
- Operation::Clean { force, .. } => operations::clean(verbose, force),
- Operation::Info => operations::info(verbose),
}
+
+ for path in to_delete {
+ std::fs::remove_dir_all(path)?;
+ }
+
+ if let Some(prune) = prune {
+ let mut packages = PackageFiles::new();
+ packages.scan(&config.repo.out)?;
+ let pruned = packages.prune(prune)?;
+ if pruned > 0 {
+ cmd_generate().await?;
+ }
+ }
+
+ Ok(())
+}
+
+async fn cmd_build(packages: Vec, concurrent: Option) -> AppResult<()> {
+ let config = Config::load()?;
+
+ let kind = match config.base.podman {
+ true => BuildKind::Podman {
+ image: config.base.image,
+ },
+ false => BuildKind::Host,
+ };
+
+ let concurrent = if let Some(concurrent) = concurrent {
+ concurrent
+ } else {
+ 1
+ };
+
+ let out = config.repo.out;
+
+ let sem = Arc::new(Semaphore::new(concurrent as usize));
+ let results = future::join_all(packages.into_iter().map(|name| async {
+ let sem = sem.clone();
+ let out = out.clone();
+ let kind = kind.clone();
+
+ let perm = sem.acquire().await.unwrap();
+ let build = Build::new(kind, name, out, vec![])?;
+ build.build().await?;
+ drop(perm);
+
+ Ok::<_, AppError>(())
+ }))
+ .await;
+
+ for result in results {
+ result?;
+ }
+
+ Ok(())
+}
+
+async fn cmd_generate() -> AppResult<()> {
+ let config = Config::load()?;
+
+ let kind = match config.base.podman {
+ true => GenerateKind::Podman {
+ image: config.base.image,
+ },
+ false => GenerateKind::Host,
+ };
+
+ generate::PacmanRepository::new(
+ kind,
+ config.repo.name,
+ config.repo.out,
+ config.repo.repo,
+ config.repo.security,
+ )
+ .generate()
+ .await?;
+
+ Ok(())
+}
+
+fn cmd_info() -> AppResult<()> {
+ let config = Config::load()?;
+ let repositories = config.repositories.names;
+
+ let mut overview = Vec::new();
+
+ for repo in repositories {
+ let path = config.base.src.join(&repo.name);
+
+ overview.push(GitInfo::new(path)?);
+ }
+
+ let mut table = tabled::Table::new(overview);
+ table.with(tabled::Style::rounded());
+
+ println!("{}", table);
+ println!(
+ " {}Key:{} {}Clean {}Unknown {}Dirty{}",
+ BOLD, RESET, CLEAN, UNKNOWN, DIRTY, RESET
+ );
+
+ Ok(())
}
diff --git a/src/operations/build.rs b/src/operations/build.rs
deleted file mode 100644
index 8a26888..0000000
--- a/src/operations/build.rs
+++ /dev/null
@@ -1,140 +0,0 @@
-use crate::internal::structs::{ErroredPackage, Repo};
-use crate::internal::AppExitCode;
-use crate::{crash, info, log, repository};
-
-pub fn build(packages: &[String], exclude: Vec, no_regen: bool, verbose: bool) {
- // Read config struct from mlc.toml
- let config = crate::internal::parse_cfg(verbose);
- log!(verbose, "Config: {:?}", config);
- // Check if any packages were passed, if not, imply all
- let all = packages.is_empty();
- log!(verbose, "All: {:?}", all);
-
- // Read signing
- let signing = config.mode.repository.as_ref().unwrap().signing.enabled;
-
- // Read on_gen
- let on_gen = config.mode.repository.as_ref().unwrap().signing.on_gen;
-
- // Parse whether to sign on build or not
- let sign = if signing && on_gen.is_some() && on_gen.unwrap() {
- false
- } else {
- signing
- };
- log!(verbose, "Signing: {:?}", sign);
-
- // Get list of repos and subtract exclude
- let mut repos: Vec = config.repositories;
- log!(verbose, "{} Repos: {:?}", repos.len(), repos);
- if !exclude.is_empty() {
- log!(verbose, "Exclude not empty: {:?}", exclude);
- for ex in exclude {
- repos.retain(|x| *x.name != ex);
- }
- }
-
- log!(
- verbose,
- "Exclusions parsed. Now {} Repos: {:?}",
- repos.len(),
- repos
- );
-
- // If packages is not empty and all isn't specified, build specified packages
- let mut errored: Vec = vec![];
- if !packages.is_empty() && !all {
- log!(verbose, "Packages not empty: {:?}", packages);
- for pkg in packages.iter() {
- // If repo is not in config, crash, otherwise, build
- if repos.iter().map(|x| x.name.clone()).any(|x| x == *pkg) {
- // Otherwise, build
- log!(verbose, "Building {}", pkg);
-
- let code = repository::build(pkg, sign, verbose);
- log!(
- verbose,
- "Package {} finished with exit code: {:?}",
- pkg,
- code
- );
-
- if code != 0 {
- let error = ErroredPackage {
- name: pkg.to_string(),
- code,
- };
- errored.push(error);
- }
- } else {
- crash!(
- AppExitCode::PkgsNotFound,
- "Package repo {} not found in in mlc.toml",
- pkg
- );
- }
- }
- }
-
- // If all is specified, attempt to build a package from all repos
- if all {
- log!(verbose, "Proceeding to build all");
-
- // Sort by package priority
- log!(verbose, "Sorting by priority: {:?}", repos);
- repos.sort_by(|a, b| b.priority.cmp(&a.priority));
- log!(verbose, "Sorted: {:?}", repos);
- for pkg in repos {
- log!(verbose, "Building {}", pkg.name);
-
- let code = repository::build(&pkg.name, sign, verbose);
- log!(
- verbose,
- "Package {} finished with exit code: {:?}",
- pkg.name,
- code
- );
-
- if code != 0 {
- let error = ErroredPackage {
- name: pkg.name,
- code,
- };
- errored.push(error);
- }
- }
- }
-
- // If all is not specified, but packages is empty, crash
- if !all && packages.is_empty() {
- log!(verbose, "Packages empty. Crashing");
- crash!(AppExitCode::PkgsNotFound, "No packages specified");
- }
-
- // If no_regen is passed, do not generate a repository
- if !no_regen {
- log!(verbose, "Generating repository");
- repository::generate(verbose);
- }
-
- // Map errored packages to a string for display
- let error_strings: Vec = errored
- .iter()
- .map(|x| format!("{}: Returned {}", x.name, x.code))
- .collect();
-
- // If errored is not empty, let the user know which packages failed
- if !errored.is_empty() {
- log!(verbose, "Errored packages: \n{:?}", error_strings);
- info!(
- "The following packages build jobs returned a non-zero exit code: \n {}",
- error_strings.join("\n ")
- );
- info!("Please check `man 8 makepkg` for more information");
- // Check if code 63 appeared at all
- if errored.iter().any(|x| x.code == 63) {
- log!(verbose, "Code 63 found");
- info!("Note: Code 63 is an internal Malachite exit code, and specifies that no PKGBUILD was found.");
- }
- }
-}
diff --git a/src/operations/clean.rs b/src/operations/clean.rs
deleted file mode 100644
index a6c6041..0000000
--- a/src/operations/clean.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use crate::{crash, info, internal::AppExitCode, log};
-
-pub fn clean(verbose: bool, force: bool) {
- info!("Resetting mlc repo, deleting all directories");
- // Get a vec of all files/dirs in the current directory
- let dir_paths = std::fs::read_dir(".").unwrap();
- log!(verbose, "Paths: {:?}", dir_paths);
- let mut dirs = dir_paths
- .map(|x| x.unwrap().path().display().to_string())
- .collect::>();
-
- // Remove mlc.toml and .git from output
- dirs.retain(|x| *x != "./mlc.toml" && *x != ".\\mlc.toml");
- dirs.retain(|x| *x != "./.git" && *x != ".\\.git");
- dirs.retain(|x| *x != "./.gitignore" && *x != ".\\.gitignore");
- dirs.retain(|x| *x != "./.gitmodules" && *x != ".\\.gitmodules");
- dirs.retain(|x| *x != "./README.md" && *x != ".\\README.md");
-
- let mut unclean_dirs = vec![];
-
- // Enter each directory and check git status
- for dir in &dirs {
- let root_dir = std::env::current_dir().unwrap();
-
- log!(verbose, "Entering directory: {}", dir);
- std::env::set_current_dir(dir).unwrap();
-
- let status = std::process::Command::new("git")
- .arg("status")
- .output()
- .unwrap();
-
- let output = std::string::String::from_utf8(status.stdout).unwrap();
- log!(verbose, "Git status: {}", output);
-
- if output.contains("Your branch is up to date with")
- && !output.contains("Untracked files")
- && !output.contains("Changes not staged for commit")
- {
- log!(verbose, "Directory {} is clean", dir);
- } else {
- unclean_dirs.push(dir);
- }
-
- std::env::set_current_dir(&root_dir).unwrap();
- log!(verbose, "Current directory: {}", root_dir.display());
- }
-
- if !unclean_dirs.is_empty() && !force && crate::parse_cfg(verbose).base.mode == "workspace" {
- crash!(
- AppExitCode::RepoNotClean,
- "The following directories are not clean: \n {}\n\
- If you are sure no important changes are staged, run `mlc clean` with the `--force` flag to delete them.",
- unclean_dirs.iter().map(|x| (*x).to_string().replace("./", "").replace(".\\", "")).collect::>().join(", ")
- );
- }
-
- log!(verbose, "Paths with mlc.toml excluded: {:?}", dirs);
- for dir in &dirs {
- log!(verbose, "Deleting directory: {}", dir);
- rm_rf::remove(dir).unwrap();
- }
- info!(
- "Reset complete, dirs removed: \n \
- {}",
- dirs.iter()
- .map(|x| x.replace("./", "").replace(".\\", ""))
- .collect::>()
- .join("\n ")
- );
-}
diff --git a/src/operations/clone.rs b/src/operations/clone.rs
deleted file mode 100644
index f7ab993..0000000
--- a/src/operations/clone.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-use std::env;
-use std::process::Command;
-
-use crate::{info, log};
-
-pub fn clone(verbose: bool) {
- // Read config struct from mlc.toml
- let config = crate::internal::parse_cfg(verbose);
- log!(verbose, "Config: {:?}", config);
- // Parse repositories from config
- let repos = &config.repositories;
- log!(verbose, "Repos: {:?}", repos);
-
- // Get a vector of all files/dirs in the current directory, excluding config file
- let dir_paths = std::fs::read_dir(".").unwrap();
- let mut dirs = dir_paths
- .map(|x| x.unwrap().path().display().to_string())
- .collect::>();
-
- // Remove mlc.toml and .git from output
- dirs.retain(|x| *x != "./mlc.toml" && *x != ".\\mlc.toml");
- dirs.retain(|x| *x != "./.git" && *x != ".\\.git");
-
- // If mode is repository, also exclude repository mode directories
- if config.mode.repository.is_some() {
- dirs.retain(|x| {
- *x != format!("./{}", config.mode.repository.as_ref().unwrap().name)
- && *x != format!(".\\{}", config.mode.repository.as_ref().unwrap().name)
- });
- dirs.retain(|x| *x != "./out" && *x != ".\\out");
- }
- log!(verbose, "Paths with mlc.toml excluded: {:?}", dirs);
-
- // Creates a vector of the difference between cloned repos and repos defined in config
- let mut repo_diff = vec![];
- for repo in repos {
- let name = &repo.name;
-
- if !dirs.contains(&format!("./{}", name)) && !dirs.contains(&format!(".\\{}", name)) {
- repo_diff.push(repo);
- }
- }
-
- // Diff logic
- if repo_diff.is_empty() {
- // No diff, do nothing
- log!(verbose, "No diff");
- info!("All repos are already cloned");
- } else {
- log!(verbose, "Diff: {:?}", repo_diff);
- // This is just for pretty display purposes
- let display = repo_diff
- .iter()
- .map(|x| x.name.to_string())
- .collect::>()
- .join(" ");
- info!("New/missing repos to clone: {}", display);
-
- // Clone all diff repos
- for r in repo_diff {
- log!(verbose, "Depth: {:?}", r.extra);
- log!(verbose, "Cloning {}", r.name);
-
- if r.extra.is_some() && config.base.mode == "workspace" {
- info!(
- "Cloning ({} mode): {} with `--depth {}`",
- config.base.mode,
- r.name,
- r.extra.as_ref().unwrap()
- );
- } else if r.extra.is_some() && config.base.mode == "repository" {
- info!(
- "Cloning ({} mode): {} at {}",
- config.base.mode,
- r.name,
- r.extra.as_ref().unwrap()
- );
- } else {
- info!("Cloning ({} mode): {}", config.base.mode, r.name);
- }
-
- if r.extra.is_some() && config.base.mode == "workspace" {
- // Clone with specified extra depth
- Command::new("git")
- .args(&["clone", &r.url, &r.name])
- // If a branch is specified, clone that specific branch
- .args(if r.branch.is_some() {
- vec!["-b", r.branch.as_ref().unwrap()]
- } else {
- vec![]
- })
- .args(if r.extra.is_some() {
- vec!["--depth", r.extra.as_ref().unwrap()]
- } else {
- vec![]
- })
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- } else if config.base.mode == "repository" {
- // Clone and checkout specified hash
- // Create an empty directory with repo.name and enter it
- let root_dir = env::current_dir().unwrap();
-
- // Git clone the repo
- Command::new("git")
- .args(&["clone", &r.url, &r.name])
- .args(if r.branch.is_some() {
- vec!["-b", r.branch.as_ref().unwrap()]
- } else {
- vec![]
- })
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
-
- std::env::set_current_dir(&r.name).unwrap();
- log!(verbose, "Entered directory: {}", r.name);
-
- // Git checkout the PKGBUILD from the hash
- if r.extra.is_some() {
- Command::new("git")
- .args(&["checkout", r.extra.as_ref().unwrap()])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- }
-
- // Return to the root directory
- std::env::set_current_dir(root_dir).unwrap();
- log!(verbose, "Returned to root directory");
- } else {
- // Clone normally
- Command::new("git")
- .args(&["clone", &r.url, &r.name])
- .args(if r.branch.is_some() {
- vec!["-b", r.branch.as_ref().unwrap()]
- } else {
- vec![]
- })
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- }
- }
- }
-}
diff --git a/src/operations/config.rs b/src/operations/config.rs
deleted file mode 100644
index 503fe25..0000000
--- a/src/operations/config.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use std::env;
-use std::path::Path;
-use std::process::Command;
-
-use crate::{log, repository::create};
-
-pub fn config(verbose: bool) {
- // Generate new config file if not already present
- if !Path::exists("mlc.toml".as_ref()) {
- log!(verbose, "Creating mlc.toml");
- create(verbose);
- }
-
- // Open config file in user's editor of choice
- let editor = env::var("EDITOR").unwrap_or_else(|_| "nano".to_string());
- log!(verbose, "Opening mlc.toml in {}", editor);
- Command::new(editor)
- .arg("mlc.toml")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
-}
diff --git a/src/operations/info.rs b/src/operations/info.rs
deleted file mode 100644
index c281df4..0000000
--- a/src/operations/info.rs
+++ /dev/null
@@ -1,286 +0,0 @@
-use colored::Colorize;
-use spinoff::{Color, Spinner, Spinners};
-use std::env;
-use std::fmt::Write;
-use std::path::Path;
-use std::process::Command;
-use tabled::Tabled;
-
-use crate::{crash, info, internal::AppExitCode, log};
-
-// For displaying the table of contents
-#[derive(Clone, tabled::Tabled, Debug)]
-struct RepoDisplayGit {
- #[tabled(rename = "Name")]
- name: String,
- #[tabled(rename = "URL")]
- url: String,
- #[tabled(skip)]
- priority: usize,
- #[tabled(rename = "Git Info")]
- git_info: String,
-}
-
-#[derive(Clone, tabled::Tabled, Debug)]
-struct RepoDisplay {
- #[tabled(rename = "Name")]
- name: String,
- #[tabled(rename = "URL")]
- url: String,
- #[tabled(skip)]
- priority: usize,
-}
-
-pub fn git_status(verbose: bool, repo: &str, colorblind: bool) -> String {
- let dir = env::current_dir().unwrap();
- log!(
- verbose,
- "Current directory: {}",
- env::current_dir().unwrap().display()
- );
- env::set_current_dir(&repo).unwrap_or_else(|e| {
- crash!(
- AppExitCode::RepoParseError,
- "Failed to enter directory {} for Git info: {}, Have you initialized the repo?",
- repo,
- e.to_string()
- );
- });
- log!(verbose, "Current directory: {}", repo);
-
- let output = Command::new("git").arg("status").output().unwrap();
- let output = String::from_utf8(output.stdout).unwrap();
- log!(verbose, "Git status: {}", output);
-
- let unstaged = output.contains("Changes not staged for commit")
- || output.contains("Changes to be committed");
- let untracked = output.contains("Untracked files");
- let dirty = unstaged || untracked;
-
- let pull = output.contains("Your branch is behind");
- let push = output.contains("Your branch is ahead");
-
- let latest_commit = Command::new("git")
- .args(&["log", "--pretty=%h", "-1"])
- .output()
- .unwrap();
- let mut latest_commit = String::from_utf8(latest_commit.stdout).unwrap();
- latest_commit.retain(|c| !c.is_whitespace());
-
- let output = if colorblind {
- format!(
- "{} {} {} {}",
- if dirty { "D".red() } else { "D".bright_blue() },
- if pull { "Pl".red() } else { "Pl".bright_blue() },
- if push { "Ps".red() } else { "Ps".bright_blue() },
- latest_commit
- )
- } else {
- format!(
- "{} {} {} {}",
- if dirty { "D".red() } else { "D".green() },
- if pull { "Pl".red() } else { "Pl".green() },
- if push { "Ps".red() } else { "Ps".green() },
- latest_commit
- )
- };
- env::set_current_dir(&dir).unwrap();
- log!(verbose, "Current directory: {}", dir.display());
- output
-}
-
-pub fn info(verbose: bool) {
- log!(verbose, "Showing Info");
- // Parse config from mlc.toml
- let config = crate::internal::parse_cfg(verbose);
- log!(verbose, "Config: {:?}", config);
-
- // Check for git_info
- let git_info = if config.mode.workspace.is_some() {
- config.mode.workspace.as_ref().unwrap().git_info
- } else {
- false
- };
- log!(verbose, "Git info: {}", git_info);
-
- // Check for colorblind mode
- let colorblind = if config.mode.workspace.is_some() {
- config.mode.workspace.as_ref().unwrap().colorblind
- } else {
- false
- };
- log!(verbose, "Colorblind: {}", colorblind);
-
- // Add the branch to the name if it's not the default branch for said repository
- let repos_unparsed = config.repositories;
- let mut repos = vec![];
- let mut repos_git = vec![];
-
- if git_info {
- // Crash early if directories are not found for git_info
- for repo in &repos_unparsed {
- if !Path::new(&repo.name).exists() {
- crash!(
- AppExitCode::RepoParseError,
- "Failed to check directory {} for Git info, have you initialized the repo?",
- repo.name,
- );
- };
- }
-
- // Start the spinner
- let sp = Spinner::new(
- Spinners::Line,
- format!("{}", "Parsing Git Info...".bold()),
- Color::Green,
- );
-
- // Construct bash script to run git remote upgrade on all repos asynchronously
- // This helps speed up the operation when, for example, you have a lot of repositories and you store your SSH key as a subkey of your GPG key on a yubikey
- // This took my `mlc info` time down from 17s to 8s (i have the above described setup)
- let mut bash_script = String::new();
- bash_script.push_str(
- "\n\
- #!/usr/bin/env bash\n\
- \n\
- # This script will run `git remote update` in all repositories\n\
- pull() { cd $1; git remote update; cd -; }\n\
- \n",
- );
- for repo in &repos_unparsed {
- writeln!(bash_script, "pull {} &", repo.name).unwrap();
- }
- bash_script.push_str("wait\n");
-
- log!(verbose, "Bash script: {}", bash_script);
-
- // Run the bash script
- Command::new("bash")
- .arg("-c")
- .arg(bash_script)
- .output()
- .unwrap();
-
- // Stop the spinner with a success message
- let text = format!("{}", "Parsing Git Info... Done".bold());
- let symbol = format!("{}", "✔".bold().green());
-
- sp.stop_and_persist(&symbol, &text);
- log!(verbose, "Repos: {:?}", repos);
- }
-
- // Iterate over all repositories
- for repo in repos_unparsed {
- // Get name with branch, '/' serving as the delimiter
- let name = if repo.branch.is_some() {
- format!("{}/{}", repo.name, repo.branch.unwrap())
- } else {
- repo.name.clone()
- };
-
- // Get git info, if applicable
- let git_info_string = if git_info {
- let info = Some(git_status(
- verbose,
- &repo.name,
- config.mode.workspace.as_ref().unwrap().colorblind,
- ));
- info
- } else {
- None
- };
-
- // Push to the correct vector, we're using a separate vector for git info because
- // the struct we're displaying is different
- if git_info {
- repos_git.push(RepoDisplayGit {
- name,
- url: repo.url.clone(),
- priority: repo.priority,
- git_info: git_info_string.unwrap(),
- });
- } else {
- repos.push(RepoDisplay {
- name,
- url: repo.url.clone(),
- priority: repo.priority,
- });
- }
- }
-
- // Sort by priority
- repos.sort_by(|a, b| b.priority.cmp(&a.priority));
- repos_git.sort_by(|a, b| b.priority.cmp(&a.priority));
- if git_info {
- log!(verbose, "Repos Sorted: {:?}", repos_git);
- } else {
- log!(verbose, "Repos Sorted: {:?}", repos);
- }
-
- // Displaying basic info about the Malachite Repository
- let internal_name = if config.mode.repository.is_none()
- || config.mode.repository.as_ref().unwrap().name.is_empty()
- {
- env::current_dir()
- .unwrap()
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- .to_string()
- } else {
- config.mode.repository.unwrap().name
- };
- let name = format!(
- "{} \"{}\":",
- // Sidenote: It should NOT be this convoluted to capitalise the first character of a string in rust. What the fuck.
- String::from_utf8_lossy(&[config.base.mode.as_bytes()[0].to_ascii_uppercase()])
- + &config.base.mode[1..],
- internal_name
- );
-
- // Get terminal width for table formatting
- let width = match crossterm::terminal::size() {
- Ok((w, _)) => w,
- Err(_) => 80,
- };
-
- // Create table for displaying info
- let table = if git_info {
- tabled::Table::new(&repos_git)
- .with(tabled::Style::modern())
- .with(tabled::Width::wrap(width as usize).keep_words())
- .to_string()
- } else {
- tabled::Table::new(&repos)
- .with(tabled::Style::modern())
- .with(tabled::Width::wrap(width as usize).keep_words())
- .to_string()
- };
-
- // Get length of Vec for displaying in the table
- let len = if git_info {
- repos_git.len()
- } else {
- repos.len()
- };
-
- // Print all of the info
- info!("{}", name);
- info!("Total Repositories: {}", len.to_string().green());
- println!("{}", table);
- if config.mode.workspace.is_some() && config.mode.workspace.as_ref().unwrap().git_info {
- info!(
- "D: Dirty - Unstaged Changes \n \
- Pl: Pull - Changes at Remote \n \
- Ps: Push - Unpushed Changes \n \
- {}: Dirty, {}: Clean",
- " ".on_red(),
- if config.mode.workspace.unwrap().colorblind {
- " ".on_bright_blue()
- } else {
- " ".on_green()
- }
- );
- }
-}
diff --git a/src/operations/mod.rs b/src/operations/mod.rs
deleted file mode 100644
index 82a8a46..0000000
--- a/src/operations/mod.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-pub use build::*;
-pub use clean::*;
-pub use clone::*;
-pub use config::*;
-pub use info::*;
-pub use prune::*;
-pub use pull::*;
-
-mod build;
-mod clean;
-mod clone;
-mod config;
-mod info;
-mod prune;
-mod pull;
diff --git a/src/operations/prune.rs b/src/operations/prune.rs
deleted file mode 100644
index 7af2204..0000000
--- a/src/operations/prune.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-use colored::Colorize;
-use std::env;
-use std::fs;
-use std::path::PathBuf;
-
-use crate::info;
-use crate::log;
-use crate::parse_cfg;
-
-#[derive(Debug, Clone)]
-struct PackageFile {
- name: String,
- ver: String,
- ext: String,
-}
-
-pub fn prune(verbose: bool) {
- // Read config struct from mlc.toml
- let config = parse_cfg(verbose);
- log!(verbose, "Config: {:?}", config);
-
- // Read current directory
- let current_dir = env::current_dir().unwrap();
- log!(verbose, "Current dir: {:?}", current_dir);
-
- // Enter out directory
- env::set_current_dir("out").unwrap();
- log!(verbose, "Current dir: {:?}", env::current_dir().unwrap());
-
- // Read all files from . into a Vec, except for .sig files
- let mut files: Vec = vec![];
- for entry in fs::read_dir(".").unwrap() {
- let entry = entry.unwrap();
- let path = entry.path();
- if path.extension().unwrap() != "sig" {
- files.push(path);
- }
- }
- log!(verbose, "Files: {:?}", files);
-
- // Split files into Vec, turning package-name-1.0.0-1-x86_64.tar.gz into PackageFile { name: "package-name", ver: "1.0.0-1", ext: "x86_64.tar.gz" }
- let mut packages: Vec = vec![];
- for file in files {
- // Regex, splits package-name-1.0.0-1-x86_64.tar.gz into 3 groups: package-name, -1.0.0-1, -x86_64.tar.gz
- let re = regex::Regex::new(r"^(.+)(-.+-.+)(-.+\..+\..+\.+..+)$").unwrap();
-
- // Get file name to string
- let file = file.to_str().unwrap();
-
- // Match file name against regex
- for cap in re.captures_iter(file) {
- // Collect regex captures
- let name = cap[1].to_string();
- let mut ver = cap[2].to_string();
- let mut ext = cap[3].to_string();
-
- // Strip leading - from ver and ext
- ver.remove(0).to_string();
- ext.remove(0).to_string();
-
- let package = PackageFile { name, ver, ext };
- log!(verbose, "Package: {:?}", package);
- packages.push(package);
- }
- }
-
- // Split packages into a Vector of Vectors by unique name
- let mut packages_by_name: Vec> = vec![];
- for package in &packages {
- log!(verbose, "Sorting Package: {:?}", package);
- let name = &package.name;
- let mut found = false;
- // Check if name is already present in packages_by_name
- for p in &mut packages_by_name {
- if &p[0].name == name {
- log!(verbose, "Found {}", name);
- found = true;
- p.push(package);
- }
- }
- // If not, create a new vector and push to it
- if !found {
- log!(verbose, "Creating {}", name);
- packages_by_name.push(vec![package]);
- }
- }
-
- // Sort each Vector of Vectors by version
- for p in &mut packages_by_name {
- log!(verbose, "Sorting {:?}", p);
- p.sort_by(|a, b| b.ver.cmp(&a.ver));
- }
-
- // Pushes all but the 3 most recent versions of each package into a new Vector of PackageFiles
- let mut packages_to_delete: Vec = vec![];
- for p in &packages_by_name {
- let mut to_delete = vec![];
- for (i, _) in p.iter().enumerate() {
- if i >= 3 {
- log!(verbose, "Deleting {:?}", p[i]);
- to_delete.push(p[i].clone());
- }
- }
- packages_to_delete.extend(to_delete);
- }
- log!(verbose, "Packages to delete: {:?}", packages_to_delete);
-
- // Delete all packages in packages_to_delete
- for p in &packages_to_delete {
- let path = format!("{}-{}-{}", p.name, p.ver, p.ext);
- log!(verbose, "Deleting {}", path);
- std::process::Command::new("bash")
- .args(&["-c", &format!("rm -rf ./{} ./{}.sig", path, path)])
- .output()
- .unwrap();
- }
-
- // Return to current directory
- env::set_current_dir(current_dir).unwrap();
- log!(verbose, "Current dir: {:?}", env::current_dir().unwrap());
-
- // Print which packages were deleted
- if packages_to_delete.is_empty() {
- info!("No packages were deleted.");
- } else {
- info!("Deleted the following packages:");
- for p in &mut packages_to_delete {
- println!(
- "{}",
- format!(
- " {}-{}",
- p.name.replace("./", "").replace(".\\", ""),
- p.ver
- )
- .bold()
- );
- }
- }
-}
diff --git a/src/operations/pull.rs b/src/operations/pull.rs
deleted file mode 100644
index 3e79a2e..0000000
--- a/src/operations/pull.rs
+++ /dev/null
@@ -1,207 +0,0 @@
-use std::env;
-use std::process::Command;
-
-use crate::info;
-use crate::{crash, internal::AppExitCode, log, prompt};
-
-struct PullParams {
- smart_pull: bool,
- build_on_update: bool,
- no_regen: bool,
-}
-
-fn do_the_pulling(repos: Vec, verbose: bool, params: &PullParams, interactive: bool) {
- for repo in repos {
- // Set root dir to return after each git pull
- let root_dir = env::current_dir().unwrap();
- log!(verbose, "Root dir: {:?}", root_dir);
-
- // Enter repo dir
- info!("Entering working directory: {}", &repo);
- env::set_current_dir(&repo).unwrap();
- log!(verbose, "Current dir: {:?}", env::current_dir().unwrap());
-
- let mut packages_to_rebuild: Vec = vec![];
-
- // Pull logic
- log!(verbose, "Pulling");
- if params.smart_pull {
- // Update the remote
- log!(verbose, "Smart pull");
- Command::new("git")
- .args(&["remote", "update"])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
-
- // Check the repository status
- let output = Command::new("git").arg("status").output().unwrap();
-
- // If there are changes, pull normally
- if String::from_utf8(output.stdout)
- .unwrap()
- .to_string()
- .contains("Your branch is behind")
- {
- info!("Branch out of date, pulling changes");
- Command::new("git")
- .arg("pull")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
-
- // If build_on_update is set, rebuild package
- if params.build_on_update {
- if interactive {
- let cont = prompt!(default true, "Rebuild package {}?", &repo);
- if cont {
- info!("Package {} updated, staging for rebuild", &repo);
- log!(verbose, "Pushing package {} to be rebuilt", &repo);
- packages_to_rebuild.push(repo);
- } else {
- info!("Not rebuilding package {}", &repo);
- }
- } else {
- packages_to_rebuild.push(repo);
- }
- }
- } else {
- // If there are no changes, alert the user
- info!("No changes to pull");
- }
- } else {
- // Pull normally
- log!(verbose, "Normal pull");
- Command::new("git")
- .arg("pull")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- }
- // Return to root dir
- env::set_current_dir(&root_dir).unwrap();
- log!(
- verbose,
- "Returned to root dir: {:?}",
- env::current_dir().unwrap()
- );
-
- // Rebuild packages if necessary
- if !packages_to_rebuild.is_empty() && params.build_on_update {
- info!("Rebuilding packages: {}", &packages_to_rebuild.join(", "));
- log!(verbose, "Rebuilding packages: {:?}", &packages_to_rebuild);
-
- // Push to build
- crate::operations::build(&packages_to_rebuild, vec![], params.no_regen, verbose);
-
- // Ensure you are in root dir
- env::set_current_dir(root_dir).unwrap();
- log!(
- verbose,
- "Returned to root dir: {:?}",
- env::current_dir().unwrap()
- );
- }
- }
-}
-
-pub fn pull(
- packages: Vec,
- exclude: &[String],
- verbose: bool,
- no_regen: bool,
- interactive: bool,
-) {
- // Read config file
- let config = crate::parse_cfg(verbose);
- log!(verbose, "Config: {:?}", config);
-
- // If no packages are specified, imply all
- let all = packages.is_empty();
- log!(verbose, "All: {}", all);
-
- // Read smart_pull from config
- let smart_pull = config.base.smart_pull;
- log!(verbose, "Smart pull: {}", smart_pull);
-
- // Read build_on_update from config
- let build_on_update = if config.mode.repository.is_some() {
- config.mode.repository.unwrap().build_on_update
- } else {
- false
- };
- log!(verbose, "Build on update: {}", build_on_update);
-
- // Read repos from config
- let repos = config
- .repositories
- .iter()
- .map(|x| x.name.clone())
- .collect::>();
- log!(verbose, "Repos: {:?}", repos);
-
- // Set repos_applicable for next function
- let mut repos_applicable = if all { repos } else { packages };
- log!(verbose, "Repos applicable: {:?}", repos_applicable);
-
- // Subtract exclude from repos_applicable
- if !exclude.is_empty() {
- for ex in exclude.iter() {
- repos_applicable.retain(|x| *x != *ex);
- }
- }
- log!(verbose, "Exclude: {:?}", exclude);
- log!(verbose, "Repos applicable excluded: {:?}", repos_applicable);
-
- // If all is not specified and packages is empty, crash
- if repos_applicable.is_empty() {
- crash!(AppExitCode::PkgsNotFound, "No packages specified");
- }
-
- // Sort repos_applicable by priority
- repos_applicable.sort_by(|a, b| {
- config
- .repositories
- .iter()
- .find(|x| x.name == *a)
- .unwrap()
- .priority
- .cmp(
- &config
- .repositories
- .iter()
- .find(|x| x.name == *b)
- .unwrap()
- .priority,
- )
- });
-
- log!(verbose, "Pulling {:?}", repos_applicable);
-
- // If any repos are not in the config, run a clone
- for repo in &repos_applicable {
- if !std::path::Path::new(repo).exists() {
- info!(
- "Repo {} does not exist, ensuring all repos are cloned",
- repo
- );
- crate::operations::clone(verbose);
- break;
- }
- }
-
- // Pull!
- do_the_pulling(
- repos_applicable,
- verbose,
- &PullParams {
- smart_pull,
- build_on_update,
- no_regen,
- },
- interactive,
- );
-}
diff --git a/src/prune.rs b/src/prune.rs
new file mode 100644
index 0000000..bd14b53
--- /dev/null
+++ b/src/prune.rs
@@ -0,0 +1,99 @@
+use std::collections::HashMap;
+use std::fs;
+use std::fs::File;
+use std::path::Path;
+
+use crate::errors::AppResult;
+
+#[derive(Debug, Clone)]
+pub struct PackageFile {
+ pub path: String,
+ pub package: Package,
+}
+
+#[derive(Debug, Clone)]
+pub struct PackageFiles {
+ files: HashMap>,
+}
+
+#[derive(Debug, Clone)]
+pub struct Package {
+ pub name: String,
+ pub version: String,
+}
+
+impl Package {
+ pub fn new(name: String, version: String) -> Self {
+ Self { name, version }
+ }
+
+ pub fn read(path: &Path) -> AppResult {
+ let mut file = File::open(path)?;
+ let mut buffer = Vec::new();
+
+ compress_tools::uncompress_archive_file(&mut file, &mut buffer, ".PKGINFO")?;
+ let pkginfo = String::from_utf8(buffer)?;
+
+ let mut pkgname = String::new();
+ let mut pkgver = String::new();
+
+ for line in pkginfo.lines() {
+ let line = line.trim();
+ if line.starts_with("pkgname") {
+ pkgname = line.split('=').nth(1).unwrap().trim().to_string();
+ } else if line.starts_with("pkgver") {
+ pkgver = line.split('=').nth(1).unwrap().trim().to_string();
+ }
+ }
+
+ Ok(PackageFile {
+ path: path.display().to_string(),
+ package: Package::new(pkgname, pkgver),
+ })
+ }
+}
+
+impl PackageFiles {
+ pub fn new() -> Self {
+ Self {
+ files: HashMap::new(),
+ }
+ }
+
+ pub fn add(&mut self, file: PackageFile) {
+ self.files
+ .entry(file.clone().package.name)
+ .or_default()
+ .push(file);
+ }
+
+ pub fn scan(&mut self, path: &Path) -> AppResult<()> {
+ for entry in fs::read_dir(path)? {
+ let entry = entry?;
+ let path = entry.path();
+ let path_display = path.display().to_string();
+ if path.is_file()
+ && path_display.contains(".pkg.tar.")
+ && !path_display.ends_with(".sig")
+ {
+ let file = Package::read(&path)?;
+ self.add(file);
+ }
+ }
+ Ok(())
+ }
+
+ pub fn prune(&mut self, keep: u8) -> AppResult {
+ let mut to_delete = Vec::new();
+ for (_, files) in self.files.iter_mut() {
+ files.sort_by(|a, b| b.package.version.cmp(&a.package.version));
+ to_delete.extend(files.iter().skip(keep as usize));
+ }
+
+ for file in &to_delete {
+ fs::remove_file(&file.path)?;
+ }
+
+ Ok(to_delete.len())
+ }
+}
diff --git a/src/repository/config.rs b/src/repository/config.rs
deleted file mode 100644
index 2e182c1..0000000
--- a/src/repository/config.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-use std::env;
-use std::fs::File;
-use std::io::Write;
-use std::path::Path;
-
-use crate::internal::AppExitCode;
-use crate::{crash, log};
-
-const DEFAULT_CONFIG: &str = r#"
-[base]
-# Either "repository" or "workspace"
-mode = ""
-# Better left as true, but can be set to false if it causes issues with branches
-smart_pull = true
-
-[mode.repository]
-# Decides what to call the repository and relevant files
-name = ""
-# Decides whether to build packages if package repo is updated on pull
-build_on_update = false
-
-[mode.repository.signing]
-# Decides whether or not to sign packages
-enabled = true
-
-[mode.workspace]
-# Whether to show rich git info for repositories
-git_info = true
-# Colorblind mode toggle
-colorblind = false
-
-[repositories]
-# List of repositories formatted as id:name (priority is decided by the ! suffix, and decides package build order)
-repos = [
- "aur:hello!",
- "crs:malachite"
-]
-
-[repositories.urls]
-# URL keys for repositories, with {} where the repository name would go
-crs = "https://github.com/crystal-linux/{}"
-aur = "https://aur.archlinux.org/{}"
-"#;
-
-pub fn create(verbose: bool) {
- // Ensure current directory is empty
- if env::current_dir()
- .unwrap()
- .read_dir()
- .unwrap()
- .next()
- .is_some()
- {
- crash!(
- AppExitCode::DirNotEmpty,
- "Directory is not empty, please only create a repository in an empty directory"
- );
- }
- log!(verbose, "Creating config file");
-
- // If config file exists, create it
- if !Path::exists("mlc.toml".as_ref()) {
- let mut file = File::create("mlc.toml").unwrap();
- file.write_all(DEFAULT_CONFIG.as_ref()).unwrap();
- }
- log!(verbose, "Config file created");
-}
diff --git a/src/repository/mod.rs b/src/repository/mod.rs
deleted file mode 100755
index 59534d6..0000000
--- a/src/repository/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-pub use config::*;
-pub use package::*;
-pub use repo::*;
-
-mod config;
-mod package;
-mod repo;
diff --git a/src/repository/package.rs b/src/repository/package.rs
deleted file mode 100755
index 04a26fa..0000000
--- a/src/repository/package.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-use std::path::Path;
-use std::process::Command;
-use std::{env, fs};
-
-use crate::internal::AppExitCode;
-use crate::{crash, log};
-
-pub fn build(pkg: &str, sign: bool, verbose: bool) -> i32 {
- log!(verbose, "Building {}", pkg);
- log!(verbose, "Signing: {}", sign);
-
- // Set root dir to return after build
- let dir = env::current_dir().unwrap();
- log!(verbose, "Root dir: {:?}", dir);
-
- // Create out dir if not already present
- if !Path::exists("out".as_ref()) {
- log!(verbose, "Creating out dir");
- fs::create_dir_all("out").unwrap();
- }
-
- // If package directory is not found, crash
- if !Path::exists(pkg.as_ref()) {
- crash!(
- AppExitCode::PkgsNotFound,
- "Repo for package {} not found, aborting",
- pkg
- );
- }
-
- // Enter build directory
- env::set_current_dir(pkg).unwrap();
- log!(verbose, "Current dir: {:?}", env::current_dir().unwrap());
-
- // If PKGBUILD is not found, return 63 and break
- if !Path::exists("PKGBUILD".as_ref()) {
- env::set_current_dir(&dir).unwrap();
- log!(verbose, "Current dir: {:?}", env::current_dir().unwrap());
- return 63;
- }
-
- // Parse extra flags from envvar
- let extra_flags = env::var("MAKEPKG_FLAGS").unwrap_or_else(|_| "".to_string());
- let extra_flags = extra_flags.split(' ').collect::>();
-
- // Default set of flags
- let default_args = vec![
- "-sf",
- "--skippgpcheck",
- if sign { "--sign" } else { "--nosign" },
- "--noconfirm",
- ];
-
- // Build each package
- let a = Command::new("makepkg")
- .args(
- default_args
- .iter()
- .chain(extra_flags.iter())
- .map(std::string::ToString::to_string),
- )
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- log!(verbose, "{} Build job returned: {:?}", pkg, a);
-
- // Copy built package to out dir
- Command::new("bash")
- .args(&["-c", "cp *.pkg.tar* ../out/"])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- log!(verbose, "Copied built package to out dir");
-
- // Return to root dir
- env::set_current_dir(dir).unwrap();
- log!(
- verbose,
- "Returned to root dir: {:?}",
- env::current_dir().unwrap()
- );
-
- // Return exit code
- a.code().unwrap()
-}
diff --git a/src/repository/repo.rs b/src/repository/repo.rs
deleted file mode 100644
index 3d53a4e..0000000
--- a/src/repository/repo.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-use std::path::Path;
-use std::process::Command;
-use std::{env, fs};
-
-use crate::{crash, info, internal::parse_cfg, internal::AppExitCode, log};
-
-pub fn generate(verbose: bool) {
- // Read config struct from mlc.toml
- let config = parse_cfg(verbose);
- log!(verbose, "Config: {:?}", config);
-
- // Get signing from config
- let signing = &config.mode.repository.as_ref().unwrap().signing.enabled;
- log!(verbose, "Signing: {:?}", signing);
-
- // Get repository name from config
- let name = &config.mode.repository.as_ref().unwrap().name;
- log!(verbose, "Name: {}", name);
-
- // Read on_gen from config
- let on_gen = &config.mode.repository.as_ref().unwrap().signing.on_gen;
- log!(verbose, "On gen: {:?}", on_gen);
-
- // Read key from config
- let key = &config.mode.repository.as_ref().unwrap().signing.key;
- log!(verbose, "Key: {:?}", key);
-
- info!("Generating repository: {}", name);
-
- // If repository exists, empty it
- if Path::exists(name.as_ref()) {
- log!(verbose, "Deleting contents of {}", name);
- Command::new("bash")
- .args(&["-c", &format!("rm -rf {}/*", &name)])
- .spawn()
- .unwrap()
- .wait()
- .unwrap(); // Dirty temp hack but oh well
- } else {
- // Create dir if it doesn't exist
- fs::create_dir_all(&name).unwrap();
- log!(verbose, "Created {}", name);
- }
-
- // Copy out packages to repository directory
- Command::new("bash")
- .args(&["-c", &format!("cp -v out/* {}/", &name)])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- log!(verbose, "Copied out packages to {}", name);
-
- // Enter repository directory
- env::set_current_dir(&name).unwrap();
- log!(verbose, "Current dir: {:?}", env::current_dir().unwrap());
-
- // Sign all package files in repository if signing and on_gen are true
- if *signing && on_gen.is_some() && on_gen.unwrap() {
- // Get a list of all .tar.* files in repository
- let files = fs::read_dir(".").unwrap();
-
- for file in files {
- // Get file name
- let file = file.unwrap();
- let path = file.path();
-
- let sign_command = if key.is_some() && !key.as_ref().unwrap().is_empty() {
- format!(
- "gpg --default-key {} --detach-sign {}",
- key.as_ref().unwrap(),
- path.to_str().unwrap()
- )
- } else {
- format!("gpg --detach-sign {}", path.to_str().unwrap())
- };
-
- // If extension is either .zst or .xz, sign it
- if path.extension().unwrap() == "zst" || path.extension().unwrap() == "xz" {
- log!(verbose, "Signing {}", path.display());
- Command::new("bash")
- .arg("-c")
- .args(&[&sign_command])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- }
- }
- log!(verbose, "Signed repository");
- }
-
- let db = format!("{}.db", &name);
- let files = format!("{}.files", &name);
-
- // Check if package files end with .tar.zst or .tar.xz
- let zst = Command::new("bash")
- .args(&["-c", "ls *.tar.zst"])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- let xz = Command::new("bash")
- .args(&["-c", "ls *.tar.xz"])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
-
- // This should never happen, crash and burn if it does
- if zst.success() && xz.success() {
- crash!(
- AppExitCode::RepoParseError,
- "Both .tar.zst and .tar.xz files found in repository. You've done something wrong. Aborting"
- );
- }
-
- // Ensuring aarch64/ALARM support for the future
- let aarch64_mode = if zst.success() {
- false
- } else if xz.success() {
- true
- } else {
- crash!(
- AppExitCode::PkgsNotFound,
- "No .zst or .xz packages found in repository"
- );
- // This should theoretically never be reached, but let's just give the compiler what it wants
- false
- };
- let suffix = if aarch64_mode { "xz" } else { "zst" };
-
- // Create repo.db and repo.files using repo-add
- Command::new("bash")
- .args(&[
- "-c",
- &format!(
- "GLOBIGNORE=\"*.sig\" repo-add {}.tar.gz *.pkg.tar.{}",
- db, suffix
- ),
- ])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- log!(verbose, "Created {} and {}", db, files);
-
- // Replace repo.{db,files}.tar.gz with just repo.{db,files}
- Command::new("bash")
- .args(&[
- "-c",
- &format!("mv {}.tar.gz {}; mv {}.tar.gz {}", db, db, files, files),
- ])
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
- log!(
- verbose,
- "Renamed {}.tar.gz to {} and {}.tar.gz to {}",
- db,
- db,
- files,
- files
- );
-}
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..4511588
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,102 @@
+use std::path::PathBuf;
+use std::process::{Child, Command, ExitStatus};
+
+use crate::errors::{AppError, AppResult};
+
+pub fn uid() -> i32 {
+ (unsafe { libc::geteuid() } as i32)
+}
+
+#[derive(Debug, Clone)]
+pub struct ShellCommand {
+ pub command: String,
+ pub args: Vec,
+ pub cwd: Option,
+}
+
+impl ShellCommand {
+ pub fn repo_add() -> Self {
+ Self::new("repo-add")
+ }
+
+ pub fn git() -> Self {
+ Self::new("git")
+ }
+
+ pub fn makepkg() -> Self {
+ Self::new("makepkg")
+ }
+
+ pub fn new(command: S) -> Self {
+ Self {
+ command: command.to_string(),
+ args: vec![],
+ cwd: None,
+ }
+ }
+
+ pub fn arg(mut self, arg: S) -> Self {
+ self.args.push(arg.to_string());
+ self
+ }
+
+ pub fn args
(mut self, args: V) -> Self
+ where
+ S: ToString,
+ V: IntoIterator- ,
+ {
+ self.args.extend(args.into_iter().map(|s| s.to_string()));
+ self
+ }
+
+ pub fn cwd>(mut self, cwd: P) -> Self {
+ self.cwd = Some(cwd.into());
+ self
+ }
+
+ #[allow(dead_code)]
+ pub fn wait(&self) -> AppResult {
+ let mut child = self.spawn()?;
+
+ child.wait().map_err(AppError::from)
+ }
+
+ #[allow(dead_code)]
+ pub fn wait_with_output(&self) -> AppResult {
+ let child = self.spawn()?;
+
+ child.wait_with_output().map_err(AppError::from)
+ }
+
+ pub fn silent(&self) -> AppResult<()> {
+ let _ = self.output()?;
+
+ Ok(())
+ }
+
+ pub fn output(&self) -> AppResult {
+ let mut command = Command::new(&self.command);
+ command.args(&self.args);
+
+ if let Some(cwd) = &self.cwd {
+ command.current_dir(cwd);
+ }
+
+ let output = command.output()?;
+
+ Ok(output)
+ }
+
+ pub fn spawn(&self) -> AppResult {
+ let mut command = Command::new(&self.command);
+ command.args(&self.args);
+
+ if let Some(cwd) = &self.cwd {
+ command.current_dir(cwd);
+ }
+
+ let child = command.spawn()?;
+
+ Ok(child)
+ }
+}