diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 643343b..8404f4f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,9 +33,9 @@ jobs: with: name: ${{ matrix.os }} path: | - target/release/pkg-upd - target/release/pkg-ver - target/release/pkg-web + target/release/aer + target/release/aer-ver + target/release/aer-web target/release/*.bin target/release/*.exe - name: Test projects with all features diff --git a/.vscode/launch.json b/.vscode/launch.json index ea2c1da..427ad88 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,36 +7,26 @@ { "type": "lldb", "request": "launch", - "name": "Debug unit tests in library 'pkg-data'", + "name": "Debug executable 'aer'", "cargo": { - "args": [ - "test", - "--no-run", - "--lib", - "--package=pkg-data" - ], + "args": ["build", "--bin=aer", "--package=aer"], "filter": { - "name": "pkg-data", - "kind": "lib" + "name": "aer", + "kind": "bin" } }, - "args": [], + "args": ["aer_upd/test-data/deserialize-full.aer.toml"], "cwd": "${workspaceFolder}" }, { "type": "lldb", "request": "launch", - "name": "Debug unit tests in library 'pkg-license'", + "name": "Debug unit tests in executable 'aer'", "cargo": { - "args": [ - "test", - "--no-run", - "--lib", - "--package=pkg-license" - ], + "args": ["test", "--no-run", "--bin=aer", "--package=aer"], "filter": { - "name": "pkg-license", - "kind": "lib" + "name": "aer", + "kind": "bin" } }, "args": [], @@ -45,16 +35,11 @@ { "type": "lldb", "request": "launch", - "name": "Debug unit tests in library 'pkg-version'", + "name": "Debug unit tests in library 'aer_upd'", "cargo": { - "args": [ - "test", - "--no-run", - "--lib", - "--package=pkg-version" - ], + "args": ["test", "--no-run", "--lib", "--package=aer_upd"], "filter": { - "name": "pkg-version", + "name": "aer_upd", "kind": "lib" } }, @@ -64,16 +49,12 @@ { "type": "lldb", "request": "launch", - "name": "Debug example 'chocolatey'", + "name": "Debug unit tests in library 'aer_data'", "cargo": { - "args": [ - "build", - "--example=chocolatey", - "--package=pkg-version" - ], + "args": ["test", "--no-run", "--lib", "--package=aer_data"], "filter": { - "name": "chocolatey", - "kind": "example" + "name": "aer_data", + "kind": "lib" } }, "args": [], @@ -82,17 +63,12 @@ { "type": "lldb", "request": "launch", - "name": "Debug unit tests in example 'chocolatey'", + "name": "Debug unit tests in library 'aer_license'", "cargo": { - "args": [ - "test", - "--no-run", - "--example=chocolatey", - "--package=pkg-version" - ], + "args": ["test", "--no-run", "--lib", "--package=aer_license"], "filter": { - "name": "chocolatey", - "kind": "example" + "name": "aer_license", + "kind": "lib" } }, "args": [], @@ -101,16 +77,11 @@ { "type": "lldb", "request": "launch", - "name": "Debug unit tests in library 'pkg-upd'", + "name": "Debug unit tests in library 'aer_version'", "cargo": { - "args": [ - "test", - "--no-run", - "--lib", - "--package=pkg-upd" - ], + "args": ["test", "--no-run", "--lib", "--package=aer_version"], "filter": { - "name": "pkg-upd", + "name": "aer_version", "kind": "lib" } }, @@ -120,16 +91,16 @@ { "type": "lldb", "request": "launch", - "name": "Debug executable 'pkg-upd'", + "name": "Debug example 'chocolatey'", "cargo": { "args": [ "build", - "--bin=pkg-upd", - "--package=pkg-upd" + "--example=chocolatey", + "--package=aer_version" ], "filter": { - "name": "pkg-upd", - "kind": "bin" + "name": "chocolatey", + "kind": "example" } }, "args": [], @@ -138,17 +109,17 @@ { "type": "lldb", "request": "launch", - "name": "Debug unit tests in executable 'pkg-upd'", + "name": "Debug unit tests in example 'chocolatey'", "cargo": { "args": [ "test", "--no-run", - "--bin=pkg-upd", - "--package=pkg-upd" + "--example=chocolatey", + "--package=aer_version" ], "filter": { - "name": "pkg-upd", - "kind": "bin" + "name": "chocolatey", + "kind": "example" } }, "args": [], @@ -157,16 +128,11 @@ { "type": "lldb", "request": "launch", - "name": "Debug unit tests in library 'pkg-web'", + "name": "Debug unit tests in library 'aer_web'", "cargo": { - "args": [ - "test", - "--no-run", - "--lib", - "--package=pkg-web" - ], + "args": ["test", "--no-run", "--lib", "--package=aer_web"], "filter": { - "name": "pkg-web", + "name": "aer_web", "kind": "lib" } }, @@ -174,4 +140,4 @@ "cwd": "${workspaceFolder}" } ] -} \ No newline at end of file +} diff --git a/Cargo.lock b/Cargo.lock index 10b3822..f2f7d0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "addr2line" version = "0.14.1" @@ -15,6 +17,89 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aer" +version = "0.1.0" +dependencies = [ + "aer_upd", + "assert_cmd", + "chrono", + "fern", + "human-panic", + "human_bytes", + "lazy_static", + "log", + "md-5", + "predicates", + "regex", + "rstest", + "rusty-hook", + "sha-1", + "sha2", + "structopt", + "yansi", +] + +[[package]] +name = "aer_data" +version = "0.1.0" +dependencies = [ + "aer_license", + "aer_version", + "rstest", + "serde", + "url", + "whoami", +] + +[[package]] +name = "aer_license" +version = "0.1.0" +dependencies = [ + "license", + "rstest", + "serde", + "url", +] + +[[package]] +name = "aer_upd" +version = "0.1.0" +dependencies = [ + "aer_data", + "aer_web", + "lazy_static", + "log", + "rstest", + "serde", + "serde_json", + "toml", +] + +[[package]] +name = "aer_version" +version = "0.1.0" +dependencies = [ + "chrono", + "num", + "rstest", + "semver", + "serde", +] + +[[package]] +name = "aer_web" +version = "0.1.0" +dependencies = [ + "aer_version", + "lazy_static", + "log", + "regex", + "reqwest", + "rstest", + "select", +] + [[package]] name = "aho-corasick" version = "0.7.15" @@ -187,6 +272,7 @@ dependencies = [ "atty", "bitflags", "strsim", + "term_size", "textwrap", "unicode-width", "vec_map", @@ -260,7 +346,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065" dependencies = [ - "chrono", "log", ] @@ -628,9 +713,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -643,9 +728,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714" [[package]] name = "license" @@ -1042,82 +1127,6 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" -[[package]] -name = "pkg-data" -version = "0.1.0" -dependencies = [ - "pkg-license", - "pkg-version", - "rstest", - "serde", - "url", - "whoami", -] - -[[package]] -name = "pkg-license" -version = "0.1.0" -dependencies = [ - "license", - "rstest", - "serde", - "url", -] - -[[package]] -name = "pkg-upd" -version = "0.1.0" -dependencies = [ - "assert_cmd", - "chrono", - "fern", - "human-panic", - "human_bytes", - "lazy_static", - "log", - "md-5", - "pkg-data", - "pkg-license", - "pkg-version", - "pkg_web", - "predicates", - "rstest", - "rusty-hook", - "serde", - "serde_json", - "sha-1", - "sha2", - "structopt", - "toml", - "url", - "whoami", - "yansi", -] - -[[package]] -name = "pkg-version" -version = "0.1.0" -dependencies = [ - "chrono", - "num", - "rstest", - "semver", - "serde", -] - -[[package]] -name = "pkg_web" -version = "0.1.0" -dependencies = [ - "lazy_static", - "log", - "pkg-version", - "regex", - "reqwest", - "rstest", - "select", -] - [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1197,9 +1206,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -1687,9 +1696,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.65" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" +checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" dependencies = [ "proc-macro2", "quote", @@ -1721,6 +1730,16 @@ dependencies = [ "utf-8", ] +[[package]] +name = "term_size" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "termcolor" version = "1.1.2" @@ -1736,6 +1755,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ + "term_size", "unicode-width", ] @@ -1995,9 +2015,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasm-bindgen" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ "cfg-if", "serde", @@ -2007,9 +2027,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", @@ -2022,9 +2042,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73157efb9af26fb564bb59a009afd1c7c334a44db171d280690d0c3faaec3468" +checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea" dependencies = [ "cfg-if", "js-sys", @@ -2034,9 +2054,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2044,9 +2064,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", @@ -2057,15 +2077,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "web-sys" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310" +checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" dependencies = [ "js-sys", "wasm-bindgen", @@ -2083,9 +2103,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" dependencies = [ "webpki", ] diff --git a/Cargo.toml b/Cargo.toml index 2418248..89722a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,11 @@ [workspace] members = [ - "pkg-data", - "pkg-license", - "pkg-upd", - "pkg-version", - "pkg_web" + "aer", + "aer_data", + "aer_license", + "aer_upd", + "aer_version", + "aer_web" ] [profile.release] diff --git a/README.md b/README.md index c12d766..2a90350 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# pkg-upd +# AER Tool to update packages diff --git a/aer/Cargo.toml b/aer/Cargo.toml new file mode 100644 index 0000000..f07bf6c --- /dev/null +++ b/aer/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "aer" +version = "0.1.0" +authors = ["AdmiringWorm "] +edition = "2018" + +[features] +default = ["human"] +human = ["human-panic", "human_bytes"] + +[[bin]] +name = "aer-ver" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aer_upd = { path = "../aer_upd" } +chrono = "0.4.19" +fern = "0.6.0" +human-panic = { git = "https://github.com/WormieCorp/human-panic", branch = "additional-info", optional = true } +human_bytes = { version = "0.2.1", optional = true } +lazy_static = "1.4.0" +log = "0.4.14" +md-5 = "0.9.1" +regex = "1.4.5" +sha-1 = "0.9.4" +sha2 = "0.9.3" +structopt = { version = "0.3.21", features = ["wrap_help"] } +yansi = "0.5.0" + +[dev-dependencies] +assert_cmd = "1.0.3" +predicates = "1.0.7" +rstest = "0.7.0" +rusty-hook = "0.11.2" diff --git a/pkg-upd/src/bin/pkg-ver.rs b/aer/src/bin/aer-ver.rs similarity index 55% rename from pkg-upd/src/bin/pkg-ver.rs rename to aer/src/bin/aer-ver.rs index 1dfe83c..1c93ba9 100644 --- a/pkg-upd/src/bin/pkg-ver.rs +++ b/aer/src/bin/aer-ver.rs @@ -1,33 +1,46 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project +#![windows_subsystem = "console"] use std::fmt::Display; +use aer::{log_data, logging}; +use aer_upd::data::chocolatey::ChocoVersion; +use aer_upd::data::SemVersion; +#[cfg(feature = "human")] use human_panic::setup_panic; -use pkg_version::*; +use lazy_static::lazy_static; +use log::info; use structopt::StructOpt; -use yansi::Color; +use yansi::{Color, Paint, Style}; + +log_data! {"aer-ver"} /// Parses version strings and outputs the converted values to the version that -/// will be used by different package managers. Additionally shows the -/// equivalent Semantic Version (as per Rust specifications). +/// will be used by the supported package managers. Additionally shows the +/// equivalent Semantic Version (as per Rust specifications) when possible. #[derive(StructOpt)] -#[structopt(author = "AdmiringWorm ", name = "pkg-ver")] +#[structopt(author = env!("CARGO_PKG_AUTHORS"), name = "aer-ver")] struct Arguments { /// The Versions to test what they would be transformed to (*multiple values /// can be specified*). - #[structopt(required = true, name = "VERSIONS")] + #[structopt(required = true)] versions: Vec, + + #[structopt(flatten)] + log: LogData, } fn main() { + #[cfg(feature = "human")] setup_panic!(); - if cfg!(windows) && !yansi::Paint::enable_windows_ascii() { - yansi::Paint::disable(); + if cfg!(windows) && !Paint::enable_windows_ascii() { + Paint::disable(); } let args = Arguments::from_args(); + logging::setup_logging(&args.log).expect("Unable to configure logging of the application!"); - println!( + info!( "Checking {} {}...", args.versions.len(), if args.versions.len() == 1 { @@ -38,9 +51,10 @@ fn main() { ); for version in args.versions { - println!(); + println!(); // We don't need to add an empty line in the log file print_line("Raw Version", &version); - let choco = chocolatey::ChocoVersion::parse(&version); + println!(); + let choco = ChocoVersion::parse(&version); if let Ok(choco) = choco { print_line("Chocolatey", &choco); let semver: SemVersion = choco.into(); @@ -49,11 +63,12 @@ fn main() { print_line("Chocolatey", "None"); print_line("SemVer from Choco", "None"); } + println!(); let semver = SemVersion::parse(&version); if let Ok(semver) = semver { print_line("SemVer", &semver); - let choco: chocolatey::ChocoVersion = semver.into(); + let choco: ChocoVersion = semver.into(); print_line("Choco from SemVer", choco); } else { print_line("SemVer", "None"); @@ -63,12 +78,14 @@ fn main() { } fn print_line(name: T, value: V) { - let name_style = Color::Magenta.style(); - let value_style = Color::Cyan.style(); + lazy_static! { + static ref NAME_STYLE: Style = Color::Magenta.style(); + static ref VALUE_STYLE: Style = Color::Cyan.style(); + }; - println!( - "{:<18}: {}", - name_style.paint(name), - value_style.paint(value) + info!( + "{:>18} : {}", + NAME_STYLE.paint(name), + VALUE_STYLE.paint(value) ); } diff --git a/aer/src/bin/aer-web.rs b/aer/src/bin/aer-web.rs new file mode 100644 index 0000000..fea00ff --- /dev/null +++ b/aer/src/bin/aer-web.rs @@ -0,0 +1,318 @@ +// Copyright (c) 2021 Kim J. Nordmo and WormieCorp. +// Licensed under the MIT license. See LICENSE.txt file in the project +#![windows_subsystem = "console"] + +use std::fmt::Display; +use std::path::PathBuf; + +use aer::{log_data, logging, ChecksumType}; +use aer_upd::data::Url; +use aer_upd::web::errors::WebError; +use aer_upd::web::{LinkElement, LinkType, ResponseType, WebRequest, WebResponse}; +#[cfg(feature = "human")] +use human_bytes::human_bytes; +#[cfg(feature = "human")] +use human_panic::setup_panic; +use lazy_static::lazy_static; +use log::{error, info, warn}; +use structopt::StructOpt; +use yansi::{Color, Paint, Style}; + +log_data! { "aer-web" } + +#[derive(StructOpt)] +#[structopt(after_help = "EXAMPLES: + Parsing all urls + `parse https://github.com/codecove/codecov-exe/releases/latest`Parsing \ + on matching urls + `parse https://github.com/codecove/codecov-exe/releases/latest \ + --regex '.*\\.zip$'` + Parsing while extracting version + `parse https://github.com/codecov/codecov-exe/releases/latest \ + --regex '/(?P[\\d\\.]+)/.*\\.zip$'`")] +struct ParseArguments { + /// The url to use to test parsing a single web page. + url: Url, + + /// The regular expression to use when parsing the specified `url`. + #[structopt(long, short)] + regex: Option, +} + +#[derive(StructOpt)] +struct DownloadArguments { + /// The url of the binary file to download. + url: Url, + + /// The etag that will be matched against the download location. If matched + /// and the server returns a Not Modified response, then no file will be + /// downloaded. + #[structopt(long, short)] + etag: Option, + + /// The last modified date as a string, this is usually the date that has + /// been previously returned by a server. If this date matches and the + /// server responds with a Not Modified response, then no file will be + /// downloaded. + #[structopt(long, short)] + last_modified: Option, + + /// The checksum to compare the downladed file with. If an existing file + /// with the a matching name and it matches the checksum, then a download + /// will not occurr (*NOT IMPLEMENTED*). + #[structopt(long, short)] + checksum: Option, + + /// The type of the checksum to use when comparing and/or outputting to the + /// console. + #[structopt(long, default_value, possible_values = ChecksumType::variants_str(), env = "AER_CHECKSUM_TYPE")] + checksum_type: ChecksumType, + + /// The directory to use when downloading the files. NOTE: This directory + /// must exist. [default: %TEMP%] + #[structopt(long, parse(from_os_str))] + work_dir: Option, +} + +#[derive(StructOpt)] +enum Commands { + /// Allows testing a single parse command using the specified url, and + /// optionally an regex. This will output any links found on the website. + Parse(ParseArguments), + /// Allows downloading a single binary file, by defailt this command will + /// use `%TEMP%` as the work directory and will remove the downladed file + /// afterwards. + Download(DownloadArguments), +} + +/// Allows testing different web related tasks. The currently supported tasks +/// included the ability to parse HTML websites, and downloading binary files. +#[derive(StructOpt)] +#[structopt(author = env!("CARGO_PKG_AUTHORS"), name = "aer-web")] +struct Arguments { + #[structopt(subcommand)] + cmd: Commands, + + #[structopt(flatten)] + log: LogData, + + /// Disable the usage of colors when outputting text to the console. + #[structopt(long, global = true, env = "NO_COLOR")] + no_color: bool, +} + +fn main() { + #[cfg(feature = "human")] + setup_panic!(); + let args = Arguments::from_args(); + if args.no_color || (cfg!(windows) && !Paint::enable_windows_ascii()) { + Paint::disable() + } + + logging::setup_logging(&args.log).expect("Unable to configure logging of the application!"); + + let request = WebRequest::create(); + match args.cmd { + Commands::Parse(args) => parse_cmd(request, args), + Commands::Download(args) => download_cmd(request, args), + } +} + +fn parse_cmd(request: WebRequest, args: ParseArguments) { + match parse_website(request, args.url, args.regex) { + Ok((parent, links)) => { + info!( + "Successfully parsed '{}'", + Color::Magenta.paint(parent.link) + ); + + for link in &links { + info!( + "{} (type: {}, title: {}, version: {}, text: {})", + Color::Magenta.paint(&link.link), + Color::Cyan.paint(link.link_type), + Color::Cyan.paint(if link.title.is_empty() { + "None" + } else { + &link.title + }), + Color::Cyan.paint(if let Some(version) = &link.version { + format!("{}", version) + } else { + "None".into() + }), + Color::Cyan.paint(&link.text) + ); + } + + info!( + "Found {} links on the webpage!", + Color::Cyan.paint(links.len()) + ); + info!("The following link types was found!"); + for link_type in LinkType::variants() { + let count = links.iter().filter(|l| l.link_type == *link_type).count(); + + info!("Found {:2} {} types!", Color::Cyan.paint(count), link_type); + } + } + Err(err) => { + error!("Unable to parse the requested website!"); + error!("Error message: {}", err); + std::process::exit(1); + } + } +} + +fn download_cmd(request: WebRequest, mut args: DownloadArguments) { + let temp_dir = if let Some(work_dir) = args.work_dir { + work_dir + } else { + std::env::temp_dir() + }; + args.work_dir = Some(temp_dir); + + if let Err(err) = download_file(request, args) { + error!("Unable to download the file. Error: {}", err); + std::process::exit(1); + } +} + +fn parse_website( + request: WebRequest, + url: Url, + regex: Option, +) -> Result<(LinkElement, Vec), WebError> { + let response = request.get_html_response(url.as_str())?; + + if let Some(ref regex) = regex { + response.read(Some(regex)) + } else { + response.read(None) + } +} + +fn download_file(request: WebRequest, args: DownloadArguments) -> Result<(), WebError> { + let etag = if let Some(ref etag) = args.etag { + Some(etag.as_str()) + } else { + None + }; + let last_modified = if let Some(ref last_modified) = args.last_modified { + Some(last_modified.as_str()) + } else { + None + }; + + let response = request.get_binary_response(args.url.as_str(), etag, last_modified)?; + + match response { + ResponseType::Updated(_) => { + info!("No download is necessary!"); + } + ResponseType::New(mut response, _) => { + let work_dir = args.work_dir.unwrap(); // We use unwrap due to the work directory being expected to be set at this point + let file_name = response.file_name().unwrap(); + let possible_path = work_dir.join(file_name); + if possible_path.exists() { + if let Some(ref checksum) = args.checksum { + let file_checksum = args.checksum_type.generate(&possible_path)?; + if checksum.to_lowercase() == file_checksum { + info!( + "File exists, and matches the specified checksum. Nothing to download!" + ); + return Ok(()); + } else { + warn!( + "File exists, but do not match the specified checksum. Re-downloading \ + file!" + ); + } + } else { + info!("File exists, but no checksum available. Continuing download!!"); + } + } + + response.set_work_dir(&work_dir); + + let (etag, last_modified) = get_info(&response); + let result = response.read(None)?; // TODO: pass in file name if specified + info!("The following information was given by the server:"); + print_string("ETag", etag.trim_matches('"')); + print_string("Last Modified", &last_modified); + + match args.checksum_type.generate(&result) { + Ok(checksum) => { + print_line("Checksum", &checksum); + print_line("Checksum Type", args.checksum_type); + + if let Some(original_checksum) = args.checksum { + if original_checksum.to_lowercase() == checksum { + info!( + "{}", + Color::Green.paint( + "Original Checksum matches the checksum of the downloaded \ + file!" + ) + ); + } else { + error!( + "Original Checksum did not match the checksum of the downloaded \ + file!" + ); + } + } + } + Err(err) => error!("Unable to generate checksum: {}", err), + } + + let len = if cfg!(feature = "human") { + human_bytes(result.metadata()?.len() as f64) + } else { + format!("{} bytes", result.metadata()?.len()) + }; + + info!("The resulting file is {} long!", Color::Cyan.paint(len)); + + let _ = std::fs::remove_file(result); + } + } + + Ok(()) +} + +fn get_info(response: &T) -> (String, String) { + let headers = response.get_headers(); + let mut etag = String::new(); + let mut last_modified = String::new(); + + if let Some(etag_val) = headers.get("etag") { + etag = etag_val.to_string(); + } + if let Some(modified_val) = headers.get("last-modified") { + last_modified = modified_val.to_string(); + } + + (etag, last_modified) +} + +fn print_line(name: T, value: V) { + lazy_static! { + static ref NAME_STYLE: Style = Color::Magenta.style(); + static ref VALUE_STYLE: Style = Color::Cyan.style(); + }; + + info!( + "{:>18} : {}", + NAME_STYLE.paint(name), + VALUE_STYLE.paint(value) + ); +} + +fn print_string(name: T, value: &str) { + if value.is_empty() { + print_line(name, "None"); + } else { + print_line(name, value); + } +} diff --git a/aer/src/lib.rs b/aer/src/lib.rs new file mode 100644 index 0000000..61eb5c2 --- /dev/null +++ b/aer/src/lib.rs @@ -0,0 +1,105 @@ +// Copyright (c) 2021 Kim J. Nordmo and WormieCorp. +// Licensed under the MIT license. See LICENSE.txt file in the project + +pub mod logging; + +use std::fmt::Display; +use std::fs::File; +use std::io::Write; +use std::ops::Add; +use std::path::Path; +use std::str::FromStr; + +use md5::Md5; +use sha1::Sha1; +use sha2::digest::generic_array::ArrayLength; +use sha2::{Digest, Sha256, Sha512}; +use structopt::StructOpt; + +#[derive(StructOpt)] +pub enum ChecksumType { + Md5, + Sha1, + Sha256, + Sha512, +} + +impl FromStr for ChecksumType { + type Err = &'static str; + + fn from_str(val: &str) -> std::result::Result::Err> { + let val: &str = &val.trim().to_lowercase(); + + match val { + "md5" => Ok(ChecksumType::Md5), + "sha1" => Ok(ChecksumType::Sha1), + "sha256" => Ok(ChecksumType::Sha256), + "sha512" => Ok(ChecksumType::Sha512), + _ => Err("The value is not a supported checksum type"), + } + } +} + +impl Display for ChecksumType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + match self { + ChecksumType::Md5 => f.write_str("MD5"), + ChecksumType::Sha1 => f.write_str("SHA1"), + ChecksumType::Sha256 => f.write_str("SHA256"), + ChecksumType::Sha512 => f.write_str("SHA512"), + } + } +} + +impl Default for ChecksumType { + fn default() -> Self { + Self::Sha256 + } +} + +impl ChecksumType { + pub fn variants() -> &'static [ChecksumType] { + static VARIANTS: &[ChecksumType] = &[ + ChecksumType::Md5, + ChecksumType::Sha1, + ChecksumType::Sha256, + ChecksumType::Sha512, + ]; + + VARIANTS + } + + pub fn variants_str() -> &'static [&'static str] { + static VARIANTS: &[&str] = &["MD5", "SHA1", "SHA256", "SHA512"]; + + VARIANTS + } + + pub fn generate(&self, path: &Path) -> Result { + generate_checksum(path, self) + } +} + +fn generate_checksum(path: &Path, checksum_type: &ChecksumType) -> Result { + match checksum_type { + ChecksumType::Md5 => generate_checksum_from_hasher(Md5::new(), path), + ChecksumType::Sha1 => generate_checksum_from_hasher(Sha1::new(), path), + ChecksumType::Sha256 => generate_checksum_from_hasher(Sha256::new(), path), + ChecksumType::Sha512 => generate_checksum_from_hasher(Sha512::new(), path), + } +} + +fn generate_checksum_from_hasher( + mut hasher: T, + path: &Path, +) -> Result +where + ::OutputSize: Add, + <::OutputSize as Add>::Output: ArrayLength, +{ + let mut f = File::open(path)?; + std::io::copy(&mut f, &mut hasher)?; + let result = hasher.finalize(); + + Ok(format!("{:x}", result)) +} diff --git a/pkg-upd/src/logging.rs b/aer/src/logging.rs similarity index 52% rename from pkg-upd/src/logging.rs rename to aer/src/logging.rs index 08dff38..ce5323f 100644 --- a/pkg-upd/src/logging.rs +++ b/aer/src/logging.rs @@ -1,44 +1,57 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project + use std::path::Path; use log::{debug, Level, LevelFilter}; use yansi::{Color, Paint, Style}; -#[derive(Copy, Clone)] -struct Colors { - trace: Style, - debug: Style, - info: Style, - warn: Style, - error: Style, -} - -pub trait LogDataTrait { - fn path(&self) -> &Path; - fn level(&self) -> &LevelFilter; -} - #[macro_export] macro_rules! log_data { + () => { + log_data!{env!("CARGO_PKG_NAME")} + }; ($app_name:expr) => { #[derive(::structopt::StructOpt)] pub struct LogData { - /// The path to where verbose logs should be written. - #[structopt(long = "log", alias = "log-file", env = "PKG_LOG_PATH", global = true, parse(from_os_str), default_value = concat!("./", $app_name, ".log") )] + /// The path to where logs should be written. + #[structopt(long = "log", alias = "log-file", env = "AER_LOG_PATH", global = true, parse(from_os_str), default_value = concat!("./", $app_name, ".log"))] pub path: ::std::path::PathBuf, /// The log level to use when outputting to the console. - #[structopt(short = "-L", long = "log-level", env = "PKG_LOG_LEVEL", global = true, default_value = "info", possible_values = &["trace", "debug", "info", "error"])] + #[structopt(short = "-L", long = "log-level", env = "AER_LOG_LEVEL", global = true, default_value = "info", possible_values = &["trace", "debug", "info", "error" ])] pub level: ::log::LevelFilter, } - impl ::pkg_upd::logging::LogDataTrait for LogData { + impl Default for LogData { + fn default() -> Self { + Self { + path: ::std::path::PathBuf::from(concat!("./", $app_name, ".log")), + level: ::log::LevelFilter::Info + } + } + } + + impl crate::logging::LogDataTrait for LogData { fn path(&self) -> &::std::path::Path { &self.path } fn level(&self) -> &::log::LevelFilter { &self.level } } }; } +pub trait LogDataTrait { + fn path(&self) -> &Path; + fn level(&self) -> &LevelFilter; +} + +#[derive(Copy, Clone)] +struct Colors { + trace: Style, + debug: Style, + info: Style, + warn: Style, + error: Style, +} + impl Colors { fn from_level(&self, level: &Level) -> &Style { match level { @@ -61,66 +74,28 @@ impl Colors { } } +impl Default for Colors { + fn default() -> Self { + Self { + trace: Style::new(Color::Black), + debug: Style::new(Color::Fixed(7)), + info: Style::new(Color::Unset), + warn: Style::new(Color::Fixed(208)).bold(), + error: Style::new(Color::Red).bold(), + } + } +} + pub fn setup_logging(log: &T) -> Result<(), Box> { - let colors = Colors { - trace: Style::new(Color::Black), - debug: Style::new(Color::Fixed(7)), - info: Style::new(Color::Unset), - warn: Style::new(Color::Fixed(208)).bold(), - error: Style::new(Color::Red).bold(), - }; - let html5ever_level = if log.level() > &log::LevelFilter::Info { - &log::LevelFilter::Info - } else { - log.level() - }; + let colors = Colors::default(); - let reqwest_level = if log.level() > &log::LevelFilter::Debug { - &log::LevelFilter::Debug - } else { - log.level() - }; - - let cli_info = if log.level() > &log::LevelFilter::Info { - fern::Dispatch::new().format(move |out, message, record| { - let level = record.level(); - out.finish(format_args!( - "[{}]: {}", - colors.paint_level(level), - colors.paint(&level, message) - )); - }) - } else { - fern::Dispatch::new().format(move |out, message, record| { - out.finish(format_args!("{}", colors.paint(&record.level(), message))); - }) - } - .filter(move |metadata| metadata.level() >= log::Level::Info) - .level(*log.level()) - .level_for("html5ever", *html5ever_level) - .level_for("rustls::client::hs", *reqwest_level) - .level_for("rustls::client::tls13", *reqwest_level) - .level_for("tokio_util::codec::framed_impl", *reqwest_level) - .level_for("reqwest::blocking::wait", *reqwest_level) - .chain(std::io::stdout()); - let cli_warn = fern::Dispatch::new() - .format(move |out, message, record| { - let level = record.level(); - out.finish(format_args!( - "[{}]: {}", - colors.paint_level(level), - colors.paint(&level, message) - )); - }) - .filter(move |metadata| metadata.level() <= log::Level::Warn) - .level(*log.level()) - .chain(std::io::stderr()); + let cli_dispatch = configure_cli_dispatch(colors, log); if log.path().exists() { let _ = std::fs::remove_file(log.path()); } - let file_log = fern::Dispatch::new() + let mut file_log = fern::Dispatch::new() .format(move |out, message, record| { out.finish(format_args!( "[{}] {} T[{:?}] [{}] {}:{}: {}", @@ -133,17 +108,15 @@ pub fn setup_logging(log: &T) -> Result<(), Box(log: &T) -> Result<(), Box(colors: Colors, log: &T) -> fern::Dispatch { + let mut cli_info = if log.level() > &LevelFilter::Info { + fern::Dispatch::new().format(move |out, message, record| { + let level = record.level(); + out.finish(format_args!( + "[{}]: {}", + colors.paint_level(level), + colors.paint(&level, message) + )); + }) + } else { + fern::Dispatch::new().format(move |out, message, record| { + out.finish(format_args!("{}", colors.paint(&record.level(), message))) + }) + } + .filter(move |metadata| metadata.level() >= Level::Info) + .level(*log.level()); + + for level in get_levels() { + cli_info = cli_info.level_for(level.0, level.1); + } + + cli_info = cli_info.chain(std::io::stdout()); + + fern::Dispatch::new().chain(cli_info).chain( + fern::Dispatch::new() + .format(move |out, message, record| { + let level = record.level(); + out.finish(format_args!( + "[{}]: {}", + colors.paint_level(level), + colors.paint(&level, message) + )); + }) + .filter(move |metadata| metadata.level() <= Level::Warn) + .level(*log.level()) + .chain(std::io::stderr()), + ) +} + +fn get_levels() -> &'static [(&'static str, LevelFilter)] { + &[ + ("html5ever", LevelFilter::Info), + ("rustls::client::hs", LevelFilter::Debug), + ("rustls::client::tls13", LevelFilter::Debug), + ("tokio_util::codec::framed_impl", LevelFilter::Debug), + ("reqwest::blocking::wait", LevelFilter::Debug), + ] +} diff --git a/aer/src/main.rs b/aer/src/main.rs new file mode 100644 index 0000000..5e3c7ff --- /dev/null +++ b/aer/src/main.rs @@ -0,0 +1,142 @@ +// Copyright (c) 2021 Kim J. Nordmo and WormieCorp. +// Licensed under the MIT license. See LICENSE.txt file in the project +#![windows_subsystem = "console"] +use std::path::{Path, PathBuf}; + +use aer::{log_data, logging}; +use aer_upd::data::*; +use aer_upd::parsers; +use aer_upd::web::{WebRequest, WebResponse}; +#[cfg(feature = "human")] +use human_panic::setup_panic; +use log::{error, info, trace, warn}; +use regex::Regex; +use structopt::StructOpt; +use yansi::Paint; + +log_data! {} + +#[derive(StructOpt)] +#[structopt(author = env!("CARGO_PKG_AUTHORS"))] +struct Arguments { + /// The files containing the necessary data (metadata+updater data) that + /// should be used during the run. + #[structopt(required = true, parse(from_os_str))] + package_files: Vec, + + #[structopt(flatten)] + log: LogData, +} + +fn main() { + #[cfg(feature = "human")] + setup_panic!(); + if cfg!(windows) && !Paint::enable_windows_ascii() { + Paint::disable(); + } + + let args = Arguments::from_args(); + logging::setup_logging(&args.log).expect("Unable to configure logging of the application!"); + + // TODO: Run updating on several threads + for file in args.package_files { + match run_update(&file) { + Err(err) => error!("An error occurred during update process: '{}'", err), + _ => { + todo!() + } + } + } +} + +fn run_update(package_file: &Path) -> Result<(), Box> { + info!("Loading package data from '{}'", "yo"); + + let data = parsers::read_file(&package_file)?; + info!( + "Successfully loaded package data with identifier '{}'!", + data.metadata().id() + ); + + // TODO: Validate data according to specified rule set, default would be Core + + // TODO: Run any before hooks + + let request = WebRequest::create(); + + if let Some(choco) = data.updater().chocolatey() { + let (_, urls) = match &choco.parse_url { + Some(chocolatey::ChocolateyParseUrl::Url(url)) => { + request.get_html_response(url.as_str())?.read(None)? + } + Some(chocolatey::ChocolateyParseUrl::UrlWithRegex { url, ref regex }) => { + info!("Parsing links on '{}' using regex '{}'", url, regex); + let (parent, urls) = request.get_html_response(url.as_str())?.read(Some(regex))?; + if !urls.is_empty() { + info!("{} links found, using first one to get links!", urls.len()); + let url = urls.get(0).unwrap(); + info!("Parsing links on '{}'", url.link); + request.get_html_response(url.link.as_str())?.read(None)? + } else { + (parent, urls) + } + } + _ => { + warn!("No url have been specified to parse!"); + std::process::exit(5); + } + }; + + let mut aarch32 = None; + let mut aarch64 = None; + let mut others = vec![]; + + for (key, regex) in choco.regexes() { + trace!("Filtering {} urls using {}", key, regex); + let re = Regex::new(®ex)?; + let mut items = urls.iter().filter_map(|link| { + let capture = re.captures(link.link.as_str())?; + let mut new_link = link.clone(); + + if let Ok(version) = + Versions::parse(capture.name("version").map(|v| v.as_str()).unwrap_or("")) + { + new_link.version = Some(version); + } + + Some(new_link) + }); + info!("Parsing urls matching '{}' for {}", regex, key); + + if key.to_lowercase() == "arch32" { + info!("Taking first match if found!!"); + aarch32 = items.next(); + } else if key.to_lowercase() == "arch64" { + info!("Taking first match if found!!"); + aarch64 = items.next(); + } else { + for link in items { + others.push(link); + } + } + if let Some(ref aarch32) = aarch32 { + info!("Arch 32: {}", aarch32.link); + } else { + info!("Arch 32: None") + } + if let Some(ref aarch64) = aarch64 { + info!("Arch 64: {}", aarch64.link); + } else { + info!("Arch 64: None"); + } + { + let others: Vec<&str> = others.iter().map(|o| o.link.as_str()).collect(); + info!("Others: {:?}", others); + } + } + + // TODO: Download architecture files + } + + Ok(()) +} diff --git a/pkg-upd/tests/pkg-web.rs b/aer/tests/aer-web.rs similarity index 91% rename from pkg-upd/tests/pkg-web.rs rename to aer/tests/aer-web.rs index 5fa6f2a..ca4caf8 100644 --- a/pkg-upd/tests/pkg-web.rs +++ b/aer/tests/aer-web.rs @@ -1,5 +1,6 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project + use std::process::Command; use assert_cmd::prelude::*; @@ -7,10 +8,11 @@ use predicates::prelude::*; #[test] fn should_parse_with_correct_information_command() -> Result<(), Box> { - let mut cmd = Command::cargo_bin("pkg-web")?; + let mut cmd = Command::cargo_bin("aer-web")?; cmd.args(&["parse", "https://github.com/codecov/codecov-exe/releases"]) .env("NO_COLOR", "true"); + cmd.assert().success().stdout( predicate::str::contains( "https://github.com/codecov/codecov-exe/tree/1.13.0 (type: Unknown, title: 1.13.0, \ @@ -21,12 +23,13 @@ fn should_parse_with_correct_information_command() -> Result<(), Box Result<(), Box> { - let mut cmd = Command::cargo_bin("pkg-web")?; + let mut cmd = Command::cargo_bin("aer-web")?; cmd.args(&["parse", "https://chocolatey.org", "--regex", "github"]) .env("NO_COLOR", "true"); @@ -39,7 +42,7 @@ fn should_parse_with_regex_command() -> Result<(), Box> { .and(predicate::str::contains("https://chocolatey.org").count(1)), /* We will just get 1 * instance, since it * is the url we - * parses */ + * parse */ ); Ok(()) @@ -47,7 +50,7 @@ fn should_parse_with_regex_command() -> Result<(), Box> { #[test] fn should_download_file_and_output_message() -> Result<(), Box> { - let mut cmd = Command::cargo_bin("pkg-web")?; + let mut cmd = Command::cargo_bin("aer-web")?; cmd.args(&[ "download", @@ -79,7 +82,7 @@ fn should_download_file_and_output_message() -> Result<(), Box Result<(), Box> { - let mut cmd = Command::cargo_bin("pkg-web")?; + let mut cmd = Command::cargo_bin("aer-web")?; cmd.args(&[ "download", @@ -92,7 +95,7 @@ fn should_not_download_up_to_date_file() -> Result<(), Box"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["chocolatey"] +chocolatey = ["aer_version/chocolatey"] +serialize = ["aer_license/serialize", "aer_version/serialize", "serde", "url/serde"] + +[dependencies] +aer_license = { path = "../aer_license", default-features = false } +aer_version = { path = "../aer_version", default-features = false } +serde = { version = "1.0.125", optional = true } +url = "2.2.1" +whoami = "1.1.1" + +[dev-dependencies] +rstest = "0.7.0" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/pkg-data/src/defaults.rs b/aer_data/src/defaults.rs similarity index 67% rename from pkg-data/src/defaults.rs rename to aer_data/src/defaults.rs index 26252bc..d9a4444 100644 --- a/pkg-data/src/defaults.rs +++ b/aer_data/src/defaults.rs @@ -1,18 +1,21 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project -use pkg_version::{SemVersion, Versions}; +#[cfg(feature = "chocolatey")] +use aer_version::{SemVersion, Versions}; +#[cfg(feature = "chocolatey")] pub fn boolean_true() -> bool { true } +#[cfg(feature = "chocolatey")] pub fn empty_version() -> Versions { Versions::SemVer(SemVersion::new(0, 0, 0)) } pub fn maintainer() -> Vec { - vec![match std::env::var("PKG_MAINTAINER") { + vec![match std::env::var("AER_MAINTAINER") { Ok(maintainer) => maintainer, Err(_) => whoami::username(), }] diff --git a/pkg-data/src/lib.rs b/aer_data/src/lib.rs similarity index 90% rename from pkg-data/src/lib.rs rename to aer_data/src/lib.rs index e1696ff..838c10c 100644 --- a/pkg-data/src/lib.rs +++ b/aer_data/src/lib.rs @@ -28,22 +28,26 @@ //! """ //! ``` +#![cfg_attr(docsrs, feature(doc_cfg))] + mod defaults; pub mod metadata; pub mod prelude; pub mod updater; +#[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; /// Structure for holding all available data that a user can specify for a /// package. -#[derive(Debug, Deserialize, Serialize, PartialEq)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))] #[non_exhaustive] pub struct PackageData { /// The metadata that will be part of any package that gets created. metadata: metadata::PackageMetadata, - #[serde(default)] + #[cfg_attr(feature = "serialize", serde(default))] updater: updater::PackageUpdateData, } @@ -94,7 +98,7 @@ mod tests { fn metadata_should_return_set_metadata() { let pkg_create = || { let mut pkg = metadata::PackageMetadata::new("test-id"); - pkg.set_license(pkg_license::LicenseType::Expression("MIT".to_owned())); + pkg.set_license(aer_license::LicenseType::Expression("MIT".to_owned())); pkg }; let pkg = PackageData { diff --git a/pkg-data/src/metadata.rs b/aer_data/src/metadata.rs similarity index 87% rename from pkg-data/src/metadata.rs rename to aer_data/src/metadata.rs index 1610f62..94b86a4 100644 --- a/pkg-data/src/metadata.rs +++ b/aer_data/src/metadata.rs @@ -1,17 +1,19 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project +#[cfg(feature = "chocolatey")] pub mod chocolatey; use std::fmt::Display; use std::path::PathBuf; -use pkg_license::LicenseType; +use aer_license::LicenseType; +#[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Debug, Deserialize, Serialize, PartialEq)] -#[serde(untagged)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize), serde(untagged))] pub enum Description { None, Location { @@ -29,7 +31,8 @@ impl PartialEq for Description { } /// Stores common values that are related to 1 or more package managers. -#[derive(Debug, Deserialize, Serialize, PartialEq)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))] #[non_exhaustive] pub struct PackageMetadata { /// The identifier of the package @@ -37,7 +40,7 @@ pub struct PackageMetadata { /// The list of maintainers that are responsible for the creating and /// maintaining of the package(s). - #[serde(default = "crate::defaults::maintainer")] + #[cfg_attr(feature = "serialize", serde(default = "crate::defaults::maintainer"))] maintainers: Vec, /// The main enpoints (homepage) of the software. @@ -81,9 +84,11 @@ pub struct PackageMetadata { /// /// If creating a chocolatey package, a license url is usually necessary /// when pushing to the chocolatey repository. - #[serde(default)] + #[cfg_attr(feature = "serialize", serde(default))] license: LicenseType, + #[cfg(feature = "chocolatey")] + #[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] chocolatey: Option, } @@ -97,6 +102,7 @@ impl PackageMetadata { summary: String::new(), project_url: Url::parse("https://example-repo.org").unwrap(), license: LicenseType::None, + #[cfg(feature = "chocolatey")] chocolatey: None, } } @@ -106,6 +112,8 @@ impl PackageMetadata { &self.id } + #[cfg(feature = "chocolatey")] + #[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] pub fn chocolatey(&self) -> Option<&chocolatey::ChocolateyMetadata> { if let Some(ref choco) = self.chocolatey { Some(&choco) @@ -128,6 +136,8 @@ impl PackageMetadata { &self.license } + #[cfg(feature = "chocolatey")] + #[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] pub fn set_chocolatey(&mut self, choco: chocolatey::ChocolateyMetadata) { self.chocolatey = Some(choco); } @@ -173,6 +183,7 @@ mod tests { project_url: Url::parse("https://example-repo.org").unwrap(), license: LicenseType::None, summary: String::new(), + #[cfg(feature = "chocolatey")] chocolatey: None, }; @@ -214,7 +225,7 @@ mod tests { #[test] fn project_url_should_return_set_project_url() { - let expected = Url::parse("https://github.com/WormieCorp/pkg-upd").unwrap(); + let expected = Url::parse("https://github.com/WormieCorp/aer").unwrap(); let mut pkg = PackageMetadata::new("test"); pkg.project_url = expected.clone(); diff --git a/pkg-data/src/metadata/chocolatey.rs b/aer_data/src/metadata/chocolatey.rs similarity index 91% rename from pkg-data/src/metadata/chocolatey.rs rename to aer_data/src/metadata/chocolatey.rs index 3518cfa..8409513 100644 --- a/pkg-data/src/metadata/chocolatey.rs +++ b/aer_data/src/metadata/chocolatey.rs @@ -5,10 +5,13 @@ //! Variables that are common between different packages managers are located in //! the default package data section. +#![cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] + use std::collections::HashMap; use std::fmt::Display; -use pkg_version::Versions; +use aer_version::Versions; +#[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; use url::Url; @@ -22,7 +25,7 @@ use crate::prelude::Description; /// /// Creating a new data structure with only default empty values. /// ``` -/// use pkg_data::metadata::chocolatey::ChocolateyMetadata; +/// use aer_data::metadata::chocolatey::ChocolateyMetadata; /// /// let mut data = ChocolateyMetadata::new(); /// @@ -31,20 +34,24 @@ use crate::prelude::Description; /// /// Creating a new data structure and initialize it with different values. /// ``` -/// use pkg_data::metadata::chocolatey::ChocolateyMetadata; +/// use aer_data::metadata::chocolatey::ChocolateyMetadata; /// /// let mut data = ChocolateyMetadata::with_authors(&["My-Username"]); /// data.set_description_str("Some description"); /// /// println!("{:#?}", data); /// ``` -#[derive(Debug, Deserialize, Serialize, PartialEq)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))] #[non_exhaustive] pub struct ChocolateyMetadata { /// Wether to force the Chocolatey package to be created using an lowercase /// identifier. This is something required to be used on the Chocolatey /// Community repository. - #[serde(default = "crate::defaults::boolean_true")] + #[cfg_attr( + feature = "serialize", + serde(default = "crate::defaults::boolean_true") + )] lowercase_id: bool, /// The title of the software. @@ -55,7 +62,10 @@ pub struct ChocolateyMetadata { /// The version of the Chocolatey package, can be automatically updated and /// is not necessary to initially be set. - #[serde(default = "crate::defaults::empty_version")] + #[cfg_attr( + feature = "serialize", + serde(default = "crate::defaults::empty_version") + )] pub version: Versions, /// The authors/developers of the software that the package will be created @@ -66,7 +76,10 @@ pub struct ChocolateyMetadata { pub description: Description, /// Wether the license of the software requires users to accept the license. - #[serde(default = "crate::defaults::boolean_true")] + #[cfg_attr( + feature = "serialize", + serde(default = "crate::defaults::boolean_true") + )] pub require_license_acceptance: bool, /// The url to the documentation of the software. @@ -75,13 +88,13 @@ pub struct ChocolateyMetadata { /// The url to where bugs or features to the software should be reported. pub issues_url: Option, - #[serde(default)] + #[cfg_attr(feature = "serialize", serde(default))] tags: Vec, - #[serde(default)] + #[cfg_attr(feature = "serialize", serde(default))] release_notes: Option, - #[serde(default)] + #[cfg_attr(feature = "serialize", serde(default))] dependencies: HashMap, } diff --git a/aer_data/src/prelude.rs b/aer_data/src/prelude.rs new file mode 100644 index 0000000..d0c0b3c --- /dev/null +++ b/aer_data/src/prelude.rs @@ -0,0 +1,22 @@ +// Copyright (c) 2021 Kim J. Nordmo and WormieCorp. +// Licensed under the MIT license. See LICENSE.txt file in the project + +pub use aer_license::LicenseType; +pub use aer_version::{SemVersion, Versions}; +pub use url::Url; + +pub use crate::metadata::{Description, PackageMetadata}; +pub use crate::updater::PackageUpdateData; +pub use crate::PackageData; + +/// Re-Exports of usable chocolatey types. +#[cfg(feature = "chocolatey")] +#[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] +pub mod chocolatey { + pub use aer_version::chocolatey::ChocoVersion; + + pub use crate::metadata::chocolatey::ChocolateyMetadata; + pub use crate::updater::chocolatey::{ + ChocolateyParseUrl, ChocolateyUpdaterData, ChocolateyUpdaterType, + }; +} diff --git a/pkg-data/src/updater.rs b/aer_data/src/updater.rs similarity index 53% rename from pkg-data/src/updater.rs rename to aer_data/src/updater.rs index 61b431d..90cf88f 100644 --- a/pkg-data/src/updater.rs +++ b/aer_data/src/updater.rs @@ -3,23 +3,34 @@ pub mod chocolatey; +#[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; -#[derive(Debug, Default, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Default, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))] #[non_exhaustive] pub struct PackageUpdateData { + #[cfg(feature = "chocolatey")] + #[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] chocolatey: Option, } impl PackageUpdateData { pub fn new() -> PackageUpdateData { - PackageUpdateData { chocolatey: None } + PackageUpdateData { + #[cfg(feature = "chocolatey")] + chocolatey: None, + } } + #[cfg(feature = "chocolatey")] + #[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] pub fn chocolatey(&self) -> &Option { &self.chocolatey } + #[cfg(feature = "chocolatey")] + #[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] pub fn set_chocolatey(&mut self, choco: chocolatey::ChocolateyUpdaterData) { self.chocolatey = Some(choco); } diff --git a/pkg-data/src/updater/chocolatey.rs b/aer_data/src/updater/chocolatey.rs similarity index 70% rename from pkg-data/src/updater/chocolatey.rs rename to aer_data/src/updater/chocolatey.rs index 8ceef16..91d1996 100644 --- a/pkg-data/src/updater/chocolatey.rs +++ b/aer_data/src/updater/chocolatey.rs @@ -1,12 +1,16 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project +#![cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] + use std::collections::HashMap; +#[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Debug, Deserialize, Serialize, PartialEq)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))] pub enum ChocolateyUpdaterType { None, Installer, @@ -19,19 +23,20 @@ impl Default for ChocolateyUpdaterType { } } -#[derive(Debug, Deserialize, Serialize, PartialEq)] -#[serde(untagged)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize), serde(untagged))] pub enum ChocolateyParseUrl { UrlWithRegex { url: Url, regex: String }, Url(Url), } -#[derive(Debug, Default, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Default, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))] #[non_exhaustive] pub struct ChocolateyUpdaterData { - #[serde(default)] + #[cfg_attr(feature = "serialize", serde(default))] pub embedded: bool, - #[serde(default, rename = "type")] + #[cfg_attr(feature = "serialize", serde(default, rename = "type"))] pub _type: ChocolateyUpdaterType, pub parse_url: Option, diff --git a/aer_license/Cargo.toml b/aer_license/Cargo.toml new file mode 100644 index 0000000..b928586 --- /dev/null +++ b/aer_license/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "aer_license" +version = "0.1.0" +authors = ["AdmiringWorm "] +edition = "2018" +publish = false + +[features] +serialize = ["serde", "url/serde"] + +[dependencies] +license = "1.1.10" +serde = { version = "1.0.125", optional = true } +url = "2.2.1" + +[dev-dependencies] +rstest = "0.7.0" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/pkg-license/src/lib.rs b/aer_license/src/lib.rs similarity index 97% rename from pkg-license/src/lib.rs rename to aer_license/src/lib.rs index e53421d..9a03ab5 100644 --- a/pkg-license/src/lib.rs +++ b/aer_license/src/lib.rs @@ -1,12 +1,13 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project +#[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; use url::Url; /// The type or location of the license for the packaged software. -#[derive(Debug, Deserialize, Serialize, PartialEq)] -#[serde(untagged)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize), serde(untagged))] pub enum LicenseType { /// When there are no License available at all. /// The item should in general never be used, but is provided for diff --git a/aer_upd/Cargo.toml b/aer_upd/Cargo.toml new file mode 100644 index 0000000..a44a7ee --- /dev/null +++ b/aer_upd/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "aer_upd" +version = "0.1.0" +authors = ["AdmiringWorm "] +edition = "2018" + +[features] +default = ["powershell", "toml_data"] +toml_data = ["aer_data/chocolatey", "toml", "aer_data/serialize"] +powershell = ["aer_data/serialize", "lazy_static", "serde_json", "serde"] + +[dependencies] +aer_data = { path = "../aer_data" } +aer_web = { path = "../aer_web" } +lazy_static = { version = "1.4.0", optional = true } +log = "0.4.14" +serde = { version = "1.0.125", optional = true } +serde_json = { version = "1.0.64", optional = true } +toml = { version = "0.5.8", optional = true } + +[dev-dependencies] +rstest = "0.7.0" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/pkg-upd/src/lib.rs b/aer_upd/src/lib.rs similarity index 65% rename from pkg-upd/src/lib.rs rename to aer_upd/src/lib.rs index e683c35..bbd875e 100644 --- a/pkg-upd/src/lib.rs +++ b/aer_upd/src/lib.rs @@ -9,9 +9,18 @@ #![doc( html_playground_url = "https://play.rust-lang.org/", - issue_tracker_base_url = "https://github.com/WormieCorp/pkg-upd/issues/" + issue_tracker_base_url = "https://github.com/WormieCorp/aer/issues/" )] +#![cfg_attr(docsrs, feature(doc_cfg))] -pub mod logging; pub mod parsers; pub mod runners; + +pub mod data { + pub use aer_data::prelude::*; +} + +pub mod web { + pub use aer_web::response::ResponseType; + pub use aer_web::{errors, LinkElement, LinkType, WebRequest, WebResponse}; +} diff --git a/pkg-upd/src/parsers.rs b/aer_upd/src/parsers.rs similarity index 92% rename from pkg-upd/src/parsers.rs rename to aer_upd/src/parsers.rs index 1ff38a1..5c5363f 100644 --- a/pkg-upd/src/parsers.rs +++ b/aer_upd/src/parsers.rs @@ -5,8 +5,8 @@ use std::fs::File; use std::io::{BufReader, Error as IoError, ErrorKind, Read}; use std::path::Path; +use aer_data::prelude::*; use log::warn; -use pkg_data::prelude::*; pub mod errors; #[cfg(feature = "toml_data")] @@ -54,6 +54,8 @@ pub trait DataReader { fn read_data(&self, reader: &mut T) -> Result; } +#[cfg(any(feature = "toml_data"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "toml_data"))))] macro_rules! call_parsers { ($path:ident,$($parser:expr=>$feature:literal),+) => { $( @@ -70,6 +72,8 @@ macro_rules! call_parsers { }; } +#[cfg(any(feature = "toml_data"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "toml_data"))))] pub fn read_file(path: &Path) -> Result { call_parsers!(path, toml::TomlParser => "toml_data"); diff --git a/pkg-upd/src/parsers/errors.rs b/aer_upd/src/parsers/errors.rs similarity index 100% rename from pkg-upd/src/parsers/errors.rs rename to aer_upd/src/parsers/errors.rs diff --git a/pkg-upd/src/parsers/toml.rs b/aer_upd/src/parsers/toml.rs similarity index 94% rename from pkg-upd/src/parsers/toml.rs rename to aer_upd/src/parsers/toml.rs index 41b1109..ad6c17f 100644 --- a/pkg-upd/src/parsers/toml.rs +++ b/aer_upd/src/parsers/toml.rs @@ -1,11 +1,13 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project +#![cfg_attr(docsrs, doc(cfg(feature = "toml_data")))] + use std::io::Read; use std::path::Path; +use aer_data::PackageData; use log::{debug, error}; -use pkg_data::PackageData; use crate::parsers::{errors, DataReader}; @@ -17,7 +19,7 @@ pub struct TomlParser; impl DataReader for TomlParser { fn can_handle_file(&self, path: &Path) -> bool { if let Some(path) = path.to_str() { - path.ends_with(".pkg.toml") + path.ends_with(".aer.toml") } else { false } @@ -63,10 +65,9 @@ mod tests { use std::path::PathBuf; use std::str::FromStr; - use pkg_data::prelude::*; - use pkg_version::{SemVersion, Versions}; + use aer_data::prelude::chocolatey::*; + use aer_data::prelude::*; use rstest::rstest; - use url::Url; use super::*; @@ -83,10 +84,10 @@ mod tests { #[rstest( file, case("test-package.toml"), - case("test-package.pkg.yml"), + case("test-package.aer.yml"), case("test-package.xml") )] - fn can_handle_file_returns_false_for_non_pkg_toml_files(file: &str) { + fn can_handle_file_returns_false_for_non_aer_toml_files(file: &str) { let path = PathBuf::from_str(file).unwrap(); let parser = TomlParser; @@ -175,7 +176,7 @@ mod tests { let parser = TomlParser; let mut expected = PackageData::new("test-package"); expected.metadata_mut().set_license(LicenseType::Location( - Url::parse("https://github.com/WormieCorp/pkg-upd/LICENSE.txt").unwrap(), + Url::parse("https://github.com/WormieCorp/aer/LICENSE.txt").unwrap(), )); let actual = parser.read_data(&mut reader).unwrap(); @@ -192,7 +193,7 @@ mod tests { expected .metadata_mut() .set_license(LicenseType::ExpressionAndLocation { - url: Url::parse("https://github.com/WormieCorp/pkg-upd/LICENSE.txt").unwrap(), + url: Url::parse("https://github.com/WormieCorp/aer/LICENSE.txt").unwrap(), expression: "MIT".into(), }); @@ -210,7 +211,7 @@ mod tests { expected .metadata_mut() .set_license(LicenseType::ExpressionAndLocation { - url: Url::parse("https://github.com/WormieCorp/pkg-upd/LICENSE.txt").unwrap(), + url: Url::parse("https://github.com/WormieCorp/aer/LICENSE.txt").unwrap(), expression: "MIT".into(), }); @@ -245,7 +246,7 @@ mod tests { #[test] fn read_data_should_deserialize_all_data() { - const VAL: &[u8] = include_bytes!("../../test-data/deserialize-full.pkg.toml"); + const VAL: &[u8] = include_bytes!("../../test-data/deserialize-full.aer.toml"); let mut reader = BufReader::new(VAL); let parser = TomlParser; let expected = { diff --git a/pkg-upd/src/runners.rs b/aer_upd/src/runners.rs similarity index 70% rename from pkg-upd/src/runners.rs rename to aer_upd/src/runners.rs index 9d407b0..61c723b 100644 --- a/pkg-upd/src/runners.rs +++ b/aer_upd/src/runners.rs @@ -5,11 +5,9 @@ use std::collections::HashMap; use std::fmt::Debug; use std::path::{Path, PathBuf}; -use log::error; -use pkg_data::prelude::*; +use aer_data::prelude::*; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use url::Url; #[cfg(feature = "powershell")] pub mod powershell; @@ -20,7 +18,8 @@ pub struct RunnerData { data: HashMap, } -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(untagged))] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(untagged))] pub enum RunnerChildType { Data(String), Child(RunnerData), @@ -54,6 +53,8 @@ pub trait ScriptRunner { ) -> Result<(), String>; } +#[cfg(any(feature = "powershell"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "powershell"))))] macro_rules! call_runners { ($work_dir:ident,$script_path:ident,$data:ident,$($runner:expr=>$feature:literal),+) => { let script_path = $script_path.canonicalize().unwrap(); @@ -67,6 +68,8 @@ macro_rules! call_runners { }; } +#[cfg(any(feature = "powershell"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "powershell"))))] pub fn run_script( work_dir: &Path, script_path: PathBuf, @@ -75,7 +78,7 @@ pub fn run_script( if !work_dir.exists() { if let Err(err) = std::fs::create_dir_all(work_dir) { let msg = format!("Failed to create work directory: '{}'", err); - error!("{}", msg); + log::error!("{}", msg); return Err(msg); } } @@ -112,7 +115,7 @@ pub trait RunnerCombiner { fn from_runner_data(&mut self, data: RunnerData); } -impl RunnerCombiner for pkg_data::PackageData { +impl RunnerCombiner for aer_data::PackageData { fn to_runner_data(&self) -> RunnerData { let mut data = RunnerData::new(); @@ -200,21 +203,9 @@ fn get_license(values: RunnerData) -> LicenseType { #[cfg(test)] mod tests { - use std::fs::{create_dir_all, File}; - use std::io::{BufWriter, Write}; use super::*; - fn write_file(content: &[u8], file_path: &Path) { - if let Some(parent) = file_path.parent() { - create_dir_all(parent).unwrap(); - } - let file = File::create(PathBuf::from(file_path)).unwrap(); - let mut writer = BufWriter::new(file); - - writer.write_all(content).unwrap(); - } - #[test] fn get_license_should_get_license_expression() { let mut data = RunnerData::new(); @@ -264,74 +255,91 @@ mod tests { assert_eq!(result, LicenseType::None); } - #[test] - fn run_script_should_run_powershell_scripts() { - const SCRIPT: &[u8] = b"param($data) - Write-Host \"Hello world\""; - let file_path = PathBuf::from("./test-files/test-path.ps1"); - write_file(SCRIPT, &file_path); + #[cfg(any(feature = "powershell"))] + mod run_script { + use std::fs::{create_dir_all, File}; + use std::io::{BufWriter, Write}; - let result = run_script( - &PathBuf::from("."), - PathBuf::from(file_path), - &mut PackageData::new("test-package"), - ); + use super::*; - assert_eq!(result, Ok(())) - } - - #[test] - fn run_script_should_return_error_on_unknown_script() { - let file_path = PathBuf::from("./test-files/test-path.qs"); - write_file(b"Test", &file_path); - - let result = run_script( - &PathBuf::from(".").canonicalize().unwrap(), - file_path.clone(), - &mut PackageData::new("test-package"), - ); + fn write_file(content: &[u8], file_path: &Path) { + if let Some(parent) = file_path.parent() { + create_dir_all(parent).unwrap(); + } + let file = File::create(PathBuf::from(file_path)).unwrap(); + let mut writer = BufWriter::new(file); + writer.write_all(content).unwrap(); + } - assert_eq!( - result, - Err(format!( - "No supported runner was found for '{}'", - file_path.display() - )) - ); - } + #[test] + fn run_script_should_run_powershell_scripts() { + const SCRIPT: &[u8] = b"param($data) + Write-Host \"Hello world\""; + let file_path = PathBuf::from("./test-files/test-path.ps1"); + write_file(SCRIPT, &file_path); - #[test] - fn run_script_should_return_error_when_work_dir_is_a_file() { - let work_dir = PathBuf::from("Cargo.toml"); - let result = run_script( - &work_dir, - PathBuf::from("test"), - &mut PackageData::new("test-data"), - ); + let result = run_script( + &PathBuf::from("."), + PathBuf::from(file_path), + &mut PackageData::new("test-package"), + ); - assert_eq!( - result, - Err(format!( - "The specified directory '{}' is not a directory!", - work_dir.canonicalize().unwrap().display() - )) - ); - } + assert_eq!(result, Ok(())) + } - #[test] - fn run_script_should_create_work_directory_if_not_exists() { - let path = PathBuf::from("test-files/work_dir"); + #[test] + fn run_script_should_return_error_on_unknown_script() { + let file_path = PathBuf::from("./test-files/test-path.qs"); + write_file(b"Test", &file_path); + + let result = run_script( + &PathBuf::from(".").canonicalize().unwrap(), + file_path.clone(), + &mut PackageData::new("test-package"), + ); + + assert_eq!( + result, + Err(format!( + "No supported runner was found for '{}'", + file_path.display() + )) + ); + } - if path.exists() { - let _ = std::fs::remove_dir(&path); + #[test] + fn run_script_should_return_error_when_work_dir_is_a_file() { + let work_dir = PathBuf::from("Cargo.toml"); + let result = run_script( + &work_dir, + PathBuf::from("test"), + &mut PackageData::new("test-data"), + ); + + assert_eq!( + result, + Err(format!( + "The specified directory '{}' is not a directory!", + work_dir.canonicalize().unwrap().display() + )) + ); } - let _ = run_script( - &path, - PathBuf::from("Cargo.toml"), - &mut PackageData::new("test-package"), - ); + #[test] + fn run_script_should_create_work_directory_if_not_exists() { + let path = PathBuf::from("test-files/work_dir"); - assert!(path.exists()); + if path.exists() { + let _ = std::fs::remove_dir(&path); + } + + let _ = run_script( + &path, + PathBuf::from("Cargo.toml"), + &mut PackageData::new("test-package"), + ); + + assert!(path.exists()); + } } } diff --git a/pkg-upd/src/runners/powershell.rs b/aer_upd/src/runners/powershell.rs similarity index 95% rename from pkg-upd/src/runners/powershell.rs rename to aer_upd/src/runners/powershell.rs index 702f91b..7434260 100644 --- a/pkg-upd/src/runners/powershell.rs +++ b/aer_upd/src/runners/powershell.rs @@ -1,6 +1,8 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project +#![cfg_attr(docsrs, doc(cfg(any(feature = "powershell"))))] + use std::fmt::Debug; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; @@ -64,8 +66,8 @@ impl ScriptRunner for PowershellRunner { $VerbosePreference = 'Continue'; $DebugPreference = 'Continue'; {} $data = (\"{}\" | \ ConvertFrom-Json -AsHashtable); [int]$exitCode = 0; try {{ {} $data; [int]$exitCode \ = $LASTEXITCODE; }} catch {{ Write-Error $_; if ($LASTEXITCODE -eq 0) {{ \ - [int]$exitCode = 1; }} }}; Write-Host \"## PKG-SCRIPT-RUNNER:START ##\"; Write-Host \ - ($data | ConvertTo-Json); Write-Host \"## PKG-SCRIPT-RUNNER:END ##\"; if ($exitCode \ + [int]$exitCode = 1; }} }}; Write-Host \"## AER-SCRIPT-RUNNER:START ##\"; Write-Host \ + ($data | ConvertTo-Json); Write-Host \"## AER-SCRIPT-RUNNER:END ##\"; if ($exitCode \ -ne 0) {{ throw \"Non-Zero exit code: $exitCode\"; }}", override_script, runner_data.replace("\"", "`\""), @@ -103,12 +105,12 @@ impl ScriptRunner for PowershellRunner { { let stdout = String::from_utf8_lossy(&cmd.stdout); let mut in_data = false; - debug!("PKG-SCRIPT-RUNNER STDOUT:"); + debug!("AER-SCRIPT-RUNNER STDOUT:"); for line in stdout.lines() { match line.trim() { - "## PKG-SCRIPT-RUNNER:START ##" => in_data = true, - "## PKG-SCRIPT-RUNNER:END ##" => in_data = false, + "## AER-SCRIPT-RUNNER:START ##" => in_data = true, + "## AER-SCRIPT-RUNNER:END ##" => in_data = false, line => { if in_data { run_data.push_str(line); @@ -124,7 +126,7 @@ impl ScriptRunner for PowershellRunner { { let stderr = String::from_utf8_lossy(&cmd.stderr); - debug!("PKG-SCRIPT-RUNNER STDERR:"); + debug!("AER-SCRIPT-RUNNER STDERR:"); let mut fail = false; for line in stderr.lines() { @@ -173,9 +175,8 @@ fn get_env_paths() -> Vec { #[cfg(test)] mod tests { - use pkg_data::prelude::*; + use aer_data::prelude::*; use rstest::rstest; - use url::Url; use super::*; @@ -264,7 +265,7 @@ mod tests { assert_eq!(result, Ok(())); assert_eq!( data.metadata().project_url(), - &Url::parse("https://github.com/WormieCorp/pkg-upd").unwrap() + &Url::parse("https://github.com/WormieCorp/aer").unwrap() ); } diff --git a/pkg-upd/test-data/astyle.md b/aer_upd/test-data/astyle.md similarity index 100% rename from pkg-upd/test-data/astyle.md rename to aer_upd/test-data/astyle.md diff --git a/pkg-upd/test-data/basic-metadata.toml b/aer_upd/test-data/basic-metadata.toml similarity index 100% rename from pkg-upd/test-data/basic-metadata.toml rename to aer_upd/test-data/basic-metadata.toml diff --git a/pkg-upd/test-data/deserialize-full.pkg.toml b/aer_upd/test-data/deserialize-full.aer.toml similarity index 100% rename from pkg-upd/test-data/deserialize-full.pkg.toml rename to aer_upd/test-data/deserialize-full.aer.toml diff --git a/pkg-upd/test-data/license-expression.toml b/aer_upd/test-data/license-expression.toml similarity index 100% rename from pkg-upd/test-data/license-expression.toml rename to aer_upd/test-data/license-expression.toml diff --git a/pkg-upd/test-data/license-long.toml b/aer_upd/test-data/license-long.toml similarity index 68% rename from pkg-upd/test-data/license-long.toml rename to aer_upd/test-data/license-long.toml index cce8e56..521bb6f 100644 --- a/pkg-upd/test-data/license-long.toml +++ b/aer_upd/test-data/license-long.toml @@ -4,5 +4,5 @@ project_url = "https://example-repo.org" summary = "" [metadata.license] -url = "https://github.com/WormieCorp/pkg-upd/LICENSE.txt" +url = "https://github.com/WormieCorp/aer/LICENSE.txt" expression = "MIT" diff --git a/aer_upd/test-data/license-short.toml b/aer_upd/test-data/license-short.toml new file mode 100644 index 0000000..d901908 --- /dev/null +++ b/aer_upd/test-data/license-short.toml @@ -0,0 +1,5 @@ +[metadata] +id = "test-package" +project_url = "https://example-repo.org" +summary = "" +license = { url = "https://github.com/WormieCorp/aer/LICENSE.txt", expression = "MIT" } diff --git a/pkg-upd/test-data/license-url.toml b/aer_upd/test-data/license-url.toml similarity index 57% rename from pkg-upd/test-data/license-url.toml rename to aer_upd/test-data/license-url.toml index 3882839..c3c57fb 100644 --- a/pkg-upd/test-data/license-url.toml +++ b/aer_upd/test-data/license-url.toml @@ -2,4 +2,4 @@ id = "test-package" project_url = "https://example-repo.org" summary = "" -license = "https://github.com/WormieCorp/pkg-upd/LICENSE.txt" +license = "https://github.com/WormieCorp/aer/LICENSE.txt" diff --git a/pkg-upd/test-data/metadata-choco.toml b/aer_upd/test-data/metadata-choco.toml similarity index 100% rename from pkg-upd/test-data/metadata-choco.toml rename to aer_upd/test-data/metadata-choco.toml diff --git a/pkg-upd/test-data/ps1/change-identifier.ps1 b/aer_upd/test-data/ps1/change-identifier.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/change-identifier.ps1 rename to aer_upd/test-data/ps1/change-identifier.ps1 diff --git a/pkg-upd/test-data/ps1/change-license-expression.ps1 b/aer_upd/test-data/ps1/change-license-expression.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/change-license-expression.ps1 rename to aer_upd/test-data/ps1/change-license-expression.ps1 diff --git a/pkg-upd/test-data/ps1/change-license-full.ps1 b/aer_upd/test-data/ps1/change-license-full.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/change-license-full.ps1 rename to aer_upd/test-data/ps1/change-license-full.ps1 diff --git a/pkg-upd/test-data/ps1/change-license-url.ps1 b/aer_upd/test-data/ps1/change-license-url.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/change-license-url.ps1 rename to aer_upd/test-data/ps1/change-license-url.ps1 diff --git a/pkg-upd/test-data/ps1/change-project_url.ps1 b/aer_upd/test-data/ps1/change-project_url.ps1 similarity index 64% rename from pkg-upd/test-data/ps1/change-project_url.ps1 rename to aer_upd/test-data/ps1/change-project_url.ps1 index 03ab9da..3d7de72 100644 --- a/pkg-upd/test-data/ps1/change-project_url.ps1 +++ b/aer_upd/test-data/ps1/change-project_url.ps1 @@ -1,4 +1,4 @@ param([hashtable]$data) Write-Information "This script changes the project_url, this should be respected!" -$data.project_url = "https://github.com/WormieCorp/pkg-upd" +$data.project_url = "https://github.com/WormieCorp/aer" diff --git a/pkg-upd/test-data/ps1/change-summary.ps1 b/aer_upd/test-data/ps1/change-summary.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/change-summary.ps1 rename to aer_upd/test-data/ps1/change-summary.ps1 diff --git a/pkg-upd/test-data/ps1/empty-run-with-data.ps1 b/aer_upd/test-data/ps1/empty-run-with-data.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/empty-run-with-data.ps1 rename to aer_upd/test-data/ps1/empty-run-with-data.ps1 diff --git a/pkg-upd/test-data/ps1/empty-run.ps1 b/aer_upd/test-data/ps1/empty-run.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/empty-run.ps1 rename to aer_upd/test-data/ps1/empty-run.ps1 diff --git a/pkg-upd/test-data/ps1/exit-code.ps1 b/aer_upd/test-data/ps1/exit-code.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/exit-code.ps1 rename to aer_upd/test-data/ps1/exit-code.ps1 diff --git a/pkg-upd/test-data/ps1/invalid-powershell.ps1 b/aer_upd/test-data/ps1/invalid-powershell.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/invalid-powershell.ps1 rename to aer_upd/test-data/ps1/invalid-powershell.ps1 diff --git a/pkg-upd/test-data/ps1/with-exception.ps1 b/aer_upd/test-data/ps1/with-exception.ps1 similarity index 100% rename from pkg-upd/test-data/ps1/with-exception.ps1 rename to aer_upd/test-data/ps1/with-exception.ps1 diff --git a/pkg-version/Cargo.toml b/aer_version/Cargo.toml similarity index 52% rename from pkg-version/Cargo.toml rename to aer_version/Cargo.toml index 9442be5..b2f576c 100644 --- a/pkg-version/Cargo.toml +++ b/aer_version/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pkg-version" +name = "aer_version" version = "0.1.0" authors = ["AdmiringWorm "] edition = "2018" @@ -7,13 +7,23 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] +default = ["chocolatey"] serialize = ["semver/serde", "serde"] +chocolatey = ["chrono", "num"] [dependencies] -chrono = "0.4.19" -num = "0.4.0" +chrono = { version = "0.4.19", optional = true } +num = { version = "0.4.0", optional = true } semver = "0.11.0" serde = { version = "1.0.125", optional = true } [dev-dependencies] rstest = "0.7.0" + +[[example]] +name = "chocolatey" +required-features = ["chocolatey"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/pkg-version/examples/chocolatey.rs b/aer_version/examples/chocolatey.rs similarity index 93% rename from pkg-version/examples/chocolatey.rs rename to aer_version/examples/chocolatey.rs index 0873115..707a3c4 100644 --- a/pkg-version/examples/chocolatey.rs +++ b/aer_version/examples/chocolatey.rs @@ -1,8 +1,8 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project -use pkg_version::chocolatey::ChocoVersion; -use pkg_version::{FixVersion, SemVersion}; +use aer_version::chocolatey::ChocoVersion; +use aer_version::{FixVersion, SemVersion}; fn main() { println!( diff --git a/pkg-version/src/lib.rs b/aer_version/src/lib.rs similarity index 78% rename from pkg-version/src/lib.rs rename to aer_version/src/lib.rs index a5bd237..ab987da 100644 --- a/pkg-version/src/lib.rs +++ b/aer_version/src/lib.rs @@ -1,5 +1,6 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project +#![cfg_attr(docsrs, feature(doc_cfg))] mod versions; @@ -9,12 +10,16 @@ use std::fmt::Display; pub use semver::Version as SemVersion; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; -pub use versions::{chocolatey, FixVersion}; +#[cfg(feature = "chocolatey")] +pub use versions::chocolatey; +pub use versions::FixVersion; #[cfg_attr(feature = "serialize", derive(Deserialize, Serialize), serde(untagged))] -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Versions { SemVer(SemVersion), + #[cfg(feature = "chocolatey")] + #[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] Choco(chocolatey::ChocoVersion), } @@ -39,14 +44,23 @@ impl Error for SemanticVersionError {} impl Versions { pub fn parse(val: &str) -> Result> { - if let Ok(semver) = SemVersion::parse(val) { - Ok(Versions::SemVer(semver)) - } else { - let val = chocolatey::ChocoVersion::parse(val)?; - Ok(Versions::Choco(val)) + #[cfg(not(feature = "chocolatey"))] + { + Ok(Versions::SemVer(SemVersion::parse(val)?)) + } + #[cfg(feature = "chocolatey")] + { + if let Ok(semver) = SemVersion::parse(val) { + Ok(Versions::SemVer(semver)) + } else { + let val = chocolatey::ChocoVersion::parse(val)?; + Ok(Versions::Choco(val)) + } } } + #[cfg(feature = "chocolatey")] + #[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] pub fn to_choco(&self) -> chocolatey::ChocoVersion { match self { Versions::SemVer(semver) => chocolatey::ChocoVersion::from(semver.clone()), @@ -57,6 +71,8 @@ impl Versions { pub fn to_semver(&self) -> SemVersion { match self { Versions::SemVer(semver) => semver.clone(), + #[cfg(feature = "chocolatey")] + #[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] Versions::Choco(ver) => SemVersion::from(ver.clone()), } } @@ -66,6 +82,7 @@ impl Display for Versions { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { match self { Versions::SemVer(version) => version.fmt(f), + #[cfg(feature = "chocolatey")] Versions::Choco(version) => version.fmt(f), } } @@ -76,6 +93,7 @@ mod tests { use super::*; #[test] + #[cfg(feature = "chocolatey")] fn to_semver_should_create_semversion_from_choco_version() { let version = Versions::Choco(chocolatey::ChocoVersion::parse("2.1.0.5-alpha0055").unwrap()); @@ -97,6 +115,7 @@ mod tests { } #[test] + #[cfg(feature = "chocolatey")] fn to_choco_should_create_chocolatey_version_from_semver() { let version = Versions::SemVer(SemVersion::parse("1.0.5-beta.55+99").unwrap()); let expected = chocolatey::ChocoVersion::parse("1.0.5-beta-0055").unwrap(); @@ -107,6 +126,7 @@ mod tests { } #[test] + #[cfg(feature = "chocolatey")] fn to_choco_should_returned_cloned_version_of_choco() { let version = Versions::Choco(chocolatey::ChocoVersion::parse("5.2.1.56-unstable-0050").unwrap()); @@ -118,6 +138,7 @@ mod tests { } #[test] + #[cfg(feature = "chocolatey")] fn display_choco_version() { let version = Versions::Choco(chocolatey::ChocoVersion::parse("2.1.0-unstable-0050").unwrap()); diff --git a/pkg-version/src/versions.rs b/aer_version/src/versions.rs similarity index 100% rename from pkg-version/src/versions.rs rename to aer_version/src/versions.rs diff --git a/pkg-version/src/versions/chocolatey.rs b/aer_version/src/versions/chocolatey.rs similarity index 98% rename from pkg-version/src/versions/chocolatey.rs rename to aer_version/src/versions/chocolatey.rs index 8d9e5c7..e2a4702 100644 --- a/pkg-version/src/versions/chocolatey.rs +++ b/aer_version/src/versions/chocolatey.rs @@ -1,6 +1,9 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project +#![cfg(feature = "chocolatey")] +#![cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] + use std::fmt::Display; use semver::Identifier; @@ -248,6 +251,7 @@ impl From for SemVersion { } #[cfg(feature = "serialize")] +#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))] impl Serialize for ChocoVersion { fn serialize(&self, serialize: S) -> Result where @@ -259,6 +263,7 @@ impl Serialize for ChocoVersion { } #[cfg(feature = "serialize")] +#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))] impl<'de> Deserialize<'de> for ChocoVersion { fn deserialize(deserializer: D) -> Result where diff --git a/pkg_web/Cargo.toml b/aer_web/Cargo.toml similarity index 87% rename from pkg_web/Cargo.toml rename to aer_web/Cargo.toml index a198c32..c4343b3 100644 --- a/pkg_web/Cargo.toml +++ b/aer_web/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pkg_web" +name = "aer_web" version = "0.1.0" authors = ["AdmiringWorm "] edition = "2018" @@ -7,7 +7,7 @@ edition = "2018" [dependencies] lazy_static = "1.4.0" log = "0.4.14" -pkg-version = { path = "../pkg-version" } +aer_version = { path = "../aer_version" } regex = "1.4.5" select = "0.5.0" diff --git a/pkg_web/src/elements.rs b/aer_web/src/elements.rs similarity index 89% rename from pkg_web/src/elements.rs rename to aer_web/src/elements.rs index 4100c23..019111a 100644 --- a/pkg_web/src/elements.rs +++ b/aer_web/src/elements.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; use std::fmt::Display; -use pkg_version::Versions; +use aer_version::Versions; use reqwest::Url; /// Defines what type (MIME or extension) the current link @@ -62,8 +62,23 @@ impl Display for LinkType { } } +impl LinkType { + /// Returns the available link types as a static array + pub fn variants() -> &'static [LinkType] { + static VARIANTS: &[LinkType] = &[ + LinkType::Binary, + LinkType::Css, + LinkType::Html, + LinkType::Json, + LinkType::Text, + LinkType::Unknown, + ]; + VARIANTS + } +} + /// Stores information that are know about the current link. -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct LinkElement { /// The full link of this element. /// In most cases this is expected to include the domain, and will only be diff --git a/pkg_web/src/errors.rs b/aer_web/src/errors.rs similarity index 88% rename from pkg_web/src/errors.rs rename to aer_web/src/errors.rs index e1f005e..aed6455 100644 --- a/pkg_web/src/errors.rs +++ b/aer_web/src/errors.rs @@ -29,3 +29,9 @@ impl Display for WebError { } } } + +impl From for WebError { + fn from(err: std::io::Error) -> Self { + WebError::IoError(err) + } +} diff --git a/pkg_web/src/lib.rs b/aer_web/src/lib.rs similarity index 97% rename from pkg_web/src/lib.rs rename to aer_web/src/lib.rs index a2989ed..117f3a0 100644 --- a/pkg_web/src/lib.rs +++ b/aer_web/src/lib.rs @@ -11,7 +11,7 @@ //! returned! //! //! ``` -//! use pkg_web::*; +//! use aer_web::*; //! //! let request = WebRequest::create(); //! let response = request diff --git a/pkg_web/src/request.rs b/aer_web/src/request.rs similarity index 99% rename from pkg_web/src/request.rs rename to aer_web/src/request.rs index bd6cbcd..3a50d17 100644 --- a/pkg_web/src/request.rs +++ b/aer_web/src/request.rs @@ -36,7 +36,7 @@ lazy_static! { /// /// Aquiring html response. /// ``` -/// use pkg_web::WebRequest; +/// use aer_web::WebRequest; /// /// let request = WebRequest::create(); /// let response = request diff --git a/pkg_web/src/response.rs b/aer_web/src/response.rs similarity index 100% rename from pkg_web/src/response.rs rename to aer_web/src/response.rs diff --git a/pkg_web/src/response/binary.rs b/aer_web/src/response/binary.rs similarity index 100% rename from pkg_web/src/response/binary.rs rename to aer_web/src/response/binary.rs diff --git a/pkg_web/src/response/html.rs b/aer_web/src/response/html.rs similarity index 99% rename from pkg_web/src/response/html.rs rename to aer_web/src/response/html.rs index 36201d4..4c4ed0b 100644 --- a/pkg_web/src/response/html.rs +++ b/aer_web/src/response/html.rs @@ -1,7 +1,7 @@ // Copyright (c) 2021 Kim J. Nordmo and WormieCorp. // Licensed under the MIT license. See LICENSE.txt file in the project -use pkg_version::Versions; +use aer_version::Versions; use regex::{Captures, Regex}; use reqwest::blocking::Response; use reqwest::{header, Url}; diff --git a/pkg-data/Cargo.toml b/pkg-data/Cargo.toml deleted file mode 100644 index d3245e7..0000000 --- a/pkg-data/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "pkg-data" -version = "0.1.0" -authors = ["AdmiringWorm "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -pkg-license = { path = "../pkg-license" } -pkg-version = { path = "../pkg-version", features = ["serialize"] } -serde = "1.0.125" -url = { version = "2.2.1", features = ["serde"] } -whoami = "1.1.1" - -[dev-dependencies] -rstest = "0.7.0" diff --git a/pkg-data/src/prelude.rs b/pkg-data/src/prelude.rs deleted file mode 100644 index 26ce92f..0000000 --- a/pkg-data/src/prelude.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2021 Kim J. Nordmo and WormieCorp. -// Licensed under the MIT license. See LICENSE.txt file in the project - -pub use pkg_license::LicenseType; - -pub use crate::metadata::chocolatey::ChocolateyMetadata; -pub use crate::metadata::{Description, PackageMetadata}; -pub use crate::updater::chocolatey::{ - ChocolateyParseUrl, ChocolateyUpdaterData, ChocolateyUpdaterType, -}; -pub use crate::updater::PackageUpdateData; -pub use crate::PackageData; diff --git a/pkg-license/Cargo.toml b/pkg-license/Cargo.toml deleted file mode 100644 index 4801743..0000000 --- a/pkg-license/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "pkg-license" -version = "0.1.0" -authors = ["AdmiringWorm "] -edition = "2018" -publish = false - -[dependencies] -license = "1.1.10" -serde = "1.0.125" -url = { version = "2.2.1", features = ["serde"] } - -[dev-dependencies] -rstest = "0.7.0" diff --git a/pkg-upd/Cargo.toml b/pkg-upd/Cargo.toml deleted file mode 100644 index 7a663e0..0000000 --- a/pkg-upd/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "pkg-upd" -version = "0.1.0" -authors = ["AdmiringWorm "] -edition = "2018" -description = "Utility to update packages" -readme = "README.md" -repository = "https://github.com/WormieCorp/pkg-upd" -license = "MIT" -keywords = ["cli", "package", "packaging", "updater"] -categories = ["command-line-utilities"] -publish = false - -[features] -default = ["powershell", "toml_data"] -toml_data = ["toml", "serde"] -powershell = ["serde_json", "serde"] - -[dependencies] -chrono = "0.4.19" -fern = { version = "0.6.0", features = ["date-based"] } -human-panic = { git = "https://github.com/WormieCorp/human-panic", branch = "additional-info" } -human_bytes = "0.2.1" -lazy_static = "1.4.0" -log = "0.4.14" -md-5 = "0.9.1" -pkg-data = { path = "../pkg-data" } -pkg-license = { path = "../pkg-license" } -pkg-version = { path = "../pkg-version", features = ["serialize"] } -pkg_web = { path = "../pkg_web" } -serde = { version = "1.0.125", optional = true } -serde_json = { version = "1.0.64", optional = true } -sha-1 = "0.9.4" -sha2 = "0.9.3" -structopt = "0.3.21" -toml = { version = "0.5.8", optional = true } -url = { version = "2.2.1", features = ["serde"] } -whoami = "1.1.0" -yansi = "0.5.0" - -[dev-dependencies] -assert_cmd = "1.0.3" -predicates = "1.0.7" -rstest = "0.7.0" -rusty-hook = "0.11.2" diff --git a/pkg-upd/src/bin/pkg-web.rs b/pkg-upd/src/bin/pkg-web.rs deleted file mode 100644 index 463b8a6..0000000 --- a/pkg-upd/src/bin/pkg-web.rs +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright (c) 2021 Kim J. Nordmo and WormieCorp. -// Licensed under the MIT license. See LICENSE.txt file in the project -#![windows_subsystem = "console"] - -use std::fmt::Display; -use std::io::Write; -use std::path::{Path, PathBuf}; -use std::str::FromStr; - -use human_bytes::human_bytes; -use human_panic::setup_panic; -use log::{error, info}; -use md5::Md5; -use pkg_upd::{log_data, logging}; -use pkg_web::errors::WebError; -use pkg_web::response::ResponseType; -use pkg_web::{LinkElement, WebRequest, WebResponse}; -use sha1::Sha1; -use sha2::{Digest, Sha256, Sha512}; -use structopt::StructOpt; -use url::Url; -use yansi::Color; - -log_data! {"pkg-web"} - -#[derive(StructOpt)] -enum ChecksumType { - Md5, - Sha1, - Sha256, - Sha512, -} - -impl FromStr for ChecksumType { - type Err = &'static str; - - fn from_str(val: &str) -> std::result::Result::Err> { - let val: &str = &val.trim().to_lowercase(); - - match val { - "md5" => Ok(ChecksumType::Md5), - "sha1" => Ok(ChecksumType::Sha1), - "sha256" | "sha2" => Ok(ChecksumType::Sha256), - "sha512" => Ok(ChecksumType::Sha512), - _ => Err("The value is not a supported checksum type"), - } - } -} - -impl Display for ChecksumType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - match self { - ChecksumType::Md5 => f.write_str("MD5"), - ChecksumType::Sha1 => f.write_str("SHA1"), - ChecksumType::Sha256 => f.write_str("SHA256"), - ChecksumType::Sha512 => f.write_str("SHA512"), - } - } -} - -#[derive(StructOpt)] -#[structopt(after_help = "EXAMPLES: - Parsing all urls - `parse https://github.com/codecove/codecov-exe/releases/latest`Parsing \ - on matching urls - `parse https://github.com/codecove/codecov-exe/releases/latest \ - --regex '.*\\.zip$'` - Parsing while extracting version - `parse https://github.com/codecov/codecov-exe/releases/latest \ - --regex '/(?P[\\d\\.]+)/.*\\.zip$'`")] -struct ParseArguments { - /// The url to parse use to test parsing of the program. - url: Url, - - /// The regular expression to use when parsing the specified `url`. - #[structopt(long, short)] - regex: Option, -} - -#[derive(StructOpt)] -struct DownloadArguments { - /// The url of the binary file to download. - url: Url, - - /// The etag that will be matched against the download folder. If matched no - /// file will be downloaded. - #[structopt(long)] - etag: Option, - - /// The last modified date as a string, this is usually the date that - /// previously was returned from the server. - #[structopt(long)] - last_modified: Option, - - /// The checksum to compare the downloaded file with - #[structopt(long)] - checksum: Option, - - /// The type of the checksum to compare and/or output to console. - #[structopt(long, default_value = "sha256", possible_values = &["md5", "sha1", "sha256", "sha512"])] - checksum_type: ChecksumType, - - /// The work directory that downloads should be downloaded to. [default: - /// temp dir] - #[structopt(long, parse(from_os_str))] - work_dir: Option, -} - -#[derive(StructOpt)] -enum Commands { - /// Allows testing a single html parse command using the specified url, and - /// optionally an regex. This will output the links found on the website. - Parse(ParseArguments), - /// Allows downloading a single binary file, by default this command will - /// use `$TEMP` as the work directory and will remove the downloaded file - /// afterwards. - Download(DownloadArguments), -} - -/// Allows testing specific urls by either checking which links will be found -/// on an HTML page, or if a file can be downloaded. -#[derive(StructOpt)] -#[structopt(author = "AdmiringWorm ", name = "pkg-web")] -struct Arguments { - #[structopt(subcommand)] - cmd: Commands, - - #[structopt(flatten)] - log: LogData, - - #[structopt(long, global = true, env = "NO_COLOR")] - no_color: bool, -} - -fn main() { - setup_panic!(); - if cfg!(windows) && !yansi::Paint::enable_windows_ascii() { - yansi::Paint::disable(); - } - let args = Arguments::from_args(); - if args.no_color { - yansi::Paint::disable(); - } - logging::setup_logging(&args.log).expect("Unable to configure logging of the application!"); - - let request = WebRequest::create(); - match args.cmd { - Commands::Parse(args) => parse_website_lone(&request, args.url, args.regex), - Commands::Download(args) => download_file_once(&request, args), - } -} - -fn parse_website_lone(request: &WebRequest, url: Url, regex: Option) { - match parse_website(request, url, regex) { - Ok((parent, elements)) => { - info!( - "Successfully parsed '{}'", - Color::Magenta.paint(parent.link) - ); - info!( - "Found {} links on the webpage!", - Color::Cyan.paint(elements.len()) - ); - - for link in elements { - info!( - "{} (type: {}, title: {}, version: {}, text: {})", - Color::Magenta.paint(link.link), - Color::Cyan.paint(link.link_type), - Color::Cyan.paint(if link.title.is_empty() { - "None".into() - } else { - link.title - }), - Color::Cyan.paint(if let Some(version) = link.version { - format!("{}", version) - } else { - "None".into() - }), - Color::Cyan.paint(link.text) - ); - } - } - Err(err) => { - error!("Unable to parse the requested website!"); - error!("Error message: {}", err); - std::process::exit(1); - } - } -} - -fn parse_website( - request: &WebRequest, - url: Url, - regex: Option, -) -> Result<(LinkElement, Vec), WebError> { - let response = request.get_html_response(url.as_str())?; - - if let Some(ref regex) = regex { - response.read(Some(regex)) - } else { - response.read(None) - } -} - -fn download_file_once(request: &WebRequest, args: DownloadArguments) { - let temp_dir = if let Some(work_dir) = &args.work_dir { - work_dir.clone() - } else { - std::env::temp_dir() - }; - - if let Err(err) = download_file(request, args, &temp_dir) { - error!("Unable to download the file. Error: {}", err); - std::process::exit(1); - } -} - -fn download_file( - request: &WebRequest, - args: DownloadArguments, - work_dir: &Path, -) -> Result<(), Box> { - let etag = if let Some(ref etag) = args.etag { - Some(etag.as_str()) - } else { - None - }; - let last_modified = if let Some(ref last_modified) = args.last_modified { - Some(last_modified.as_str()) - } else { - None - }; - let response = request.get_binary_response(args.url.as_str(), etag, last_modified)?; - - match response { - ResponseType::Updated(_) => { - info!("No update is necessary!"); - } - ResponseType::New(mut response, _) => { - response.set_work_dir(work_dir); - let (etag, last_modified) = get_info(&response); - let result = response.read(None)?; - info!("The following information was given by the server:"); - if !etag.is_empty() { - print_line("ETag", etag.trim_matches('"')); - } else { - print_line("ETag", "None"); - } - if !last_modified.is_empty() { - print_line("Last Modified", last_modified); - } else { - print_line("Last Modified", "None"); - } - match generate_checksum(&result, &args.checksum_type) { - Ok(checksum) => { - if let Some(original_checksum) = args.checksum { - if original_checksum.to_lowercase() == checksum { - info!("Originial Checksum matches checksum of downloaded file"); - } else { - error!("Original Checksum do not match checksum of downloaded file!"); - } - } - print_line("Checksum", checksum); - print_line("Checksum Type", args.checksum_type); - } - Err(err) => error!("Unable to generate checksum: {}", err), - } - - info!( - "The resulting file is {} long!", - Color::Cyan.paint(human_bytes(result.metadata()?.len() as f64)) - ); - - let _ = std::fs::remove_file(result); - } - } - Ok(()) -} - -fn generate_checksum(path: &Path, checksum_type: &ChecksumType) -> Result { - match checksum_type { - ChecksumType::Md5 => generate_checksum_from_hasher(Md5::new(), path), - ChecksumType::Sha1 => generate_checksum_from_hasher(Sha1::new(), path), - ChecksumType::Sha256 => generate_checksum_from_hasher(Sha256::new(), path), - ChecksumType::Sha512 => generate_checksum_from_hasher(Sha512::new(), path), - } -} - -fn generate_checksum_from_hasher( - mut hasher: T, - path: &Path, -) -> Result -where - ::OutputSize: std::ops::Add, - <::OutputSize as std::ops::Add>::Output: - sha2::digest::generic_array::ArrayLength, -{ - let mut f = std::fs::File::open(path)?; - std::io::copy(&mut f, &mut hasher)?; - let result = hasher.finalize(); - - Ok(format!("{:x}", result)) -} - -fn print_line(name: T, value: V) { - let name_style = Color::Magenta.style(); - let value_style = Color::Cyan.style(); - - info!( - "{:>16} : {}", - name_style.paint(name), - value_style.paint(value) - ); -} - -fn get_info(response: &T) -> (String, String) { - let headers = response.get_headers(); - let mut etag = String::new(); - let mut last_modified = String::new(); - - if let Some(etag_val) = headers.get("etag") { - etag = etag_val.to_string(); - } - if let Some(modified_val) = headers.get("last-modified") { - last_modified = modified_val.to_string(); - } - - (etag, last_modified) -} diff --git a/pkg-upd/src/main.rs b/pkg-upd/src/main.rs deleted file mode 100644 index f1df90b..0000000 --- a/pkg-upd/src/main.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2021 Kim J. Nordmo and WormieCorp. -// Licensed under the MIT license. See LICENSE.txt file in the project -#![windows_subsystem = "console"] - -extern crate pkg_upd; - -use std::path::PathBuf; - -use human_panic::setup_panic; -use log::{error, info}; -use pkg_upd::{log_data, logging}; -use structopt::StructOpt; - -log_data! {env!("CARGO_PKG_NAME")} - -#[derive(StructOpt)] -#[structopt(author = "AdmiringWorm ")] -struct Arguments { - /// The files containing the necessary data (metadata+updater data) that - /// should be used during the run. - #[structopt(name = "PKG_FILE", required = true, parse(from_os_str))] - package_files: Vec, - - #[structopt(flatten)] - log: LogData, -} - -fn main() { - setup_panic!(); - if cfg!(windows) && !yansi::Paint::enable_windows_ascii() { - yansi::Paint::disable(); - } - - let arguments = Arguments::from_args(); - logging::setup_logging(&arguments.log) - .expect("Unable to configure logging of the application!"); - - for file in arguments.package_files { - let data = match pkg_upd::parsers::read_file(&file) { - Ok(data) => data, - Err(error) => { - error!("Error reading package file: {}", error); - continue; - } - }; - - info!( - "Should continue with updating package: {}", - data.metadata().id() - ); - - // let file = data.updater().chocolatey().unwrap(). - - // if let Some(ref file) = file { - // let cwd = std::env::current_dir().unwrap(); - - // let data = run_script(&cwd, PathBuf::from_str(file).unwrap(), - // &mut data); info!("{:?}", data); - // } - } - - info!("Hello, world!"); -} diff --git a/pkg-upd/test-data/license-short.toml b/pkg-upd/test-data/license-short.toml deleted file mode 100644 index 17733f8..0000000 --- a/pkg-upd/test-data/license-short.toml +++ /dev/null @@ -1,5 +0,0 @@ -[metadata] -id = "test-package" -project_url = "https://example-repo.org" -summary = "" -license = { url = "https://github.com/WormieCorp/pkg-upd/LICENSE.txt", expression = "MIT" }