diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..ff19fdb --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [edu4rdshl] diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..d54fa2f --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,22 @@ +name: Rust + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..fb24a6d --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,14 @@ +# Rustfmt configuration file. + +# How imports should be grouped into use statements. Imports will be merged or split to the configured level of granularity. +imports_granularity = "One" +# Unix or Windows line endings +newline_style = "Unix" +# Convert /* */ comments to // comments where possible +normalize_comments = true +# Convert #![doc] and #[doc] attributes to //! and /// doc comments. +normalize_doc_attributes = true +# Remove nested parens. +remove_nested_parens = true +# Reorder impl items. type and const are put first, then macros and methods. +reorder_impl_items = true diff --git a/Cargo.lock b/Cargo.lock index 43bbcaf..e768aa9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,251 +1,207 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 4 + [[package]] -name = "addr2line" -version = "0.14.1" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 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.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", "libc", - "winapi", ] +[[package]] +name = "anstream" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + [[package]] name = "autocfg" -version = "1.0.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "backtrace" -version = "0.3.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" -version = "1.2.1" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - -[[package]] -name = "bstr" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde 1.0.125", -] +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "bumpalo" -version = "3.6.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cc" -version = "1.0.67" +version = "1.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "libc", - "num-integer", - "num-traits 0.2.14", - "time", - "winapi", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link 0.2.0", ] [[package]] name = "clap" -version = "2.33.3" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ - "ansi_term", - "atty", - "bitflags", + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", "strsim", - "textwrap", - "unicode-width", - "vec_map", - "yaml-rust 0.3.5", ] [[package]] -name = "config" -version = "0.11.0" +name = "clap_derive" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ - "lazy_static", - "nom", - "rust-ini", - "serde 1.0.125", - "serde-hjson", - "serde_json", - "toml", - "yaml-rust 0.4.5", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "constant_time_eq" -version = "0.1.5" +name = "clap_lex" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] -name = "core-foundation" -version = "0.9.1" +name = "colorchoice" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "core-foundation-sys" -version = "0.8.2" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" - -[[package]] -name = "crossbeam-utils" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" -dependencies = [ - "autocfg", - "cfg-if", - "lazy_static", -] +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "csv" -version = "1.1.6" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ - "bstr", "csv-core", "itoa", "ryu", - "serde 1.0.125", + "serde", ] [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] [[package]] -name = "dirs" -version = "1.0.5" +name = "dirs-next" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", @@ -254,562 +210,205 @@ dependencies = [ [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] -name = "encoding_rs" -version = "0.8.28" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" -dependencies = [ - "cfg-if", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" +name = "find-msvc-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" - -[[package]] -name = "futures-sink" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" - -[[package]] -name = "futures-task" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" - -[[package]] -name = "futures-util" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "pin-utils", -] +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" [[package]] name = "getrandom" -version = "0.1.16" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gimli" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" - -[[package]] -name = "h2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", + "wasi", ] [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" -dependencies = [ - "libc", -] +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] -name = "http" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" +name = "hostname" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" +checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" dependencies = [ - "bytes", - "http", - "pin-project-lite", + "cfg-if", + "libc", + "windows-link 0.1.3", ] [[package]] -name = "httparse" -version = "1.3.6" +name = "iana-time-zone" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc35c995b9d93ec174cf9a27d425c7892722101e14993cd227fdb51d70cf9589" - -[[package]] -name = "httpdate" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" - -[[package]] -name = "hyper" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "hyper-tls" -version = "0.5.0" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "idna" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", + "cc", ] [[package]] name = "indexmap" -version = "1.6.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ - "autocfg", + "equivalent", "hashbrown", ] [[package]] -name = "ipnet" -version = "2.3.0" +name = "is-terminal" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "0.4.7" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.50" +version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" +checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lexical-core" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.93" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] -name = "linked-hash-map" -version = "0.5.4" +name = "libredox" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +dependencies = [ + "bitflags", + "libc", +] [[package]] name = "log" -version = "0.4.14" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "md5" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" -version = "2.3.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "mio" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "native-tls" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits 0.2.14", -] +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "num-traits" -version = "0.1.43" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -dependencies = [ - "num-traits 0.2.14", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] -[[package]] -name = "object" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" - [[package]] name = "once_cell" -version = "1.7.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "openssl" -version = "0.10.33" +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", -] - -[[package]] -name = "openssl-probe" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" - -[[package]] -name = "openssl-src" -version = "111.15.0+1.1.1k" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a5f6ae2ac04393b217ea9f700cd04fa9bf3d93fae2872069f3d15d908af70a" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f" -dependencies = [ - "autocfg", - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pin-project" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "pkg-config" -version = "0.3.19" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "prettytable-rs" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" dependencies = [ - "atty", "csv", "encode_unicode", + "is-terminal", "lazy_static", "term", "unicode-width", @@ -817,313 +416,109 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" -dependencies = [ - "getrandom 0.2.2", -] - -[[package]] -name = "rand_hc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "redox_syscall" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" -dependencies = [ - "bitflags", -] - [[package]] name = "redox_users" -version = "0.3.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.1.16", - "redox_syscall 0.1.57", - "rust-argon2", -] - -[[package]] -name = "regex" -version = "1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -dependencies = [ - "byteorder", -] - -[[package]] -name = "regex-syntax" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2296f2fac53979e8ccbc4a1136b25dcefd37be9ed7e4a1f6b05a6029c84ff124" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "serde 1.0.125", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", + "getrandom", + "libredox", + "thiserror", ] [[package]] name = "rusnapshot" -version = "0.1.0" +version = "0.5.3" dependencies = [ + "anyhow", "chrono", "clap", - "config", - "failure", + "hostname", "md5", - "openssl", "prettytable-rs", - "reqwest", + "serde", "sqlite", + "toml", ] [[package]] -name = "rust-argon2" -version = "0.8.3" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" -dependencies = [ - "base64", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils", -] - -[[package]] -name = "rust-ini" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" - -[[package]] -name = "rustc-demangle" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - -[[package]] -name = "security-framework" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" -version = "0.8.23" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" - -[[package]] -name = "serde" -version = "1.0.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" - -[[package]] -name = "serde-hjson" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ - "lazy_static", - "num-traits 0.1.43", - "regex", - "serde 0.8.23", + "serde_derive", ] [[package]] -name = "serde_json" -version = "1.0.64" +name = "serde_derive" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ - "itoa", - "ryu", - "serde 1.0.125", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "serde_urlencoded" -version = "0.7.0" +name = "serde_spanned" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde 1.0.125", + "serde", ] [[package]] -name = "slab" -version = "0.4.2" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" - -[[package]] -name = "socket2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" -dependencies = [ - "libc", - "winapi", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "sqlite" -version = "0.26.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1a534c07ec276fbbe0e55a1c00814d8563da3a2f4d9d9d4c802bd1278db6a" +checksum = "f66e9c01a11936154f3910dbba732c01f8b591543bc4d6672bddee79fd9c4783" dependencies = [ - "libc", "sqlite3-sys", ] [[package]] name = "sqlite3-src" -version = "0.3.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a260b07ce75a0644c6f5891f34f46db9869e731838e95293469ab17999abcfa3" +checksum = "e5b6d3c860886b0a33e69e421796a5f4a27f23597a182c2450f6d7ace5103120" dependencies = [ "cc", "pkg-config", @@ -1131,290 +526,144 @@ dependencies = [ [[package]] name = "sqlite3-sys" -version = "0.13.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d2f028faeb14352df7934b4771806f60d61ce61be1928ec92396d7492e2e54" +checksum = "a7781d97adc13a1d5081127a9ee29afad8427f3757bd984daf814d8265267039" dependencies = [ - "libc", "sqlite3-src", ] -[[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.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "1.0.69" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", - "unicode-xid", -] - -[[package]] -name = "synstructure" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall 0.2.5", - "remove_dir_all", - "winapi", + "unicode-ident", ] [[package]] name = "term" -version = "0.5.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ - "byteorder", - "dirs", + "dirs-next", + "rustversion", "winapi", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "unicode-width", + "thiserror-impl", ] [[package]] -name = "time" -version = "0.1.44" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "tinyvec" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "pin-project-lite", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "toml" -version = "0.5.8" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ - "serde 1.0.125", + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] -name = "tower-service" -version = "0.3.1" +name = "toml_datetime" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", + "serde", ] [[package]] -name = "tracing-core" -version = "0.1.17" +name = "toml_parser" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" dependencies = [ - "lazy_static", + "winnow", ] [[package]] -name = "try-lock" -version = "0.2.3" +name = "toml_writer" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] -name = "unicode-bidi" -version = "0.3.5" +name = "unicode-ident" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" -dependencies = [ - "matches", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" -dependencies = [ - "tinyvec", -] +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] -name = "unicode-xid" -version = "0.2.1" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" - -[[package]] -name = "url" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "vcpkg" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.73" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" +checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" dependencies = [ "cfg-if", - "serde 1.0.125", - "serde_json", + "once_cell", + "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.73" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" +checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" dependencies = [ "bumpalo", - "lazy_static", "log", "proc-macro2", "quote", @@ -1422,23 +671,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.73" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" +checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1446,9 +683,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.73" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" +checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" dependencies = [ "proc-macro2", "quote", @@ -1459,18 +696,11 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.73" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" - -[[package]] -name = "web-sys" -version = "0.3.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" +checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" dependencies = [ - "js-sys", - "wasm-bindgen", + "unicode-ident", ] [[package]] @@ -1496,25 +726,219 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "winreg" -version = "0.7.0" +name = "windows-core" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "winapi", + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result", + "windows-strings", ] [[package]] -name = "yaml-rust" -version = "0.3.5" +name = "windows-implement" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "windows-interface" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ - "linked-hash-map", + "proc-macro2", + "quote", + "syn", ] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link 0.1.3", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" diff --git a/Cargo.toml b/Cargo.toml index de951b4..513c773 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,25 @@ [package] name = "rusnapshot" -version = "0.1.0" +version = "0.5.3" authors = ["edu4rdshl"] -edition = "2018" +edition = "2024" +description = "Simple and handy btrfs snapshoting tool." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "2.33.3", features = ["yaml"] } -reqwest = { version = "0.11.3", features = ["default-tls"] } -openssl = { version = "0.10.30", features = ["vendored"] } -config = { version = "0.11.0", features = ["yaml", "json", "toml", "hjson", "ini"] } -failure = "0.1.8" -sqlite = "0.26.0" -md5 = "0.7.0" -chrono = "0.4.19" -prettytable-rs = "0.8.0" +clap = { version = "4.5.47", features = ["derive", "env"] } +serde = { version = "1.0.219", features = ["derive"] } +sqlite = "0.37.0" +md5 = "0.8.0" +chrono = "0.4.42" +prettytable-rs = "0.10.0" +anyhow = "1.0.99" +toml = "0.9.5" +hostname = "0.4.1" + +[profile.release] +lto = true +codegen-units = 1 +panic = 'abort' +strip = true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d33da15 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Eduard Tolosa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 06dedf0..65e0f90 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Description -Simple and handy btrfs snapshoting tool. Supports unattended snapshots, tracking, restoring, automatic cleanup and more. Backed with SQLite. +Simple and handy definitions-based snapshoting tool for BTRFS. Supports unattended snapshots, tracking, restoring, automatic cleanup and more. Backed with SQLite. # Documentation @@ -7,16 +7,21 @@ See the [docs folder](https://github.com/Edu4rdSHL/rusnapshot/blob/master/docs/) # Features +**The main feature of Rustnapshot is the ability to create snapshots of BTRFS subvolumes using simple TOML definitions. It is designed to be simple and easy to use, while still providing powerful features for managing snapshots. See [the examples folder](https://github.com/Edu4rdSHL/rusnapshot/tree/master/examples) for more information.** + +Among the features of Rustnapshot are: + - Allows you to specify the origin and destination of snapshots at will of the user. - Track snapshots using SQLite as backend database. - Easy setup using small templates instead of confusing long files. -- You can create snapshots of the volumes you want simply by using different configuration templates. -- You can create read-only or read-write snapshots. -- You can use the same SQLite database for everything. -- You can specify the prefix of the name for the snapshots. -- You can specify a `kind` identifier to differentiate them in the database. Useful if you plan to have hourly, weekly, montly or more "kind" of snapshots of the same subvolume(s). +- Ability to create snapshots of the volumes you want simply by using different configuration templates. +- Ability to create read-only or read-write snapshots. +- Ability to use the same SQLite database for everything. +- Ability to specify the prefix of the name for the snapshots for better identification. +- Ability to specify a `kind` identifier to differentiate them in the database. Useful if you plan to have hourly, weekly, monthly or more "kind" of snapshots of the same subvolume(s). +- Ability to specify the maximum number of snapshots to keep for automatic cleanup. - Supports restoration of snapshots in the original directory or a specific one. -- Automatic snapshots cleanup. +- Supports machine name identification for better tracking when using the same database in multiple machines. - Nice CLI output to see the status and details of snapshots. # Known limitations diff --git a/builder.sh b/builder.sh new file mode 100755 index 0000000..59abe81 --- /dev/null +++ b/builder.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +NAME="rusnapshot" + +LINUX_TARGET="x86_64-unknown-linux-musl" +LINUX_X86_TARGET="i686-unknown-linux-musl" +ARMV7_TARGET="armv7-unknown-linux-gnueabihf" +AARCH_TARGET="aarch64-unknown-linux-gnu" +MANPAGE_DIR="./$NAME.1" +BIN_OUTPUT_DIR="./ghbinaries" + +rm -rf "$BIN_OUTPUT_DIR" +mkdir -p "$BIN_OUTPUT_DIR" + +# Linux build +echo "Building Linux artifact." +if cross build -q --release --target="$LINUX_TARGET"; then + echo "Linux artifact build: SUCCESS" + cp "target/$LINUX_TARGET/release/$NAME" "target/$LINUX_TARGET/release/$NAME-linux" + strip "target/$LINUX_TARGET/release/$NAME-linux" + sha512sum "target/$LINUX_TARGET/release/$NAME-linux" >"$BIN_OUTPUT_DIR/$NAME-linux.sha512" + zip -q -j "$BIN_OUTPUT_DIR/$NAME-linux-x64.zip" "target/$LINUX_TARGET/release/$NAME-linux" +else + echo "Linux artifact build: FAILED" +fi + +# Linux x86 build +echo "Building Linux x86 artifact." +if cross build -q --release --target="$LINUX_X86_TARGET"; then + echo "Linux x86 artifact build: SUCCESS" + cp "target/$LINUX_X86_TARGET/release/$NAME" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" + strip "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" + sha512sum "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" >"$BIN_OUTPUT_DIR/$NAME-linux-i386.sha512" + zip -q -j "$BIN_OUTPUT_DIR/$NAME-linux-i386.zip" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" +else + echo "Linux x86 artifact build: FAILED" +fi + +# ARMV7 build +echo "Building ARMv7 artifact." +if cross build -q --release --target="$ARMV7_TARGET"; then + echo "ARMv7 artifact build: SUCCESS" + cp "target/$ARMV7_TARGET/release/$NAME" "target/$ARMV7_TARGET/release/$NAME-armv7" + strip "target/$ARMV7_TARGET/release/$NAME-armv7" + sha512sum "target/$ARMV7_TARGET/release/$NAME-armv7" >"$BIN_OUTPUT_DIR/$NAME-armv7.sha512" + zip -q -j "$BIN_OUTPUT_DIR/$NAME-armv7.zip" "target/$ARMV7_TARGET/release/$NAME-armv7" +else + echo "ARMv7 artifact build: FAILED" +fi + +# Aarch64 build +echo "Building Aarch64 artifact." +if cross build -q --release --target="$AARCH_TARGET"; then + echo "Aarch64 artifact build: SUCCESS" + cp "target/$AARCH_TARGET/release/$NAME" "target/$AARCH_TARGET/release/$NAME-aarch64" + strip "target/$AARCH_TARGET/release/$NAME-aarch64" + sha512sum "target/$AARCH_TARGET/release/$NAME-aarch64" >"$BIN_OUTPUT_DIR/$NAME-aarch64.sha512" + zip -q -j "$BIN_OUTPUT_DIR/$NAME-aarch64.zip" "target/$AARCH_TARGET/release/$NAME-aarch64" +else + echo "Aarch64 artifact build: FAILED" +fi + +echo "Creating manpage..." +if command -v help2man >/dev/null; then + if help2man -o "$MANPAGE_DIR" "target/$LINUX_TARGET/release/$NAME"; then + echo "Manpage created sucessfully and saved in $MANPAGE_DIR" + else + echo "Error creating manpage." + fi +else + echo "Please install the help2man package." +fi diff --git a/docs/SETUP.md b/docs/SETUP_AND_USAGE.md similarity index 86% rename from docs/SETUP.md rename to docs/SETUP_AND_USAGE.md index 78dd227..943a002 100644 --- a/docs/SETUP.md +++ b/docs/SETUP_AND_USAGE.md @@ -14,7 +14,7 @@ - Create a snapshot using a [config file](https://github.com/Edu4rdSHL/rusnapshot/tree/master/examples/config-templates): -`sudo rusnapshot --config {{path/to/config.toml}} --cr` +`sudo rusnapshot --config {{path/to/config.toml}} --create` - List created snapshots: @@ -30,11 +30,11 @@ - Create a read-write snapshot: -`sudo rusnapshot -c {{path/to/config.toml}} --cr --rw` +`sudo rusnapshot -c {{path/to/config.toml}} --create --rw` - Restore a snapshot: -`sudo rusnapshot -c {{path/to/config.toml}} --id {{snapshot_id}} --restore` +`sudo rusnapshot --id {{snapshot_id}} --restore` # Notes diff --git a/examples/config-templates/config-all.toml b/examples/config-templates/config-all.toml index f8aeccf..3db87b6 100644 --- a/examples/config-templates/config-all.toml +++ b/examples/config-templates/config-all.toml @@ -1,16 +1,17 @@ -# Example configuration file for rustnapshot +# Example configuration file for rusnapshot # These are all the current available options # Snapshots directory -dest_dir = "/mnt/defvol/_snapshots/" -# Directory to be snapshoted -source_dir = "/mnt/defvol/_active/root/" -# SQLite database file PATH, created if no exists, recommended to be in the snapshots dir -database_file = "/mnt/defvol/_snapshots/rustnapshot.sqlite" +dest_dir = "/.snapshots" +# Directory to be snapshot-ed +source_dir = "/" +# SQLite database file PATH where snapshots metadata will be stored. Created if not exists +database_file = "/.snapshots/rusnapshot.db" # Prefix for the snapshots, they will be saved in the format prefix-$current_date snapshot_prefix = "root" # Snapshots identifier snapshot_kind = "weekly" -# Last snapshots to keep. If prefix is specified then only the snapshots with that prefix -# are deleted -keep_only = "2" +# Last snapshots to keep. If prefix is specified then only the snapshots with that prefix will be deleted +keep_only = 3 +# Machine name to be used in the snapshots metadata +machine = "Oribos" diff --git a/examples/config-templates/config-home.toml b/examples/config-templates/config-home.toml index 288450d..ea9b505 100644 --- a/examples/config-templates/config-home.toml +++ b/examples/config-templates/config-home.toml @@ -1,5 +1,12 @@ -dest_dir = "/mnt/defvol/_snapshots/" -source_dir = "/home/" -database_file = "/mnt/defvol/_snapshots/rustnapshot.sqlite" +# Snapshots directory +dest_dir = "/.snapshots" +# Directory to be snapshot-ed +source_dir = "/home" +# SQLite database file PATH where snapshots metadata will be stored. Created if not exists +database_file = "/.snapshots/rusnapshot.db" +# Prefix for the snapshots, they will be saved in the format prefix-$current_date snapshot_prefix = "home" -keep_only = "2" +# Last snapshots to keep. If prefix is specified then only the snapshots with that prefix will be deleted +keep_only = 3 +# Machine name to be used in the snapshots metadata +machine = "Oribos" diff --git a/examples/config-templates/config-root.toml b/examples/config-templates/config-root.toml index 2228e95..4443ff7 100644 --- a/examples/config-templates/config-root.toml +++ b/examples/config-templates/config-root.toml @@ -1,5 +1,12 @@ -dest_dir = "/mnt/defvol/_snapshots/" +# Snapshots directory +dest_dir = "/.snapshots" +# Directory to be snapshot-ed source_dir = "/" -database_file = "/mnt/defvol/_snapshots/rustnapshot.sqlite" +# SQLite database file PATH where snapshots metadata will be stored. Created if not exists +database_file = "/.snapshots/rusnapshot.db" +# Prefix for the snapshots, they will be saved in the format prefix-$current_date snapshot_prefix = "root" -keep_only = "2" +# Last snapshots to keep. If prefix is specified then only the snapshots with that prefix will be deleted +keep_only = 3 +# Machine name to be used in the snapshots metadata +machine = "Oribos" diff --git a/examples/services/rusnapshot-three-hours.service b/examples/services/rusnapshot-daily.service similarity index 56% rename from examples/services/rusnapshot-three-hours.service rename to examples/services/rusnapshot-daily.service index b1f5a73..51639da 100644 --- a/examples/services/rusnapshot-three-hours.service +++ b/examples/services/rusnapshot-daily.service @@ -1,12 +1,9 @@ [Unit] -Description=Snapshot root directory. +Description=Daily snapshots for home and root subvolumes [Service] Type=oneshot -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind three-hours -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind three-hours --rw -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind three-hours -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind three-hours --rw - -[Install] -WantedBy=default.target \ No newline at end of file +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --create --kind daily +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --clean --kind daily +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --create --kind daily +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --clean --kind daily diff --git a/examples/services/rusnapshot-daily.timer b/examples/services/rusnapshot-daily.timer new file mode 100644 index 0000000..98f2641 --- /dev/null +++ b/examples/services/rusnapshot-daily.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Take daily snapshots with rusnapshot + +[Timer] +OnCalendar=daily +Persistent=true + +[Install] +WantedBy=timers.target \ No newline at end of file diff --git a/examples/services/rusnapshot-monthly.service b/examples/services/rusnapshot-monthly.service index ac9eefd..5478ce5 100644 --- a/examples/services/rusnapshot-monthly.service +++ b/examples/services/rusnapshot-monthly.service @@ -1,12 +1,9 @@ [Unit] -Description=Take system snapshots monthly. +Description=Monthly snapshots for home and root subvolumes [Service] Type=oneshot -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind monthly -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind monthly --rw -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind monthly -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind monthly --rw - -[Install] -WantedBy=default.target \ No newline at end of file +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --create --kind monthly +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --clean --kind monthly +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --create --kind monthly +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --clean --kind monthly diff --git a/examples/services/rusnapshot-monthly.timer b/examples/services/rusnapshot-monthly.timer index 88eb6fd..366608b 100644 --- a/examples/services/rusnapshot-monthly.timer +++ b/examples/services/rusnapshot-monthly.timer @@ -1,5 +1,5 @@ [Unit] -Description=Take system snapshots monthly. +Description=Take monthly snapshots with rusnapshot [Timer] OnCalendar=monthly diff --git a/examples/services/rusnapshot-three-hours.timer b/examples/services/rusnapshot-three-hours.timer deleted file mode 100644 index 54ef618..0000000 --- a/examples/services/rusnapshot-three-hours.timer +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=Take snapshots every 3 hours. - -[Timer] -OnBootSec=1h -OnUnitActiveSec=3h - -[Install] -WantedBy=timers.target \ No newline at end of file diff --git a/examples/services/rusnapshot-weekly.service b/examples/services/rusnapshot-weekly.service index 25ad102..ba4db04 100644 --- a/examples/services/rusnapshot-weekly.service +++ b/examples/services/rusnapshot-weekly.service @@ -1,12 +1,9 @@ [Unit] -Description=Take system snapshots weekly. +Description=Weekly snapshots for home and root subvolumes [Service] Type=oneshot -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind weekly -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind weekly --rw -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind weekly -ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind weekly --rw - -[Install] -WantedBy=default.target \ No newline at end of file +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --create --kind weekly +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --clean --kind weekly +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --create --kind weekly +ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --clean --kind weekly diff --git a/examples/services/rusnapshot-weekly.timer b/examples/services/rusnapshot-weekly.timer index 8126e47..219cbf2 100644 --- a/examples/services/rusnapshot-weekly.timer +++ b/examples/services/rusnapshot-weekly.timer @@ -1,5 +1,5 @@ [Unit] -Description=Take system snapshots weekly. +Description=Take weekly snapshots with rusnapshot [Timer] OnCalendar=weekly diff --git a/src/args.rs b/src/args.rs index cfc6ad5..f9086d4 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,155 +1,163 @@ use { - crate::structs::Args, - clap::{load_yaml, value_t, App}, - std::{collections::HashMap, path::Path}, + crate::{database, operations, structs::ExtraArgs}, + anyhow::Result, + clap::Parser, + serde::{Deserialize, Serialize}, + sqlite::Connection, + std::collections::BTreeMap, }; -pub fn get_args() -> Args { - let yaml = load_yaml!("cli.yml"); - let matches = App::from_yaml(yaml) - .version(clap::crate_version!()) - .get_matches(); - let settings: HashMap = - return_settings(&matches, &mut config::Config::default()); - Args { - create_snapshot: matches.is_present("create-snapshot"), - delete_snapshot: matches.is_present("delete-snapshot"), - list_snapshots: matches.is_present("list-snapshots"), - clean_snapshots: matches.is_present("clean-snapshots") || matches.is_present("keep-only"), - restore_snapshot: matches.is_present("restore-snapshot"), - rw_snapshots: matches.is_present("read-write"), - dest_dir: if matches.is_present("dest-dir") { - value_t!(matches, "dest-dir", String).unwrap_or_else(|_| String::new()) - } else { - return_value_or_default(&settings, "dest_dir", String::new()) - }, - source_dir: if matches.is_present("source-dir") { - value_t!(matches, "source-dir", String).unwrap_or_else(|_| String::new()) - } else { - return_value_or_default(&settings, "source_dir", String::new()) - }, - database_connection: { - let db_file_path = if matches.is_present("database-file") { - value_t!(matches, "database-file", String).unwrap_or_else(|_| String::new()) - } else { - return_value_or_default(&settings, "database_file", String::new()) - }; - if db_file_path.is_empty() { - eprintln!( - "Please specify a database file with -d/--dfile or the database_file option in the config file, it's required for all the operations, leaving." - ); - std::process::exit(1) - } else { - match sqlite::open(&db_file_path) { - Ok(mut connection) => { - let timeout = value_t!(matches, "timeout", usize).unwrap_or_else(|_| 5000); - connection - .set_busy_timeout(timeout) - .expect("Failed to set database timeout"); - connection - } - Err(e) => { - eprintln!( - "Error while trying to stablish the database connection. Error: {}", - e - ); - std::process::exit(1) - } - } +/// Simple and handy btrfs snapshoting tool. +#[derive(Parser, Debug, Default, Serialize, Deserialize, Clone)] +#[clap(author, version, about, long_about = None, arg_required_else_help = true)] +pub struct Args { + /// Path to configuration file. + #[clap(short = 'c', long = "config")] + pub config_file: Option, + /// Directory where snapshots should be saved. + #[clap(long = "to", requires_all = &["create_snapshot"], default_value = "")] + pub dest_dir: String, + /// Directory from where snapshots should be created. It can also be used to specify the directory where a snapshot will be restored. + #[clap(long = "from", requires_all = &["create_snapshot"], default_value = "")] + pub source_dir: String, + /// Snapshot id or name to work with. + #[clap(long = "id", default_value = "")] + pub snapshot_id: String, + /// Path to the `SQLite` database file. + #[clap( + short = 'd', + long = "dfile", + env = "RUSNAPSHOT_DB_FILE", + default_value = "/.snapshots/rustnapshot.sqlite" + )] + pub database_file: String, + /// Prefix for the snapshot name. + #[clap(short = 'p', long = "prefix", default_value = "rusnapshot")] + pub snapshot_prefix: String, + /// Used to specify a differentiator between snapshots with the same prefix. + #[clap(long = "kind", default_value = "rusnapshot")] + pub snapshot_kind: String, + /// Keep only the last X items. + #[clap(short = 'k', long = "keep", default_value = "3")] + pub keep_only: usize, + /// Time in milliseconds until `SQLite` can return a timeout. Do not touch if you don't know what you are doing. + #[clap(long = "timeout", default_value = "10000")] + pub timeout: usize, + /// Create a read-only/ro snapshot. + #[clap(long = "create", conflicts_with_all = &["restore_snapshot", "delete_snapshot", "list_snapshots", "clean_snapshots"])] + pub create_snapshot: bool, + /// Enable snapshots cleaning, will keep only the last X snapshots specified with -k/--keep. + /// This option requires: -k/--keep, -p/--prefix and --kind via command line or configuration file. + #[clap(long = "clean")] + pub clean_snapshots: bool, + /// Delete a snapshot. + #[clap(long = "del", requires_all = &["snapshot_id"])] + pub delete_snapshot: bool, + /// Restore a specific snapshot. + #[clap(short = 'r', long = "restore", requires_all = &["snapshot_id"])] + pub restore_snapshot: bool, + /// List the snapshots tracked in the database. + #[clap(short = 'l', long = "list")] + pub list_snapshots: bool, + /// Create read-write/rw snapshots. + #[clap(short = 'w', long = "rw")] + pub read_write: bool, + /// Machine name to be used in the metadata. + #[clap(short, long, default_value = "")] + pub machine: String, +} + +impl Args { + /// Get the database connection string. + #[must_use] + pub fn database_connection(&self) -> Connection { + match sqlite::open(&self.database_file) { + Ok(mut connection) => { + connection + .set_busy_timeout(self.timeout) + .expect("Failed to set database timeout"); + + connection } - }, - snapshot_name: String::new(), - snapshot_id: value_t!(matches, "snapshot-id", String).unwrap_or_else(|_| String::new()), - snapshot_prefix: if matches.is_present("snapshot-prefix") { - value_t!(matches, "snapshot-prefix", String) - .unwrap_or_else(|_| String::from("snapshot")) - } else { - return_value_or_default(&settings, "snapshot_prefix", String::from("snapshot")) - }, - snapshot_kind: if matches.is_present("snapshot-kind") { - value_t!(matches, "snapshot-kind", String) - .unwrap_or_else(|_| String::from("rusnapshot")) - } else { - return_value_or_default(&settings, "snapshot_kind", String::from("rusnapshot")) - }, - snapshot_ro_rw: if matches.is_present("read-write") { - "read-write".to_string() - } else { - "read-only".to_string() - }, - keep_only: if matches.is_present("keep-only") { - value_t!(matches, "keep-only", usize).unwrap_or_else(|_| 0) - } else { - return_value_or_default(&settings, "keep_only", 0.to_string()) + Err(e) => { + eprintln!("Error while trying to stablish the database connection. Error: {e}"); + std::process::exit(1) + } + } + } + + /// Initialize the database and directory structure. + pub fn init(&self, extra_args: &ExtraArgs) -> Result<()> { + if !std::path::Path::new(&self.dest_dir).exists() { + println!("Setting up the directory structure."); + operations::setup_directory_structure(self)?; + } + database::setup_initial_database(&extra_args.database_connection)?; + + Ok(()) + } + + pub fn check_for_source_and_dest_dir(&mut self) { + if self.source_dir.is_empty() || self.dest_dir.is_empty() { + eprintln!("Specify both source and destination directories before taking a snapshot."); + std::process::exit(1); + } + } + + /// Deserialize the configuration file. + pub fn from_config_file(&mut self) -> Result<()> { + let config_file = std::fs::read_to_string(self.config_file.as_deref().unwrap())?; + let config: BTreeMap = toml::from_str(&config_file)?; + + if let Some(dest_dir) = config.get("dest_dir") { + self.dest_dir = dest_dir + .as_str() + .expect("Failed to parse dest_dir") + .to_string(); + } + if let Some(source_dir) = config.get("source_dir") { + self.source_dir = source_dir + .as_str() + .expect("Failed to parse source_dir") + .to_string(); + } + if let Some(snapshot_prefix) = config.get("snapshot_prefix") { + self.snapshot_prefix = snapshot_prefix + .as_str() + .expect("Failed to parse snapshot_prefix") + .to_string(); + } + if let Some(snapshot_kind) = config.get("snapshot_kind") { + self.snapshot_kind = snapshot_kind + .as_str() + .expect("Failed to parse snapshot_kind") + .to_string(); + } + if let Some(database_file) = config.get("database_file") { + self.database_file = database_file + .as_str() + .expect("Failed to parse database_file") + .to_string(); + } + if let Some(keep_only) = config.get("keep_only") { + self.keep_only = keep_only + .to_string() .parse() - .unwrap_or_default() - }, + .expect("Failed to parse keep_only, make sure it's a number"); + } + if let Some(timeout) = config.get("timeout") { + self.timeout = timeout + .to_string() + .parse() + .expect("Failed to parse timeout, make sure it's a number"); + } + if let Some(machine) = config.get("machine") { + self.machine = machine + .to_string() + .parse() + .expect("Failed to parse machine"); + } + + Ok(()) } } - -fn return_settings( - matches: &clap::ArgMatches, - settings: &mut config::Config, -) -> HashMap { - if matches.is_present("config-file") { - match settings.merge(config::File::with_name( - &value_t!(matches, "config-file", String).unwrap(), - )) { - Ok(settings) => match settings.merge(config::Environment::with_prefix("RUSNAPSHOT")) { - Ok(settings) => settings - .clone() - .try_into::>() - .unwrap(), - Err(e) => { - eprintln!("Error merging environment variables into settings: {}", e); - std::process::exit(1) - } - }, - Err(e) => { - eprintln!("Error reading config file: {}", e); - std::process::exit(1) - } - } - } else if Path::new("rusnapshot.toml").exists() - || Path::new("rusnapshot.json").exists() - || Path::new("rusnapshot.hjson").exists() - || Path::new("rusnapshot.ini").exists() - || Path::new("rusnapshot.yml").exists() - { - match settings.merge(config::File::with_name("rusnapshot")) { - Ok(settings) => match settings.merge(config::Environment::with_prefix("RUSNAPSHOT")) { - Ok(settings) => settings - .clone() - .try_into::>() - .unwrap(), - Err(e) => { - eprintln!("Error merging environment variables into settings: {}", e); - std::process::exit(1) - } - }, - Err(e) => { - eprintln!("Error reading config file: {}", e); - std::process::exit(1) - } - } - } else { - match settings.merge(config::Environment::with_prefix("RUSNAPSHOT")) { - Ok(settings) => settings - .clone() - .try_into::>() - .unwrap(), - Err(e) => { - eprintln!("Error merging environment variables into settings: {}", e); - std::process::exit(1) - } - } - } -} - -fn return_value_or_default( - settings: &HashMap, - value: &str, - default_value: String, -) -> String { - settings.get(value).unwrap_or(&default_value).to_string() -} diff --git a/src/cli.yml b/src/cli.yml deleted file mode 100644 index af5263a..0000000 --- a/src/cli.yml +++ /dev/null @@ -1,107 +0,0 @@ -name: Rusnapshot -author: Eduard Tolosa -settings: - - ArgRequiredElseHelp - - StrictUtf8 -about: Simple and handy btrfs snapshoting tool. -args: - - config-file: - help: Path to configuration file. - short: c - long: config - takes_value: true - multiple: false - - - dest-dir: - help: Directory where snapshots should be saved. - long: to - takes_value: true - multiple: false - - - source-dir: - help: Directory from where snapshots should be created. It can also be used to specify the directory where a snapshot will be restored. - long: from - takes_value: true - multiple: false - - - snapshot-id: - help: Snapshot id or name to work with. - long: id - takes_value: true - multiple: false - - - database-file: - help: Path to the SQLite database file. - short: d - long: dfile - takes_value: true - multiple: false - - - snapshot-prefix: - help: "Prefix for the snapshot name. Default: snapshot" - short: p - long: prefix - takes_value: true - multiple: false - - - snapshot-kind: - help: "Used to specify a differentiator between snapshots with the same prefix. Default: rusnapshot" - long: kind - takes_value: true - multiple: false - - - keep-only: - help: Keep only the last X items. - short: k - long: keep - takes_value: true - multiple: false - - - timeout: - help: "Time in milliseconds until SQLite can return a timeout (Do not touch if you don't know what you are doing). Default: 5000" - long: timeout - takes_value: true - multiple: false - - - create-snapshot: - help: Create an snapshot. - long: cr - takes_value: false - multiple: false - - - clean-snapshots: - help: "Enable snapshots cleaning, will keep only the last X snapshots specified with -k/--keep. WARNING: if -k/--keep is not specified, IT WILL DELETE ALL THE SNAPSHOTS." - long: clean - takes_value: false - multiple: false - - - delete-snapshot: - help: Delete an snapshot. - long: del - takes_value: false - multiple: false - requires: - - snapshot-id - - - restore-snapshot: - help: Restore an specific snapshot. - short: r - long: restore - takes_value: false - multiple: false - requires: - - snapshot-id - - - list-snapshots: - help: List the snapshots tracked in the database. - short: l - long: list - takes_value: false - multiple: false - - - read-write: - help: Create read-write/rw snapshots. - short: w - long: rw - takes_value: false - multiple: false diff --git a/src/controller.rs b/src/controller.rs index b9d216a..9593370 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -1,66 +1,76 @@ use { - crate::{database, errors::*, operations, structs::Args}, - chrono::Utc, + crate::{args::Args, database, operations, structs::ExtraArgs}, + anyhow::Result, md5, - prettytable::{cell, row, Table}, + prettytable::{Table, row}, + sqlite::Connection, }; -pub fn manage_creation(args: &mut Args) -> Result<()> { - args.snapshot_name = format!( - "{}-{}", - args.snapshot_prefix, - Utc::now().format("%Y-%m-%d-%H-%M-%S-%6f") - ); - args.snapshot_id = format!("{:?}", md5::compute(&args.snapshot_name)); - if operations::take_snapshot(args) { - database::commit_to_database(&args.database_connection, args)? +pub fn manage_creation(args: &mut Args, extra_args: &ExtraArgs) -> Result<()> { + args.snapshot_id = format!("{:?}", md5::compute(&extra_args.snapshot_name)); + if operations::take_snapshot(args, &extra_args.snapshot_name) { + database::commit_to_database(args, extra_args)?; + } else { + eprintln!("Error while taking the snapshot"); } Ok(()) } -pub fn manage_deletion(args: &mut Args) -> Result<()> { - let snapshot_data = database::return_snapshot_data(&args.database_connection, args)?; - if !snapshot_data.snap_id.is_empty() { - args.snapshot_name = snapshot_data.destination + &snapshot_data.name; - } else { +pub fn manage_deletion(args: &Args, extra_args: &mut ExtraArgs) -> Result<()> { + let snapshot_data = database::return_snapshot_data(&extra_args.database_connection, args)?; + if snapshot_data.snap_id.is_empty() { eprintln!( "Snapshot ID {} does not returned any data. Please double check the ID.", args.snapshot_id - ) + ); + std::process::exit(1); + } else { + extra_args.snapshot_name = snapshot_data.destination + &snapshot_data.name; } - if !args.snapshot_name.is_empty() && operations::del_snapshot(args) { - database::delete_from_database(&args.database_connection, args)? + if !extra_args.snapshot_name.is_empty() && operations::del_snapshot(&extra_args.snapshot_name) { + database::delete_from_database(&extra_args.database_connection, args)?; + } else { + eprintln!("Error while deleting the snapshot"); } Ok(()) } -pub fn manage_restoring(args: &mut Args) -> Result<()> { - let snapshot_data = database::return_snapshot_data(&args.database_connection, args)?; - if !snapshot_data.snap_id.is_empty() { - args.snapshot_name = snapshot_data.destination + &snapshot_data.name; - if args.source_dir.is_empty() { - args.source_dir = snapshot_data.source; +pub fn manage_restoring(args: &mut Args, extra_args: &mut ExtraArgs) -> Result<()> { + let snapshot_data = database::return_snapshot_data(&extra_args.database_connection, args)?; + if snapshot_data.snap_id.is_empty() { + eprintln!( + "Snapshot ID {} does not returned any data. Please double check the ID.", + args.snapshot_id + ); + std::process::exit(1); + } else { + extra_args.snapshot_name = snapshot_data.destination + &snapshot_data.name; + if args.dest_dir.is_empty() { + // The current destination directory corresponds to the source directory + // at the moment of the snapshot creation. + args.dest_dir = snapshot_data.source; } - } else { - eprintln!( - "Snapshot ID {} does not returned any data. Please double check the ID.", - args.snapshot_id - ) } - if !args.snapshot_name.is_empty() && operations::restore_snapshot(args) { - println!( - "The snapshot with ID {} was successfully restored to {}", - args.snapshot_id, args.source_dir - ) + println!( + "Restoring snapshot {} to {}", + extra_args.snapshot_name, args.dest_dir + ); + + if !extra_args.snapshot_name.is_empty() + && operations::restore_snapshot(args, &extra_args.snapshot_name) + { + println!("The snapshot was successfully restored"); + } else { + eprintln!("Error while restoring the snapshot"); } Ok(()) } -pub fn manage_listing(args: &mut Args) -> Result<()> { - let snaps_data = database::return_all_data(&args.database_connection)?; +pub fn manage_listing(database_connection: &Connection) -> Result<()> { + let snaps_data = database::return_all_data(database_connection)?; let mut table = Table::new(); table.set_titles(row![ @@ -69,7 +79,8 @@ pub fn manage_listing(args: &mut Args) -> Result<()> { "KIND", "SOURCE DIR", "DESTINATION DIR", - "RO/RW", + "MACHINE", + "RW", "DATE" ]); @@ -80,6 +91,7 @@ pub fn manage_listing(args: &mut Args) -> Result<()> { data.kind, data.source, data.destination, + data.machine, data.ro_rw, data.date, ]); @@ -89,12 +101,14 @@ pub fn manage_listing(args: &mut Args) -> Result<()> { Ok(()) } -pub fn keep_only_x(args: &mut Args) -> Result<()> { - let snaps_data = database::return_only_x_items(&args.database_connection, args)?; +pub fn keep_only_x(args: &mut Args, extra_args: &mut ExtraArgs) -> Result<()> { + let snaps_data = database::return_only_x_items(&extra_args.database_connection, args)?; + for data in &snaps_data { - args.snapshot_name = data.destination.clone() + &data.name; - args.snapshot_id = data.snap_id.clone(); - manage_deletion(args)? + extra_args.snapshot_name = data.destination.clone() + &data.name; + args.snapshot_id.clone_from(&data.snap_id); + + manage_deletion(args, extra_args)?; } Ok(()) diff --git a/src/database.rs b/src/database.rs index e7a8398..8ffa8fc 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,24 +1,31 @@ use { crate::{ - errors::*, - structs::{Args, Database}, + args::Args, + structs::{Database, ExtraArgs}, }, + anyhow::Result, sqlite::{Connection, State, Statement}, }; pub fn setup_initial_database(connection: &Connection) -> Result<()> { - connection.execute("CREATE TABLE IF NOT EXISTS snapshots (name TEXT NOT NULL, snap_id TEXT NOT NULL, kind TEXT NOT NULL, source TEXT NOT NULL, destination TEXT NOT NULL, ro_rw TEXT NOT NULL, date TEXT DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(name, snap_id))")?; + connection.execute("CREATE TABLE IF NOT EXISTS snapshots (name TEXT NOT NULL, snap_id TEXT NOT NULL, kind TEXT NOT NULL, source TEXT NOT NULL, destination TEXT NOT NULL, machine TEXT NOT NULL, ro_rw TEXT NOT NULL, date TEXT DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(name, snap_id))")?; connection.execute("PRAGMA journal_mode=WAL")?; Ok(()) } -pub fn commit_to_database(connection: &Connection, args: &Args) -> Result<()> { +pub fn commit_to_database(args: &Args, extra_args: &ExtraArgs) -> Result<()> { let statement = format!( - "INSERT INTO snapshots (name, snap_id, kind, source, destination, ro_rw) VALUES ('{}', '{}', '{}', '{}', '{}', '{}')", - args.snapshot_name, args.snapshot_id, args.snapshot_kind, args.source_dir, args.dest_dir, args.snapshot_ro_rw + "INSERT INTO snapshots (name, snap_id, kind, source, destination, machine, ro_rw) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}')", + &extra_args.snapshot_name, + args.snapshot_id, + args.snapshot_kind, + args.source_dir, + args.dest_dir, + args.machine, + args.read_write ); - connection.execute(&statement)?; + extra_args.database_connection.execute(statement)?; Ok(()) } @@ -28,7 +35,7 @@ pub fn delete_from_database(connection: &Connection, args: &Args) -> Result<()> "DELETE FROM snapshots WHERE name = '{}' OR snap_id = '{}'", args.snapshot_id, args.snapshot_id ); - connection.execute(&statement)?; + connection.execute(statement)?; Ok(()) } @@ -40,8 +47,8 @@ pub fn return_snapshot_data(connection: &Connection, args: &Args) -> Result Result> { let mut snapshots_data: Vec = Vec::new(); let mut statement = connection - .prepare("SELECT name,snap_id,kind,source,destination,ro_rw,datetime(date, 'localtime') FROM snapshots ORDER BY date DESC")?; + .prepare("SELECT name,snap_id,kind,source,destination,machine,ro_rw,datetime(date, 'localtime') FROM snapshots ORDER BY date DESC")?; - while let State::Row = statement.next()? { - let db_struct = Database::default(); - snapshots_data.push(populate_db_struct(&statement, db_struct)?); + while statement.next()? == State::Row { + snapshots_data.push(populate_db_struct(&statement)?); } Ok(snapshots_data) @@ -64,23 +70,24 @@ pub fn return_all_data(connection: &Connection) -> Result> { pub fn return_only_x_items(connection: &Connection, args: &Args) -> Result> { let mut snapshots_data: Vec = Vec::new(); - let mut statement = connection.prepare(&format!("SELECT name,snap_id,kind,source,destination,ro_rw,date FROM (SELECT row_number() over(ORDER BY date DESC) n,* from snapshots WHERE name like '{}%' AND kind = '{}' AND ro_rw = '{}') WHERE n > {}", args.snapshot_prefix, args.snapshot_kind, args.snapshot_ro_rw, args.keep_only))?; + let mut statement = connection.prepare(format!("SELECT name,snap_id,kind,source,destination,machine,ro_rw,date FROM (SELECT row_number() over(ORDER BY date DESC) n,* from snapshots WHERE name like '{}%' AND kind = '{}' AND ro_rw = '{}') WHERE n > {}", args.snapshot_prefix, args.snapshot_kind, args.read_write, args.keep_only))?; - while let State::Row = statement.next()? { - let db_struct = Database::default(); - snapshots_data.push(populate_db_struct(&statement, db_struct)?); + while statement.next()? == State::Row { + snapshots_data.push(populate_db_struct(&statement)?); } Ok(snapshots_data) } -fn populate_db_struct(row: &Statement, mut db_struct: Database) -> Result { - db_struct.name = row.read::(0)?; - db_struct.snap_id = row.read::(1)?; - db_struct.kind = row.read::(2)?; - db_struct.source = row.read::(3)?; - db_struct.destination = row.read::(4)?; - db_struct.ro_rw = row.read::(5)?; - db_struct.date = row.read::(6)?; - Ok(db_struct) +fn populate_db_struct(stmt: &Statement) -> Result { + Ok(Database { + name: stmt.read(0)?, + snap_id: stmt.read(1)?, + kind: stmt.read(2)?, + source: stmt.read(3)?, + destination: stmt.read(4)?, + machine: stmt.read(5)?, + ro_rw: stmt.read(6)?, + date: stmt.read(7)?, + }) } diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index 245cff2..0000000 --- a/src/errors.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub use failure::{Error, ResultExt}; -pub type Result = ::std::result::Result; diff --git a/src/lib.rs b/src/lib.rs index 45d64eb..4062b4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ pub mod args; pub mod controller; pub mod database; -pub mod errors; -mod operations; -mod structs; +pub mod operations; +pub mod structs; +pub mod utils; diff --git a/src/main.rs b/src/main.rs index 12efa74..6c762a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,41 +1,51 @@ -use rusnapshot::{args, controller, database, errors::*}; +use { + anyhow::Result, + chrono::Utc, + clap::Parser, + rusnapshot::{args, controller, structs::ExtraArgs, utils}, +}; -fn run() -> Result<()> { - let mut arguments = args::get_args(); +fn try_run() -> Result<()> { + let mut arguments = args::Args::parse(); - if !arguments.source_dir.is_empty() && !arguments.source_dir.ends_with('/') { - arguments.source_dir += "/" - } - if !arguments.dest_dir.is_empty() && !arguments.dest_dir.ends_with('/') { - arguments.dest_dir += "/" + if arguments.config_file.is_some() { + arguments.from_config_file()?; } - database::setup_initial_database(&arguments.database_connection)?; + let mut extra_args = ExtraArgs { + snapshot_name: format!( + "{}-{}", + arguments.snapshot_prefix, + Utc::now().format("%Y-%m-%d-%H-%M-%S-%6f") + ), + database_connection: arguments.database_connection(), + }; if arguments.create_snapshot { - controller::manage_creation(&mut arguments)? + utils::check_creation_requirements(&mut arguments, &extra_args)?; + } + + if arguments.create_snapshot { + controller::manage_creation(&mut arguments, &extra_args)?; } if arguments.delete_snapshot { - controller::manage_deletion(&mut arguments)? + controller::manage_deletion(&arguments, &mut extra_args)?; } if arguments.list_snapshots { - controller::manage_listing(&mut arguments)? + controller::manage_listing(&extra_args.database_connection)?; } if arguments.clean_snapshots { - controller::keep_only_x(&mut arguments)? + controller::keep_only_x(&mut arguments, &mut extra_args)?; } if arguments.restore_snapshot { - controller::manage_restoring(&mut arguments)? + controller::manage_restoring(&mut arguments, &mut extra_args)?; } Ok(()) } fn main() { - if let Err(err) = run() { - eprintln!("\nError: {}", err); - for cause in err.iter_chain().skip(1) { - eprintln!("Error description: {}", cause); - } + if let Err(err) = try_run() { + eprintln!("\nError: {err}"); std::process::exit(1); } } diff --git a/src/operations.rs b/src/operations.rs index d37c913..6db1448 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -1,13 +1,18 @@ use { - crate::structs::Args, - std::{path::Path, process::Command}, + crate::{args::Args, utils::is_same_character}, + anyhow::Result, + std::{ + path::{Path, PathBuf}, + process::Command, + }, }; -pub fn take_snapshot(args: &Args) -> bool { - let snapshot_name = format!("{}/{}", args.dest_dir, args.snapshot_name); +#[must_use] +pub fn take_snapshot(args: &Args, snapshot_name: &str) -> bool { + let snapshot_name = format!("{}/{}", args.dest_dir, snapshot_name); let mut btrfs_args = vec!["subvolume", "snapshot", &args.source_dir, &snapshot_name]; - if !args.rw_snapshots { - btrfs_args.push("-r") + if !args.read_write { + btrfs_args.push("-r"); } Command::new("btrfs") .args(&btrfs_args) @@ -16,29 +21,39 @@ pub fn take_snapshot(args: &Args) -> bool { .success() } -pub fn del_snapshot(args: &Args) -> bool { +#[must_use] +pub fn del_snapshot(snapshot_name: &str) -> bool { + // Refuse to delete the root subvolume. + if snapshot_name == "/" || is_same_character(snapshot_name, '/') { + println!( + "Snapshot name to delete is: {snapshot_name}. Refusing to delete the root subvolume." + ); + std::process::exit(1); + } Command::new("btrfs") - .args(&["subvolume", "delete", &args.snapshot_name]) + .args(["subvolume", "delete", snapshot_name]) .status() .expect("Error deleting the snapshot.") .success() } -pub fn restore_snapshot(args: &Args) -> bool { - (!Path::new(&args.source_dir).exists() - || Command::new("btrfs") - .args(&["subvolume", "delete", &args.source_dir]) - .status() - .expect("Error deleting the subvolume.") - .success()) +#[must_use] +pub fn restore_snapshot(args: &Args, snapshot_name: &str) -> bool { + !Path::new(&args.dest_dir).exists() && Command::new("btrfs") - .args(&[ - "subvolume", - "snapshot", - &args.snapshot_name, - &args.source_dir, - ]) + .args(["subvolume", "snapshot", snapshot_name, &args.dest_dir]) .status() .expect("Error restoring the snapshot.") .success() } + +pub fn setup_directory_structure(args: &Args) -> Result<()> { + let dest_dir = PathBuf::from(&args.dest_dir); + if !dest_dir.exists() { + Command::new("btrfs") + .args(["subvolume", "create", &args.dest_dir]) + .spawn()?; + } + + Ok(()) +} diff --git a/src/structs.rs b/src/structs.rs index 6bccf66..ddad18d 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,23 +1,6 @@ use sqlite::Connection; -pub struct Args { - pub create_snapshot: bool, - pub delete_snapshot: bool, - pub list_snapshots: bool, - pub clean_snapshots: bool, - pub restore_snapshot: bool, - pub rw_snapshots: bool, - pub dest_dir: String, - pub source_dir: String, - pub database_connection: Connection, - pub snapshot_name: String, - pub snapshot_id: String, - pub snapshot_prefix: String, - pub snapshot_kind: String, - pub snapshot_ro_rw: String, - pub keep_only: usize, -} - +#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Database { pub name: String, pub snap_id: String, @@ -25,19 +8,11 @@ pub struct Database { pub source: String, pub destination: String, pub ro_rw: String, + pub machine: String, pub date: String, } -impl Database { - pub fn default() -> Database { - Database { - name: String::new(), - snap_id: String::new(), - kind: String::new(), - source: String::new(), - destination: String::new(), - ro_rw: String::new(), - date: String::new(), - } - } +pub struct ExtraArgs { + pub snapshot_name: String, + pub database_connection: Connection, } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..3f63313 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,50 @@ +use { + crate::{args::Args, structs::ExtraArgs}, + anyhow::Result, + std::path::Path, +}; + +#[must_use] +pub fn is_same_character(s: &str, specific_char: char) -> bool { + s.chars().all(|c| c == specific_char) +} + +pub fn check_creation_requirements(arguments: &mut Args, extra_args: &ExtraArgs) -> Result<()> { + arguments.check_for_source_and_dest_dir(); + + arguments.init(extra_args)?; + + // It's required to have a trailing slash for the source and destination directories. + // Otherwise, when we retrieve the snapshot data from the database, we won't be able to + // restore/delete the snapshot because the source/destination paths won't match. + if !arguments.source_dir.is_empty() && !arguments.source_dir.ends_with('/') { + arguments.source_dir += "/"; + } + if !arguments.dest_dir.is_empty() && !arguments.dest_dir.ends_with('/') { + arguments.dest_dir += "/"; + } + + // We need to make sure that the paths for source and destination are full paths. + // If they are not, we will use the current working directory to build the full path. + if !Path::new(&arguments.source_dir).is_absolute() { + arguments.source_dir = std::env::current_dir()? + .join(&arguments.source_dir) + .to_str() + .expect("Failed to get the source directory") + .to_string(); + } + + if !Path::new(&arguments.dest_dir).is_absolute() { + arguments.dest_dir = std::env::current_dir()? + .join(&arguments.dest_dir) + .to_str() + .expect("Failed to get the destination directory") + .to_string(); + } + + if arguments.machine.is_empty() { + arguments.machine = hostname::get()?.to_string_lossy().to_string(); + } + + Ok(()) +}