diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd86f8a..cb7dd17 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,10 @@ -on: ['push', 'pull_request'] +on: ["push", "pull_request"] jobs: compile_exec: name: Compile to executables runs-on: ${{ matrix.config.os }} - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/rewrite' # just to see if it works strategy: matrix: config: @@ -13,14 +13,23 @@ jobs: - os: windows-latest steps: - uses: actions/checkout@v3 - - name: Setup python - uses: actions/setup-python@v3 + - name: Setup Rust + uses: actions-rs/toolchain@v1 with: - python-version: '3.9' - - name: Install dependencies for ${{ matrix.config.os }} - run: | - python -m pip install --upgrade pip wheel setuptools - pip install -r requirements.txt - pip install pyinstaller - - name: Build with pyinstaller for ${{ matrix.config.os }} - run: pyinstaller popcorn.spec \ No newline at end of file + toolchain: stable + + - name: Install dependencies on Ubuntu + if: startsWith(matrix.config.os, 'ubuntu') + run: sudo apt-get update && sudo apt-get install cmake pkg-config libgtk-3-dev + + - name: Update toolchain + run: rustup update + + - name: Cache dependencies + uses: Swatinem/rust-cache@v2 + + - name: Build with Cargo + uses: actions-rs/cargo@v1 + with: + command: build + args: --release diff --git a/.github/workflows/compile_and_release.yml b/.github/workflows/compile_and_release.yml index d745fc7..1fcbf7c 100644 --- a/.github/workflows/compile_and_release.yml +++ b/.github/workflows/compile_and_release.yml @@ -54,17 +54,50 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # [!] we need to checkout with tags and commit history - - name: Setup python - uses: actions/setup-python@v3 + + - name: Setup Rust + uses: actions-rs/toolchain@v1 with: - python-version: '3.9' - - name: Install dependencies for ${{ matrix.config.os }} - run: | - python -m pip install --upgrade pip wheel setuptools - pip install -r requirements.txt - pip install pyinstaller - - name: Build with pyinstaller for ${{ matrix.config.os }} - run: pyinstaller popcorn.spec + toolchain: stable + + - name: Install dependencies on Ubuntu + if: startsWith(matrix.config.os, 'ubuntu') + run: sudo apt-get update && sudo apt-get install cmake pkg-config libgtk-3-dev + + - name: Build with Cargo for ${{ matrix.config.os }} + if: startsWith(matrix.config.os, 'ubuntu') + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: x86_64-unknown-linux-gnu + + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target ${{with.target}} + + if: startsWith(matrix.config.os, 'macos') + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: aarch64-apple-darwin # will probably have to change this around to accomodate for M1 & M2 + + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target ${{with.target}} + + if: startsWith(matrix.config.os, 'windows') # not sure if a target is needed here + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: x86_64-pc-windows-msvc + + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target ${{with.target}} + - name: Copy files to be compressed run: | cp README.md ./dist diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d7ed1c9..2eb96bd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,13 +5,17 @@ jobs: runs-on: ubuntu-latest name: CI workflow steps: - - name: checkout source repo - uses: actions/checkout@v2 + - name: checkout source repo + uses: actions/checkout@v2 - - name: linting - uses: programmingwithalex/pylinters@v1.4.2 - with: - python-root: '.' - flake8-flags: '--max-line-length 120' - mypy-flags: '--ignore-missing-imports --install-types --non-interactive' - fail-on-isort: true \ No newline at end of file + - name: setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + + - name: Run cargo clean + uses: actions-rs/cargo@v1 + with: + command: clean diff --git a/.gitignore b/.gitignore index 49dc793..02aa44c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,22 @@ # editors .idea .vscode +.fleet # python **/__pycache__ venv # pyinstaller -build +/build dist # internal .popcorn # my list -**/TODO \ No newline at end of file +**/TODO + +# Added by cargo + +/target diff --git a/.kernelrc b/.kernelrc index d8f9719..ba599aa 100644 --- a/.kernelrc +++ b/.kernelrc @@ -1,11 +1,15 @@ { - "kernel_name": "popcorn", - "kernel_type": "unpacked", - "unpacked_husk": "python @local/popcorn.py @args", - "dev_cmd": "popcorn dev", - "seed_cmd": "cp -r * @dest", - "advanced": { - "os": ["mac", "windows", "linux"], - "dev_node": "-dev" - } -} \ No newline at end of file + "kernel_name": "popcorn", + "kernel_type": "unpacked", + "unpacked_husk": "python @local/popcorn.py @args", + "dev_cmd": "popcorn dev", + "seed_cmd": "cp -r * @dest", + "advanced": { + "os": [ + "mac", + "windows", + "linux" + ], + "dev_node": "-dev" + } +} diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0f4782f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2262 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "anyhow" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" + +[[package]] +name = "async-compression" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "bzip2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi 0.3.9", +] + +[[package]] +name = "clap" +version = "3.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "console" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "terminal_size", + "unicode-width", + "winapi 0.3.9", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "cxx" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "filetime" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "windows-sys 0.42.0", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fsevent" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" +dependencies = [ + "bitflags", + "fsevent-sys", +] + +[[package]] +name = "fsevent-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" +dependencies = [ + "libc", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generator" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc184cace1cea8335047a471cc1da80f18acf8a76f3bab2028d499e328948ec7" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi 0.3.9", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if 1.0.0", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.36.1", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio 0.6.23", + "slab", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-glue" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" +dependencies = [ + "libc", + "log", + "ndk", + "ndk-context", + "ndk-macro", + "ndk-sys", + "once_cell", + "parking_lot", +] + +[[package]] +name = "ndk-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ndk-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "net2" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "notify" +version = "4.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" +dependencies = [ + "bitflags", + "filetime", + "fsevent", + "fsevent-sys", + "inotify", + "libc", + "mio 0.6.23", + "mio-extras", + "walkdir", + "winapi 0.3.9", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "openssl" +version = "0.10.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.36.1", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "popcorn" +version = "2.0.0" +dependencies = [ + "anyhow", + "async-compression", + "chrono", + "clap", + "console", + "dirs", + "flate2", + "log", + "md5", + "notify", + "progress_bar", + "regex", + "reqwest", + "serde", + "serde_derive", + "serde_json", + "state", + "tar", + "tokio", + "webbrowser", + "zip-extract", +] + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "progress_bar" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8e8843722ce55deb442e7d6476a97c081b8485ea2807b031d160f592f99037" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" +dependencies = [ + "cty", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "reqwest" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys 0.36.1", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "security-framework" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "state" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" +dependencies = [ + "loom", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.9", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "textwrap" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" + +[[package]] +name = "thiserror" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio 0.8.4", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +dependencies = [ + "ansi_term", + "matchers", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi 0.3.9", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d62aa75495ab67cdc273d0b95cc76bcedfea2ba28338a4cf9b4137949dfac5" +dependencies = [ + "jni", + "ndk-glue", + "objc", + "raw-window-handle", + "url", + "web-sys", + "widestring", + "winapi 0.3.9", +] + +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec" +dependencies = [ + "windows_aarch64_msvc 0.32.0", + "windows_i686_gnu 0.32.0", + "windows_i686_msvc 0.32.0", + "windows_x86_64_gnu 0.32.0", + "windows_x86_64_msvc 0.32.0", +] + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + +[[package]] +name = "zip" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +dependencies = [ + "byteorder", + "bzip2", + "crc32fast", + "flate2", + "thiserror", + "time", +] + +[[package]] +name = "zip-extract" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c5cc0309f6e81ab96c2b43d5e935025f8732c886690be8f78f68e06bad1d274" +dependencies = [ + "log", + "thiserror", + "zip", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5289298 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "popcorn" +version = "2.0.0" +edition = "2021" +license = "MIT" +authors = ["Matt "] +description = "An all-system package manager." +repository = "https://github.com/punctuations/popcorn" +#build = "build.rs" +# +#[package.metadata.winres] +#ProductName = "Popcorn" +#OriginalFilename = "popcorn.exe" +#LegalCopyright = "© 2022 Matt" +#FileDescription = "An all-system package manager." + +[[bin]] +name = "popcorn" + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +regex = "1" +md5 = "0.7.0" +flate2 = "1.0" +tar = "0.4.38" +log = "0.4.17" +chrono = "0.4" +dirs = "4.0.0" +state = "0.5.3" +notify = "4.0.0" +serde = "1.0.117" +anyhow = "1.0.59" +console = "0.15.1" +webbrowser = "0.8.0" +zip-extract = "0.1.1" +serde_json = "1.0.59" +progress_bar = "1.0.3" +serde_derive = "1.0.117" +reqwest = { version = "0.11", features = ["json"] } +tokio = { version = "1.20.1", features = ["full"] } +clap = { version = "3.2.16", features = ["derive"] } +async-compression = { version = "0.3.14", features = ["tokio", "gzip"] } \ No newline at end of file diff --git a/README.md b/README.md index e63ab7e..e4852b6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ # 🍿 Popcorn + ### Say goodbye to complicated installation instructions and build steps. + #### So easy you can sit back and enjoy some popcorn. + ![](demo.gif) ## Contents + - [Requirements](#requirements) - [Terminology](#terminology) - [Installation](#install) @@ -14,6 +18,7 @@ - [Config](#config) ### Requirements + - python 3.7+ - pip - bash @@ -35,20 +40,26 @@ ### Install **Linux/Mac:** + ```bash curl -sSL cmdf.at/popcorn | bash ``` + or with homebrew + ```bash brew tap punctuations/tap brew install punctuations/tap/popcorn ``` **Windows:** + ```powershell iwr -useb raw.githubusercontent.com/punctuations/popcorn/main/install.ps1 | iex ``` + or with Chocolately + ```powershell coming soon... ``` @@ -56,6 +67,7 @@ coming soon... #### [Back to contents](#contents) ### Updating + If installed with the installation script just re-run the script, and it will update the program. #### [Back to contents](#contents) @@ -69,9 +81,11 @@ popcorn aims to help with the development process of not only clis using framewo ### Commands ##### Build: + The build command will compile the code in the current directory to the production directory, making a cli tool compiled and stored as a kernel. Ex. + ``` > popcorn build ``` @@ -79,16 +93,19 @@ Ex. output flag: Using the output flag will fix any conflicts between kernel names by storing it a different parent directory. + ``` > popcorn build -o raspberry ``` ##### Dev: + The dev command will launch a development environment which will listen to the specified directory for any changes and hot reload the development version of the command, to test out the command up-to-date and without having to manually compile every time. The development command will be accessible through the `{kernel_name}{dev_node}`, for example: `popcorn-dev` Ex. + ``` > popcorn dev ``` @@ -96,14 +113,17 @@ Ex. listen flag: The listen flag specifies the directory that it is listening to for changes. + ``` > popcorn dev -l ./foo/bar ``` ##### Install: + The install command is to install your kernel to the path and initialize the environment for it, this command should be run if the kernel has not been installed yet. Ex. + ``` > popcorn install ``` @@ -111,14 +131,17 @@ Ex. dev flag: the dev flag will install the **development** version of the command, the path will be saved to either shellrc file or .profile (on windows it will go straight to path) + ``` > popcorn install -d ``` ##### Remove: + The remove command is to remove already installed kernels. Ex. + ``` > popcorn remove foo ``` @@ -126,14 +149,17 @@ Ex. dev flag: The dev flag is used to indicate the requested kernel is a development kernel, which will be removed from a separate place from the production kernels. + ``` > popcorn remove foo -d ``` ##### Init: + The init command initializes the config file for popcorn. Ex. + ``` > popcorn init ``` @@ -149,15 +175,17 @@ The force flag will replace any existing config file with the base/skeleton one. packed flag: The packed flag generates a different skeleton to that of the unpacked or default one and will only have necessary values. -```` -> popcorn init --packed -```` +``` +> popcorn init --packed +``` ##### Help: + The help command provides help for the commands, providing more detail about them. Ex. + ``` > popcorn help dev ``` @@ -165,30 +193,35 @@ Ex. #### [Back to contents](#contents) ### Kernel Examples + Some examples of how to construct a kernel can be seen at the following: + - **[Tablespoon](https://github.com/punctuations/tablespoon)** created by [Matt](https://dont-ping.me) #### [Back to contents](#contents) ### Config + The config (.kernelrc) is used for all kernels, and is required, you can use the init command which will generate a skeleton config to edit for the user. ##### Skeleton config file: + ```json { - "kernel_name": "popcorn", - "kernel_type": "unpacked", - "unpacked_husk": "python @local/popcorn.py @args", - "dev_cmd": "popcorn dev", - "seed_cmd": "cp -r * @dest", - "advanced": { - "os": ["mac", "windows", "linux"], - "dev_node": "-dev" - } + "kernel_name": "popcorn", + "kernel_type": "unpacked", + "unpacked_husk": "python @local/popcorn.py @args", + "dev_cmd": "popcorn dev", + "seed_cmd": "cp -r * @dest", + "advanced": { + "os": ["mac", "windows", "linux"], + "dev_node": "-dev" + } } ``` ##### Values: + - `kernel_name`: The name that will be used to access the program. - `kernel_type`: Two possible values 'packed' or 'unpacked' which determine the file structure of the project. - `unpacked`: Requires entrypoint that bounces to another file. (directory) @@ -199,8 +232,9 @@ The config (.kernelrc) is used for all kernels, and is required, you can use the - `advanced/dev_node`: The naming scheme applied for the development command. ##### Variables: + - `@local`: used for the local directory for the program. - `@dest`: used to specify the destination of the kernel (changes per command) - `@args`: used only in `unpacked_husk`, will be all arguments passed to command. -#### [Back to contents](#contents) \ No newline at end of file +#### [Back to contents](#contents) diff --git a/_utils/styled_print.py b/_utils/styled_print.py deleted file mode 100644 index 2b72850..0000000 --- a/_utils/styled_print.py +++ /dev/null @@ -1,22 +0,0 @@ -def error(text): - print(f" error -  {text}") - - -def success(text): - print(f" success -  {text}") - - -def warning(text): - print(f" warning -  {text}") - - -def info(text): - print(f" info -  {text}") - - -def event(text): - print(f" event -  {text}") - - -def debug(text): - print(f" debug -  {text}") diff --git a/blame.json b/blame.json deleted file mode 100644 index 35912c0..0000000 --- a/blame.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "🍿 Popcorn", - "description": "Say good-bye to complicated installation instructions.", - "version": "1.0.7", - "source": "https://github.com/punctuations/popcorn" -} \ No newline at end of file diff --git a/cmds/__init__.py b/cmds/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/cmds/build.py b/cmds/build.py deleted file mode 100644 index 0a85c43..0000000 --- a/cmds/build.py +++ /dev/null @@ -1,205 +0,0 @@ -import json -import os -import re -import shutil -import stat -import sys -import threading - -from _utils import styled_print - -config = {} -kernel_name = "" -kernel_type = "" - - -def initialize_globals(): - global kernel_name - global kernel_type - global config - try: - f = open("./.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Please create a .kernelrc file.") - sys.exit(0) - - kernel_name = config["kernel_name"] - kernel_type = config["kernel_type"] - - -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" - -rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" -dotprofile = f"{os.environ['HOME']}{os.sep}.profile" - - -def build_thread(output): - path_init = False - - try: - shutil.rmtree(f"{PROD_DIR}{os.sep}{output}") - os.mkdir(PROD_DIR) - if kernel_type.lower() == "unpacked": - os.mkdir(f"{PROD_DIR}{os.sep}{output}") - except FileExistsError: - pass - except FileNotFoundError: - pass - except KeyError: - styled_print.error("Please include a kernel_name in the config.") - sys.exit(0) - - try: - # run seed_cmd - if kernel_type.lower() == "unpacked": - try: - os.mkdir(f"{PROD_DIR}{os.sep}{output}") - except FileExistsError: - pass - seed_cmd = re.compile(re.escape("@dest"), re.IGNORECASE) \ - .sub(f"{PROD_DIR}{os.sep}{output}", config["seed_cmd"]) - else: - seed_cmd = re.compile(re.escape("@dest"), re.IGNORECASE).sub(f"{PROD_DIR}", config["seed_cmd"]) - exit_status = os.WEXITSTATUS(os.system(seed_cmd)) - - # create unpacked kernel with unpacked stem. - if kernel_type.lower() == "unpacked": - try: - arg_stem = re.compile(re.escape("@args"), re.IGNORECASE).sub("\"$@\"", config["unpacked_husk"]) - unpacked_husk = re.compile(re.escape("@local"), re.IGNORECASE) \ - .sub(f"{PROD_DIR}{os.sep}{output[:-1] if output.endswith('/') else output}", arg_stem) - with open(f"{PROD_DIR}{os.sep}{output}{kernel_name}", "w") as kernel: - kernel.write(f"#!/bin/bash\n{unpacked_husk}") - kernel.close() - except KeyError: - styled_print.error("Please include the unpacked_husk in the config.") - kernel.close() - sys.exit(0) - - # change permissions - try: - os.chmod(os.path.join(PROD_DIR, output if kernel_type.lower() == "packed" else f"{output}{kernel_name}"), - stat.S_IRWXO | stat.S_IRWXU | stat.S_IRWXG) - except FileNotFoundError: - styled_print.error("Unable to amend permission of file; file not found.") - sys.exit(0) - - if exit_status == 0: - styled_print.success("Compiled successfully!") - - except KeyError: - styled_print.error("Please enter in a seed_cmd") - sys.exit(0) - - if os.name != "nt": - try: - buttered = open(f'{PROD_DIR}{os.sep}butter.sh', 'r') - lines = buttered.readlines() - buttered.close() - - if kernel_type == "packed": - if f"export PATH=$PATH{os.pathsep}{PROD_DIR}\n" not in lines: - with open(f'{PROD_DIR}{os.sep}butter.sh', "a") as builds: - builds.write(f"export PATH=$PATH{os.pathsep}{PROD_DIR}\n") - builds.close() - else: - if f"export PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n" not in lines: - with open(f'{PROD_DIR}{os.sep}butter.sh', "a") as builds: - builds.write(f"export PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n") - builds.close() - except FileNotFoundError: - with open(f'{PROD_DIR}{os.sep}butter.sh', "w") as butter: - if kernel_type == "packed": - butter.write(f"#!/bin/bash\nexport PATH=$PATH{os.pathsep}{PROD_DIR}\n") - else: - butter.write(f"#!/bin/bash\nexport PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n") - butter.close() - else: - if PROD_DIR not in os.environ["PATH"] and kernel_type.lower() == "packed": - path_init = True - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{PROD_DIR}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{PROD_DIR}'") - styled_print.success("Added to path") - - if f"{PROD_DIR}{os.sep}{output}" not in os.environ["PATH"] and kernel_type.lower() == "unpacked": - path_init = True - os.system( - f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{PROD_DIR}{output}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{PROD_DIR}{output}'") - styled_print.success("Added to path") - - if os.name != "nt": - if os.path.exists(rcfile): - shellrc = open(rcfile, "r") - rclines = shellrc.read() - shellrc.close() - if f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n" not in rclines: - with open(rcfile, "w") as init: - init.write(f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n") - init.write(rclines) - init.close() - else: - profile = open(dotprofile, "r") - proflines = profile.read() - profile.close() - if f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n" not in proflines: - with open(dotprofile, "w") as init: - init.write(f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n") - init.write(proflines) - init.close() - if path_init: - if os.name == 'nt': - os.system(". $profile") - else: - if os.path.exists(rcfile): - os.system(f"source {rcfile}") - else: - os.system(f"source {dotprofile}") - - styled_print.success(f"Successfully built {kernel_name}.") - - -# popcorn build -# flags: -o, --output: change the output directory of the kernel -def build(args): - """ - Used to create production-level kernels. - - :param args: arguments passed to command - """ - initialize_globals() - - output = ["-o", "--output"] - # when file is placed here make sure to set path variable to file containing accessible target - # ex. (unpacked) export PATH = $PATH:$HOME/.local/bin/popcorn - # ex. (packed) export PATH = $PATH:$HOME/.local/bin - has_output_flag = [element for element in output if (element in args)] - output_index = args.index(has_output_flag[0]) if len(has_output_flag) >= 1 else 0 - - if not os.path.exists(PROD_DIR): - os.mkdir(PROD_DIR) - - if has_output_flag: - try: - output = args[output_index + 1] - except IndexError: - styled_print.error("Please specify an output name.") - sys.exit(0) - else: - try: - output = kernel_name - except KeyError: - styled_print.error("Please include a kernel_name in the config.") - sys.exit(0) - - try: - if kernel_type.lower() == "unpacked": - output = output + os.sep - except KeyError: - styled_print.error("Please include the kernel_type in the config.") - sys.exit(0) - - thread = threading.Thread(target=build_thread, args=(output,)) - thread.start() diff --git a/cmds/dev.py b/cmds/dev.py deleted file mode 100644 index 94fe0b4..0000000 --- a/cmds/dev.py +++ /dev/null @@ -1,244 +0,0 @@ -import json -import os -import re -import shutil -import stat -import subprocess -import sys -import threading -import time -from datetime import datetime, timedelta - -from watchdog.events import FileSystemEventHandler -from watchdog.observers import Observer - -from _utils import styled_print - -listen = ["-l", "--listen"] - -DEV_DIR = f"{os.path.realpath(os.getcwd())}{os.sep}.popcorn" -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" - -last_event = {"message": False} -event_delta = timedelta(seconds=int(2)) - -config = {} -kernel_name = "" -kernel_type = "" - - -def initialize_globals(): - global config - global kernel_name - global kernel_type - - try: - f = open("./.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Please create a .kernelrc file.") - sys.exit(0) - - kernel_name = config["kernel_name"] - kernel_type = config["kernel_type"] - - -rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" -profile = f"{os.environ['HOME']}{os.sep}.profile" - - -def thread_compile(): - time.sleep(2) - - event_time_remaining = last_event["time"] + event_delta - datetime.now() - if event_time_remaining.days <= -1: - if last_event["message"]: - styled_print.info("Updates to config detected, to see up-to-date changes re-run this command.") - styled_print.event("Received compile event.") - - try: - try: - shutil.rmtree(f"{DEV_DIR}{os.sep}") - os.mkdir(DEV_DIR) - except FileExistsError: - pass - except FileNotFoundError: - pass - - seed_cmd = re.compile(re.escape("@dest"), re.IGNORECASE).sub(f"{DEV_DIR}", config["seed_cmd"]) - exit_status = os.WEXITSTATUS(os.system(seed_cmd)) - - # create unpacked kernel. - try: - if kernel_type.lower() == "unpacked": - try: - arg_stem = re.compile(re.escape("@args"), re.IGNORECASE).sub("\"$@\"", config["unpacked_husk"]) - unpacked_husk = re.compile(re.escape("@local"), re.IGNORECASE).sub(DEV_DIR, arg_stem) - with open(f"{DEV_DIR}{os.sep}{kernel_name}", "w") as kernel: - kernel.write(f"#!/bin/bash\n{unpacked_husk}") - kernel.close() - except KeyError: - styled_print.error("Please include the unpacked_husk in the config.") - kernel.close() - sys.exit(0) - - except KeyError: - styled_print.error("Please include the kernel_type in the config.") - sys.exit(0) - - # edit permissions to all - try: - os.chmod(os.path.join(DEV_DIR, kernel_name), stat.S_IRWXO | stat.S_IRWXU | stat.S_IRWXG) - except FileNotFoundError: - styled_print.error("Unable to amend permission of file; file not found.") - sys.exit(0) - - if exit_status == 0: - styled_print.success("Compiled successfully.") - else: - styled_print.error("Failed to compile, please check seed_cmd.") - except KeyError: - styled_print.error("Please enter in a seed_cmd") - sys.exit(0) - - # rename kernel to dev branch. - try: - dev_node = config['advanced']['dev_node'] - - os.rename(f"{DEV_DIR}{os.sep}{kernel_name}", - f"{DEV_DIR}{os.sep}{kernel_name}{config['advanced']['dev_node'] if dev_node else '-dev'}") - except FileNotFoundError: - styled_print.error(f"Please have a file named {kernel_name} as entry point.") - sys.exit(0) - - -class Handler(FileSystemEventHandler): - - @staticmethod - def on_any_event(event): - if event.is_directory: - return None - - try: - p = subprocess.check_output(["git", "check-ignore", event.src_path]) - - if type(p) is not bytes: - ignored = False - else: - ignored = True - except subprocess.CalledProcessError: - ignored = False - - if event.src_path.split("/")[-1] == ".kernelrc": - last_event["message"] = True - - if not ignored: - last_event["time"] = datetime.now() - thread = threading.Thread(target=thread_compile) - thread.start() - - -# popcorn dev -# flags: -l, --listen: set the dir/file that is being watched for changes -def dev(args): - """ - Used to create development kernels. - - :param args: arguments passed to command - """ - initialize_globals() - - if DEV_DIR not in os.environ["PATH"]: - styled_print.warning("Development kernel not installed.\n\t\tplease try: popcorn install --dev") - sys.exit(0) - - has_listen_flag = [element for element in listen if (element in args)] - listen_index = args.index(has_listen_flag[0]) if len(has_listen_flag) >= 1 else 0 - - if not kernel_name: - styled_print.error("Please enter a kernel_name in config.") - sys.exit(0) - - # initialize development env - shutil.rmtree(DEV_DIR, ignore_errors=True) - os.mkdir(DEV_DIR) - - try: - seed_cmd = re.compile(re.escape("@dest"), re.IGNORECASE).sub(DEV_DIR, config["seed_cmd"]) - exit_status = os.WEXITSTATUS(os.system(seed_cmd)) - - # create packed kernel. - try: - if kernel_type.lower() == "unpacked": - try: - arg_stem = re.compile(re.escape("@args"), re.IGNORECASE).sub("\"$@\"", config["unpacked_husk"]) - unpacked_husk = re.compile(re.escape("@local"), re.IGNORECASE).sub(DEV_DIR, arg_stem) - with open(f"{DEV_DIR}{os.sep}{kernel_name}", "w") as kernel: - kernel.write(f"#!/bin/bash\n{unpacked_husk}") - kernel.close() - except KeyError: - styled_print.error("Please include the unpacked_husk in the config.") - kernel.close() - sys.exit(0) - - except KeyError: - styled_print.error("Please include the kernel_type in the config.") - sys.exit(0) - - # edit permissions to all - try: - os.chmod(os.path.join(DEV_DIR, kernel_name), stat.S_IRWXO | stat.S_IRWXU | stat.S_IRWXG) - except FileNotFoundError: - styled_print.error("Unable to amend permission of file; file not found.") - sys.exit(0) - - if exit_status == 0: - styled_print.success("Compiled successfully.") - else: - styled_print.error("Failed to compile, please check seed_cmd.") - - except KeyError: - styled_print.error("Please enter in a seed_cmd") - sys.exit(0) - - # rename kernel to dev branch. - try: - dev_node = config['advanced']['dev_node'] - os.rename(f"{DEV_DIR}{os.sep}{kernel_name}", - f"{DEV_DIR}{os.sep}{kernel_name}{config['advanced']['dev_node'] if dev_node else '-dev'}") - except FileNotFoundError: - styled_print.error(f"Please have a file named {kernel_name} as entry point.") - sys.exit(0) - - if has_listen_flag: - try: - print(f"Listening to {args[listen_index + 1]}") - watch_file = args[listen_index + 1] - except IndexError: - styled_print.error("Please specify a file or directory.") - sys.exit(0) - else: - print("Listening to .") - watch_file = "." - - if not os.path.exists(watch_file): - styled_print.error("File or directory does not exist.") - sys.exit(0) - - # Initialize logging event handler - event_handler = Handler() - - # Initialize Observer - observer = Observer() - observer.schedule(event_handler, watch_file, recursive=True) - - # Start the observer - observer.start() - try: - while True: - # Set the thread sleep time - time.sleep(1) - except KeyboardInterrupt: - observer.stop() - observer.join() diff --git a/cmds/help.py b/cmds/help.py deleted file mode 100644 index 6b61885..0000000 --- a/cmds/help.py +++ /dev/null @@ -1,73 +0,0 @@ -import json -import os -import sys - -from _utils import styled_print - - -# popcorn help -def help(args): - """ - Used to get more information about commands. - - :param args: arguments passed to command - """ - - ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - - def resource_path(relative_path): - """ Get absolute path to resource, works for dev and for PyInstaller """ - try: - # PyInstaller creates a temp folder and stores path in _MEIPASS - base_path = sys._MEIPASS - except Exception: - base_path = ROOT_DIR - - return os.path.join(base_path, relative_path) - - f = open(resource_path("blame.json")) - blame = json.load(f) - f.close() - - if len(args) < 1: - command_list = os.listdir(path=os.path.dirname(os.path.realpath(__file__))) - - commands = [] - definitions = [] - - for file in command_list: - if not file.startswith("_"): - commands.append(file.split(".")[0]) - - for file in commands: - with open(f"{os.path.dirname(os.path.realpath(__file__))}{os.sep}{file}.py", "r") as f: - contents = f.read() - definitions.append(contents.split("\"\"\"")[1].split(":")[0]) - f.close() - - print(f"\n  {blame['name']}  v{blame['version']}") - print(f"\n 💻 {blame['source']}") - print("\n ### Description \n") - print(f" {blame['description']}") - print("\n ### Commands \n") - for i, command in enumerate(commands): - print(f" •  {command} > {' '.join(definitions[i].split())}") - else: - command_file = f"{os.path.dirname(os.path.realpath(__file__))}{os.sep}{args[0]}.py" - if os.path.exists(command_file): - command = open(command_file, "r") - contents = command.read() - command.close() - command_desc = contents.split('\"\"\"\n')[1].split("\n")[0] - - print(f"\n  {blame['name']}  v{blame['version']}") - print(f"\n 💻 {blame['source']}") - print("\n ### Usage \n") - print(f" {command_desc}") - print("\n ### Flags \n") - for flags in contents.split("# flags: ")[1].split("\n")[0].split(";"): - flag = flags.split(":")[0] - flag_desc = flags.split(":")[1].split("\n")[0] - print(f" •  {flag} > {flag_desc}") - else: - styled_print.error("Command not found") diff --git a/cmds/init.py b/cmds/init.py deleted file mode 100644 index bf9fa8f..0000000 --- a/cmds/init.py +++ /dev/null @@ -1,38 +0,0 @@ -import os - -from _utils import styled_print - - -def create_config(has_packed): - with open('.kernelrc', "w") as config: - if has_packed: - config.write(f'{{\n"kernel_name": "{os.getcwd().split(os.sep)[-1]}",\n"kernel_type": "packed",' - f'\n"dev_cmd": "popcorn dev",\n"seed_cmd": "go build -o @dest",\n"advanced": {{' - f'\n"dev_node": "-dev"\n}}\n}}') - else: - config.write(f'{{\n\t"kernel_name": "{os.getcwd().split(os.sep)[-1]}",\n\t"kernel_type": "unpacked",' - f'\n\t"unpacked_husk": "python @local/popcorn.py @args",\n\t"dev_cmd": "popcorn dev",' - f'\n\t"seed_cmd": "cp -r * @dest",\n\t"advanced": {{\n\t\t"dev_node": "-dev"\n\t}}\n}}') - config.close() - - -# popcorn init -# flags: --force: replaces any existing config with default; -p, --packed: generates the packed config -def init(args): - """ - Used to initialize a .kernelrc configuration file. - - :param args: arguments passed to command - """ - force = ['--force'] - packed = ['-p', '--packed'] - - has_force_flag = [element for element in force if (element in args)] - has_packed_flag = [element for element in packed if (element in args)] - - if not os.path.exists('.kernelrc'): - create_config(has_packed_flag) - elif has_force_flag: - create_config(has_packed_flag) - else: - styled_print.info(".kernelrc already exists -- No changes made.") diff --git a/cmds/install.py b/cmds/install.py deleted file mode 100644 index c6db5b7..0000000 --- a/cmds/install.py +++ /dev/null @@ -1,109 +0,0 @@ -import json -import os -import sys - -from _utils import styled_print - -DEV_DIR = f"{os.path.realpath(os.getcwd())}{os.sep}.popcorn" -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" - -config = {} - - -def initialize_globals(): - global config - - try: - f = open("./.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Please create a .kernelrc file.") - sys.exit(0) - - -# popcorn install -# flags: -d, --dev: install development kernels -def install(args): - """ - Used to initialize environment for kernels. - - :param args: arguments passed to command. - """ - initialize_globals() - - dev = ["-d", "--dev"] - has_dev_flag = [element for element in dev if (element in args)] - - kernel_name = config["kernel_name"] - - if not kernel_name: - styled_print.error("Please include a kernel_name in config.") - sys.exit(0) - - if not os.path.exists(f'{PROD_DIR}{os.sep}butter.sh'): - if not os.path.exists(PROD_DIR): - os.mkdir(PROD_DIR) - - with open(f'{PROD_DIR}{os.sep}butter.sh', "w") as butter: - butter.write("#!/bin/bash\n") - butter.close() - - if has_dev_flag: - if DEV_DIR not in os.environ["PATH"]: - if os.name == 'nt': - # change path - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{DEV_DIR}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{DEV_DIR}'") - styled_print.success("Please run popcorn source to apply changes.") - else: - # edit .shellrc or .profile file - rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" - - if os.path.exists(rcfile): - shellrc = open(rcfile, "a") - shellrc.write(f"\nexport PATH=$PATH{os.pathsep}{DEV_DIR}\n") - shellrc.close() - else: - profile = open(f"{os.environ['HOME']}{os.sep}.profile", "a") - profile.write(f"\nexport PATH=$PATH{os.pathsep}{DEV_DIR}\n") - profile.close() - - if os.name == 'nt': - os.system(". $profile") - else: - rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" - dotprofile = f"{os.environ['HOME']}{os.sep}.profile" - - if os.path.exists(rcfile): - os.system(f"source {rcfile}") - styled_print.success("Please restart the terminal to apply changes.") - else: - os.system(f"source {dotprofile}") - styled_print.success("Please restart the terminal to apply changes.") - else: - styled_print.info("kernel already installed.") - else: - rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" - dotprofile = f"{os.environ['HOME']}{os.sep}.profile" - - if os.path.exists(rcfile): - shellrc = open(rcfile, "r+") - if f". $HOME{os.sep}.kernels{os.sep}butter.sh\n" not in shellrc.readlines(): - rclines = "\n".join(shellrc.readlines()) - shellrc.write(f"\n. $HOME{os.sep}.kernels{os.sep}butter.sh\n{rclines}") - shellrc.close() - else: - styled_print.info("kernel already installed.") - shellrc.close() - else: - profile = open(dotprofile, "r+") - if f". $HOME{os.sep}.kernels{os.sep}butter.sh\n" not in profile.readlines(): - proflines = "\n".join(profile.readlines()) - profile.seek(0) - profile.write(f"\n. $HOME{os.sep}.kernels{os.sep}butter.sh\n{proflines}") - profile.truncate() - profile.close() - else: - styled_print.info("kernel already installed.") - profile.close() diff --git a/cmds/issue.py b/cmds/issue.py deleted file mode 100644 index 69ba665..0000000 --- a/cmds/issue.py +++ /dev/null @@ -1,11 +0,0 @@ -import webbrowser - - -# popcorn issue -def issue(args): - """ - Submit issues about popcorn. - - :param args: arguments passed to command - """ - webbrowser.open("https://github.com/punctuations/popcorn/issues/new") diff --git a/cmds/remove.py b/cmds/remove.py deleted file mode 100644 index 601504c..0000000 --- a/cmds/remove.py +++ /dev/null @@ -1,100 +0,0 @@ -import os -import shutil -import sys - -from _utils import styled_print - -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" - - -# popcorn remove -# flags: -d, --dev: remove development kernels -def remove(args): - """ - Used to remove both production kernels and development kernels. - - :param args: arguments passed to command - """ - dev = ["-d", "--dev"] - has_dev_flag = [element for element in dev if (element in args)] - - if len(args) >= 1: - path = os.environ["PATH"] - - styled_print.info(f"Removing {args[0]}...") - if has_dev_flag: - if f"{args[0]}{os.sep}.popcorn" in path: - if os.name == 'nt': - split_paths = path.split(os.pathsep) - removed_kernel = f'{args[0]}{os.sep}.popcorn' - fixed_path = f'{os.pathsep}'.join([i for i in split_paths if removed_kernel not in i]) - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '{fixed_path}', 'User')") - os.system(f"$env:PATH = '{fixed_path}'") - else: - rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" - profile = f"{os.environ['HOME']}{os.sep}.profile" - - if os.path.exists(rcfile): - # remove form .shellrc - with open(rcfile, 'r+') as f: - data = ''.join([i for i in f if f"{args[0]}{os.sep}.popcorn" not in i]) - f.seek(0) - f.write(data) - f.truncate() - f.close() - else: - # remove from .profile - with open(profile, 'r+') as f: - data = ''.join([i for i in f if f"{args[0]}{os.sep}.popcorn" not in i]) - f.seek(0) - f.write(data) - f.truncate() - f.close() - styled_print.success(f"Removed {args[0]}") - styled_print.info("Please restart the terminal to apply changes.") - else: - styled_print.error("kernel not installed.") - else: - if f"{PROD_DIR}{os.sep}{args[0]}" in path or os.path.exists(f"{PROD_DIR}{os.sep}{args[0]}"): - if os.name == 'nt': - split_paths = path.split(os.pathsep) - removed_kernel = f'{PROD_DIR}{os.sep}{args[0]}' - fixed_path = f'{os.pathsep}'.join([i for i in split_paths if removed_kernel not in i]) - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '{fixed_path}', 'User')") - os.system(f"$env:PATH = '{fixed_path}'") - - try: - shutil.rmtree(f"{PROD_DIR}{os.sep}{args[0]}") - except FileNotFoundError: - styled_print.error("kernel not found.") - sys.exit(0) - except NotADirectoryError: - os.remove(f"{PROD_DIR}{os.sep}{args[0]}") - - else: - if args[0] == "butter.sh": - styled_print.error("Not a kernel.") - sys.exit(0) - - buttered = f'{PROD_DIR}{os.sep}butter.sh' - with open(buttered, 'r+') as butter: - data = ''.join([i for i in butter if args[0] not in i]) - butter.seek(0) - butter.write(data) - butter.truncate() - butter.close() - - try: - shutil.rmtree(f"{PROD_DIR}{os.sep}{args[0]}") - except FileNotFoundError: - styled_print.error("kernel not found.") - sys.exit(0) - except NotADirectoryError: - os.remove(f"{PROD_DIR}{os.sep}{args[0]}") - - styled_print.success(f"Removed {args[0]}") - styled_print.info("Please restart the terminal to apply changes.") - else: - styled_print.error("kernel not installed.") - else: - styled_print.error("Please specify a kernel to remove.") diff --git a/cmds/theatre.py b/cmds/theatre.py deleted file mode 100644 index 48acffb..0000000 --- a/cmds/theatre.py +++ /dev/null @@ -1,289 +0,0 @@ -import json -import os -import shutil -import stat -import subprocess -import sys -import tarfile -import threading -from zipfile import ZipFile - -import requests - -from _utils import styled_print - -PROD_DIR = f"{os.path.expanduser('~')}{os.sep}.kernels" -TMP_DIR = f"{os.sep}tmp" if os.name != 'nt' else os.environ['TEMP'] -TMP_DIR += f"{os.sep}popcorn-kernel{os.sep}" - -rcfile = f"{os.environ['HOME']}{os.sep}.{os.environ['SHELL'].split('/')[-1]}rc" -dotprofile = f"{os.environ['HOME']}{os.sep}.profile" - - -def build_thread(output, location, config): - os.chdir(f'{location}') - kernel_type = config['kernel_type'] - kernel_name = config['kernel_name'] - path_init = False - - try: - shutil.rmtree(f"{PROD_DIR}{os.sep}{output}") - os.mkdir(PROD_DIR) - if kernel_type.lower() == "unpacked": - os.mkdir(f"{PROD_DIR}{os.sep}{output}") - except FileExistsError: - pass - except FileNotFoundError: - pass - - try: - if kernel_type.lower() == 'unpacked': - kernel_files = [f for f in os.listdir()] - - for file in kernel_files: - shutil.move(file, f"{PROD_DIR}{os.sep}{output}{file}") - else: - # kernel is packed, only transfer the entry point. - os.rename(kernel_name, f"{PROD_DIR}{os.sep}{output}") - - # change permissions - try: - os.chmod(os.path.join(PROD_DIR, output if kernel_type.lower() == "packed" else f"{output}{kernel_name}"), - stat.S_IRWXO | stat.S_IRWXU | stat.S_IRWXG) - except FileNotFoundError: - styled_print.error("Unable to amend permission of file; file not found.") - sys.exit(0) - - styled_print.success("Compiled successfully!") - - except KeyError: - styled_print.error("Theatre does not contain a seed_cmd") - sys.exit(0) - - # add to buttered - if os.name != "nt": - try: - buttered = open(f'{PROD_DIR}{os.sep}butter.sh', 'r') - lines = buttered.readlines() - buttered.close() - - if kernel_type == "packed": - if f"export PATH=$PATH{os.pathsep}{PROD_DIR}\n" not in lines: - with open(f'{PROD_DIR}{os.sep}butter.sh', "a") as builds: - builds.write(f"export PATH=$PATH{os.pathsep}{PROD_DIR}\n") - builds.close() - else: - if f"export PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n" not in lines: - with open(f'{PROD_DIR}{os.sep}butter.sh', "a") as builds: - builds.write(f"export PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n") - builds.close() - except FileNotFoundError: - with open(f'{PROD_DIR}{os.sep}butter.sh', "w") as butter: - if kernel_type == "packed": - butter.write(f"#!/bin/bash\nexport PATH=$PATH{os.pathsep}{PROD_DIR}\n") - else: - butter.write(f"#!/bin/bash\nexport PATH=$PATH{os.pathsep}{PROD_DIR}{os.sep}{output}\n") - butter.close() - else: - # is windows, add to path - if PROD_DIR not in os.environ["PATH"] and kernel_type.lower() == "packed": - path_init = True - os.system(f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{PROD_DIR}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{PROD_DIR}'") - styled_print.success("Added to path") - - if f"{PROD_DIR}{os.sep}{output}" not in os.environ["PATH"] and kernel_type.lower() == "unpacked": - path_init = True - os.system( - f"[Environment]::SetEnvironmentVariable('PATH', '$env:PATH{os.pathsep}{PROD_DIR}{output}', 'User')") - os.system(f"$env:PATH += '{os.pathsep}{PROD_DIR}{output}'") - styled_print.success("Added to path") - - # add buttered execution to rcfile/dotprofile - if os.name != "nt": - if os.path.exists(rcfile): - shellrc = open(rcfile, "r") - rclines = shellrc.read() - shellrc.close() - if f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n" not in rclines: - with open(rcfile, "w") as init: - init.write(f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n") - init.write(rclines) - init.close() - else: - profile = open(dotprofile, "r") - proflines = profile.read() - profile.close() - if f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n" not in proflines: - with open(dotprofile, "w") as init: - init.write(f". \"$HOME{os.sep}.kernels{os.sep}butter.sh\"\n") - init.write(proflines) - init.close() - if path_init: - if os.name == 'nt': - os.system(". $profile") - else: - if os.path.exists(rcfile): - os.system(f"source {rcfile}") - else: - os.system(f"source {dotprofile}") - - -def build(config, is_unpacked, location): - if not os.path.exists(PROD_DIR): - os.mkdir(PROD_DIR) - - try: - output = config["kernel_name"] - except KeyError: - styled_print.error("Theatre has no kernel_name.") - sys.exit(0) - - if is_unpacked: - output = output + os.sep - thread = threading.Thread(target=build_thread, args=(output, location, config,)) - thread.start() - else: - thread = threading.Thread(target=build_thread, args=(output, location, config,)) - thread.start() - - -# popcorn theatre -# flags: -u, --unpacked: get a unpacked remote kernel; --url: specify the download URL -def theatre(args): - """ - Used to get remote kernels. - - :param args: arguments passed to command - """ - - unpacked = ["-u", "--unpacked"] - has_unpacked_flag = [element for element in unpacked if (element in args)] - url = ["--url"] - has_url_flag = [element for element in url if (element in args)] - url_index = args.index(has_url_flag[0]) if len(has_url_flag) >= 1 else 0 - - has_target_ver = False - ver = None - - if len(args) >= 1: - if not has_url_flag and len(args[0].split("/")) != 2: - styled_print.error("Please follow the scheme of user/repo") - sys.exit(0) - - if not has_url_flag and "@" in args[0]: - ver = args[0].split("@")[1] - args[0] = args[0].split("@")[0] - has_target_ver = True - - if has_url_flag: - hash_name = args[url_index + 1].encode('utf-8').hex() - if os.name == 'nt': - file_ext = '.zip' - os.system(f"Invoke-WebRequest {args[url_index + 1]} -Out {TMP_DIR}url-{hash_name}{file_ext}") - else: - file_ext = '.tar.gz' - os.system(f"curl --silent {args[url_index + 1]} -L --output {TMP_DIR}url-{hash_name}{file_ext} " - f"--create-dirs") - - if os.name == 'nt': - with ZipFile(f'{TMP_DIR}url-{hash_name}{file_ext}', "r") as zip_obj: - zip_obj.extractall(f'{TMP_DIR}url-{hash_name}') - else: - with tarfile.open(f'{TMP_DIR}url-{hash_name}{file_ext}', "r") as tar_obj: - tar_obj.extractall(f'{TMP_DIR}url-{hash_name}') - - # load .kernelrc - try: - f = open(f"{TMP_DIR}url-{hash_name}{os.sep}.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Theatre does not contain .kernelrc") - shutil.rmtree(f"{TMP_DIR}url-{hash_name}") - sys.exit(0) - - # check to see if it is compatible with current os - os_type = 'windows' if os.name == 'nt' else 'mac' if sys.platform == 'darwin' else 'linux' - if os_type not in config["os"]: - styled_print.warning("Unsupported os type.") - shutil.rmtree(f"{TMP_DIR}url-{hash_name}") - sys.exit(0) - - # find .kernelrc and make sure the kernel names aren't a conflict. - if os.path.exists(f"{PROD_DIR}{os.sep}{args.splt('/')[1]}"): - styled_print.warning("A kernel with that name already exists.") - # delete the conflicted kernel here. - shutil.rmtree(f"{TMP_DIR}url-{hash_name}") - sys.exit(0) - - build(config, has_unpacked_flag, location=f"url-{hash_name}") - styled_print.success(f"Successfully added {config['kernel_name']}") - - else: - # from github. - file_name = '@'.join(args[0].split('/')) - os_type = 'windows' if os.name == 'nt' else 'mac' if sys.platform == 'darwin' else 'linux' - if not has_target_ver: - release = requests.get(f'https://api.github.com/repos/{args[0]}/tags').json() - ver = release[0]['name'] - - if os.name == 'nt': - file_ext = '.zip' - try: - subprocess.run(f"Invoke-WebRequest https://github.com/{args[0]}/releases/download/{ver}/kernel" - f"-{os_type}{file_ext} -Out {TMP_DIR}{file_name}{file_ext}", shell=True, check=True) - except (OSError, subprocess.SubprocessError, subprocess.CalledProcessError) as e: - print(e) - styled_print.error("An error occurred while getting the theatre's kernel.") - sys.exit(0) - - else: - file_ext = '.tar.gz' - try: - subprocess.run(f"curl --silent https://github.com/{args[0]}/releases/download/{ver}/kernel" - f"-{os_type}{file_ext} -L --output {TMP_DIR}{file_name}{file_ext} --create-dirs", - shell=True, check=True) - except (OSError, subprocess.SubprocessError, subprocess.CalledProcessError) as e: - print(e) - styled_print.error("An error occurred while getting the theatre's kernel.") - sys.exit(0) - - if os.name == 'nt': - with ZipFile(f'{TMP_DIR}{file_name}{file_ext}', "r") as zip_obj: - zip_obj.extractall(f'{TMP_DIR}{file_name}') - else: - with tarfile.open(f'{TMP_DIR}{file_name}{file_ext}', "r") as tar_obj: - tar_obj.extractall(f'{TMP_DIR}{file_name}') - - # load .kernelrc - try: - f = open(f"{TMP_DIR}{file_name}{os.sep}.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Theatre does not contain .kernelrc") - shutil.rmtree(f"{TMP_DIR}{file_name}") - shutil.rmtree(f"{TMP_DIR}{file_name}{file_ext}") - sys.exit(0) - - # check to see if it is compatible with current os - if config["advanced"]["os"] and os_type not in config["advanced"]["os"]: - styled_print.warning("Unsupported os type.") - shutil.rmtree(f"{TMP_DIR}{file_name}") - shutil.rmtree(f"{TMP_DIR}{file_name}{file_ext}") - sys.exit(0) - - # find .kernelrc and make sure the kernel names aren't a conflict. - if os.path.exists(f"{PROD_DIR}{os.sep}{config['kernel_name']}"): - styled_print.warning("A kernel with that name already exists.") - # delete the conflicted kernel here. - shutil.rmtree(f"{TMP_DIR}{file_name}") - shutil.rmtree(f"{TMP_DIR}{file_name}{file_ext}") - sys.exit(0) - - build(config, has_unpacked_flag, location=f"{TMP_DIR}{file_name}") - styled_print.success(f"Successfully added {config['kernel_name']}") - - else: - styled_print.error("Please specify a Theatre.") diff --git a/popcorn.py b/popcorn.py deleted file mode 100644 index 42566d9..0000000 --- a/popcorn.py +++ /dev/null @@ -1,132 +0,0 @@ -import json -import os -import sys - -from _utils import styled_print - -alias = ["-a", "--alias"] -dev = ["-d", "--dev"] -debug = ["--debug"] -help_flag = ["--help", "-h"] -version_flag = ["--version", "-v"] - -ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) - - -def resource_path(relative_path): - """ Get absolute path to resource, works for dev and for PyInstaller """ - try: - # PyInstaller creates a temp folder and stores path in _MEIPASS - base_path = sys._MEIPASS - except Exception: - base_path = ROOT_DIR - - return os.path.join(base_path, relative_path) - - -def load_cmds(has_debug_flag: list[str]): - styled_print.info("begin load") if has_debug_flag else None - cmds = os.listdir(resource_path("cmds")) # get all files in /cmds - cmds = [s for s in cmds if s[0] not in "_."] # remove hidden files - cmds = [s[:-3] for s in cmds] # remove '.py' - cmds_dict = {} # init cmds_dict - - styled_print.info("before command load loop") if has_debug_flag else None - for s in cmds: - cmds_dict[s] = __import__("cmds" + "." + s, fromlist=["*"]) - styled_print.info(f"imported {s}") if has_debug_flag else None - - # sort by key - styled_print.info("return cmds") if has_debug_flag else None - return cmds_dict - - -def popcorn(command): - has_alias_flag = [element for element in alias if (element in command)] - # get positional index where it is in list - has_dev_flag = [element for element in dev if (element in command)] - dev_index = command.index(has_dev_flag[0]) if len(has_dev_flag) >= 1 else 0 - has_debug_flag = [element for element in debug if (element in command)] - debug_index = command.index(has_debug_flag[0]) if len(has_debug_flag) >= 1 else 0 - has_help_flag = [element for element in help_flag if (element in command)] - help_index = command.index(has_help_flag[0]) if len(has_help_flag) >= 1 else 0 - has_version_flag = [element for element in version_flag if (element in command)] - - styled_print.info("before load") if has_debug_flag else None - cmds = load_cmds(has_debug_flag) - styled_print.info("after load") if has_debug_flag else None - - cmd_with_debug = 0 if not has_debug_flag or debug_index != 0 else debug_index + 1 - - styled_print.debug(f"ran with args {command}: {len(command)}") if has_debug_flag else None - - if has_alias_flag: - styled_print.debug("running alias command") if has_debug_flag else None - - print("function pop () { eval $(popcorn $@); }") - elif has_version_flag: - f = open(resource_path("blame.json")) - blame = json.load(f) - f.close() - styled_print.info(blame["version"]) - elif has_dev_flag and len(command) == 1: - styled_print.debug("running dev command from flag") if has_debug_flag else None - # * Run popcorn command based on .kernelrc options and pass in the path - - try: - f = open("./.kernelrc") - config = json.load(f) - f.close() - except FileNotFoundError: - styled_print.error("Please create a .kernelrc file.") - sys.exit(0) - - run_dev = getattr(cmds["dev"], "dev") - try: - dev_command = config["dev_cmd"] - os.system(dev_command) - except KeyError: - run_dev(command[dev_index + 1:] if len(command) >= 2 else ["-l", "."]) - elif has_help_flag: - styled_print.debug("running help command from flag") if has_debug_flag else None - - run_help = getattr(cmds["help"], "help") - run_help(command[help_index + 1:]) - elif command[cmd_with_debug] == 'theater': - # alias for theatre - styled_print.debug("running theatre command") if has_debug_flag else None - - run_install = getattr(cmds["theatre"], "theatre") - run_install(command[cmd_with_debug + 1:] if command[cmd_with_debug + 1:] != ['--debug'] else []) - elif command[cmd_with_debug] == 'uninstall': - # alias for theatre - styled_print.debug("running remove command") if has_debug_flag else None - - run_install = getattr(cmds["remove"], "remove") - run_install(command[cmd_with_debug + 1:] if command[cmd_with_debug + 1:] != ['--debug'] else []) - else: - # if argument is passed in - if len(command) >= 1: - # if command does not exist go to build command. - try: - run_command = getattr(cmds[command[cmd_with_debug]], command[cmd_with_debug]) - - styled_print.debug(f"running {command[cmd_with_debug]} command") if has_debug_flag else None - run_command(command[cmd_with_debug + 1:] if command[cmd_with_debug + 1:] != ['--debug'] else []) - except KeyError: - styled_print.debug("running build command as fallback") if has_debug_flag else None - - # command is: popcorn /path/to/files - run_build = getattr(cmds["build"], "build") - run_build(command) - else: - styled_print.debug("running install command from no args") if has_debug_flag else None - - # command is: popcorn - # * Run install command - run_install = getattr(cmds["install"], "install") - run_install([]) - - -if __name__ == '__main__': - popcorn(sys.argv[1:]) diff --git a/popcorn.spec b/popcorn.spec deleted file mode 100644 index 7e595ea..0000000 --- a/popcorn.spec +++ /dev/null @@ -1,44 +0,0 @@ -# -*- mode: python ; coding: utf-8 -*- - - -block_cipher = None - - -a = Analysis( - ['popcorn.py'], - pathex=[], - binaries=[], - datas=[('cmds', './cmds'), ('blame.json', '.')], - hiddenimports=['watchdog.events', 'watchdog.observers', 'webbrowser', 'requests'], - hookspath=[], - hooksconfig={}, - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, - noarchive=False, -) -pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) - -exe = EXE( - pyz, - a.scripts, - a.binaries, - a.zipfiles, - a.datas, - [], - name='popcorn', - debug=False, - bootloader_ignore_signals=False, - strip=False, - upx=True, - upx_exclude=[], - runtime_tmpdir=None, - console=True, - disable_windowed_traceback=False, - argv_emulation=False, - target_arch=None, - codesign_identity=None, - entitlements_file=None, -) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 72e9393..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -watchdog==2.1.8 - -requests~=2.28.1 \ No newline at end of file diff --git a/src/commands/build/mod.rs b/src/commands/build/mod.rs new file mode 100644 index 0000000..a6ec3f7 --- /dev/null +++ b/src/commands/build/mod.rs @@ -0,0 +1,421 @@ +use anyhow::Result; +use clap::Parser; + +use std::env; +use std::fs::{self, File, OpenOptions}; +use std::io::{prelude::*, BufReader}; +use std::path::PathBuf; +use std::process::Command; +use std::thread; +use std::{fs::create_dir_all, fs::remove_dir_all, path::Path}; + +use crate::util::{get_config, set_permissions, Config, Print, DEV_DIR, PATHSEP, PROD_DIR, SEP}; + +use super::install::apply_changes; + +#[derive(Debug, Parser)] +#[clap(about = "Used to create production-level kernels.")] +pub struct Options { + #[clap( + short = 'o', + long = "output", + help = "Change the output directory of the kernel." + )] + output: Option, +} + +pub fn seed_cmd( + config_seed: String, + kernel_type: String, + output: PathBuf, + is_dev: bool, +) -> Result<(), &'static str> { + let seed_cmd: String; + + let dest = if is_dev { DEV_DIR() } else { PROD_DIR() }; + + // initiliaze seed_cmd + if kernel_type == "unpacked" { + // make unpacked output dir and run seed cmd + if !is_dev { + create_dir_all(output.clone()); + } + + seed_cmd = config_seed.to_lowercase().replace( + "@dest", + &output.clone().into_os_string().into_string().unwrap(), + ); + } else { + // put into prod_dir (is only one file & output is callable name) + seed_cmd = config_seed.to_lowercase().replace("@dest", &dest); + } + + // run new seed cmd + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + if let Ok(mut child) = Command::new(shell.split("/").last().unwrap()) + .arg("-c") + .arg(seed_cmd.clone()) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + return Err("An error occured while executing the seed_cmd"); + } + return Ok(()); + } else { + return Err("An error occured while running the seed_cmd"); + } +} + +fn ensure_butter_in_path() -> Result<(), String> { + // edit .shellrc or .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; + + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let profile_path = home.join(".profile"); + + if rc_path.exists() { + // edit rc_path + let mut file = match OpenOptions::new().read(true).append(true).open(rc_path) { + Ok(file) => file, + Err(_e) => return Err(".profile not found".to_string()), + }; + let mut reader = BufReader::new(file.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if !contents.contains(&format!(". $HOME{SEP}.kernels{SEP}butter.sh\n", SEP = SEP)) { + file.write(format!(". $HOME{SEP}.kernels{SEP}butter.sh\n", SEP = SEP).as_bytes()) + .unwrap(); + + match apply_changes() { + Ok(()) => return Ok(()), + Err(err) => { + return Err(err); + } + } + } + } else { + // edit .profile + let mut file = match OpenOptions::new() + .read(true) + .append(true) + .open(profile_path) + { + Ok(file) => file, + Err(_e) => return Err(".profile not found".to_string()), + }; + let mut reader = BufReader::new(file.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if !contents.contains(&format!(". $HOME{SEP}.kernels{SEP}butter.sh\n", SEP = SEP)) { + file.write(format!(". $HOME{SEP}.kernels{SEP}butter.sh\n", SEP = SEP).as_bytes()) + .unwrap(); + + match apply_changes() { + Ok(()) => return Ok(()), + Err(err) => { + return Err(err); + } + } + } + } + + Ok(()) +} + +fn update_path(kernel_type: String, output: String) -> Result<(), String> { + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + let updated_path = if kernel_type.to_lowercase() == "packed" { + PROD_DIR() + } else { + format!("{}{}{}", &PROD_DIR(), SEP, output) + }; + + if !PATH.contains(&updated_path) { + if cfg!(target_os = "windows") { + // add to the path + match Command::new("[Environment]::SetEnvironmentVariable('PATH',") + .args([ + format!("$env:PATH{}{},", PATHSEP, &updated_path), + "'User')".to_string(), + ]) + .output() + { + Ok(_) => (), + Err(e) => return Err(e.to_string()), + } + Command::new("$env:PATH") + .args(["+=", format!("'{}{}'", PATHSEP, &updated_path).as_str()]) + .output() + .unwrap(); + + match apply_changes() { + Ok(()) => return Ok(()), + Err(err) => { + return Err(err); + } + } + } else { + let dir = &PROD_DIR(); + let prod_directory = Path::new(dir); + let butter_file = prod_directory.join("butter.sh").clone(); + + if !butter_file.exists() { + let mut butter = OpenOptions::new() + .write(true) + .create(true) + .open(butter_file) + .unwrap(); + + write!( + butter, + "{}", + format!("#!/bin/bash\nexport PATH=$PATH{}{}", PATHSEP, &updated_path) + ) + .unwrap(); + } else { + let mut butter = OpenOptions::new() + .read(true) + .append(true) + .open(butter_file) + .unwrap(); + + let mut reader = BufReader::new(butter.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if !contents.contains(&format!("export PATH=$PATH{}{}", PATHSEP, &updated_path)) { + write!( + butter, + "{}", + format!("\nexport PATH=$PATH{}{}", PATHSEP, &updated_path) + ); + } + } + + match ensure_butter_in_path() { + Ok(()) => return Ok(()), + Err(err) => return Err(err), + } + } + } + + Ok(()) +} + +pub fn build_thread( + output: String, + config: Config, + external: bool, + external_dir: String, +) -> Result<(), ()> { + let output_path = Path::new(&PROD_DIR()).join(output.clone()); + let output_dir = if config.kernel_type.to_lowercase() == "unpacked" { + output_path.clone() + } else { + Path::new(&PROD_DIR()).to_path_buf() + }; + + // delete dir if already exists + if config.kernel_type.to_lowercase() == "unpacked" && output_path.exists() { + // prod dir already exists, only need to remove possible unpacked conflict. + match remove_dir_all(output_path) { + Ok(()) => (), + Err(_err) => { + Print::error("Unable to remove existing kernel."); + return Err(()); + } + } + } + if external { + // move files from /tmp/ to prod_dir + if config.kernel_type.to_lowercase() == "unpacked" { + let dir_contents = fs::read_dir(external_dir).unwrap(); + + for contents in dir_contents { + let file = contents.unwrap(); + + create_dir_all(output_dir.clone().as_os_str()); + + match fs::rename( + file.path(), + format!( + "{}{SEP}{}", + output_dir.clone().display(), + file.file_name().into_string().unwrap(), + SEP = SEP + ), + ) { + Ok(()) => (), + Err(_) => { + Print::error("Unable to move files."); + remove_dir_all(output_dir); + return Err(()); + } + } + } + } else { + match fs::rename( + format!( + "{}{SEP}{}", + external_dir, + config.kernel_name.clone(), + SEP = SEP + ), + format!( + "{}{SEP}{}", + output_dir.clone().display(), + config.kernel_name.clone(), + SEP = SEP + ), + ) { + Ok(()) => (), + Err(_) => { + Print::error("Unable to move entry file."); + remove_dir_all(output_dir); + return Err(()); + } + } + } + } else { + match seed_cmd( + config.seed_cmd.clone(), + config.kernel_type.clone(), + output_dir.clone(), + false, + ) { + Ok(()) => (), + Err(err) => { + Print::error(err); + return Err(()); + } + } + } + + // make unpacked kernel from unpacked husk + if config.kernel_type.to_lowercase() == "unpacked" { + let stem_cmd = config + .unpacked_husk + .expect("unpacked_husk not found for unpacked kernel.") + .to_lowercase() + .replace("@args", "$@") + .replace( + "@local", + if output.clone().ends_with("/") { + output.split_at(output.len() - 1).0 + } else { + &output + }, + ); + + // enter stem command to unpacked husk (make file and enter data) + let mut husk = match File::create(format!( + "{dir}{sep}{output}{husk_name}", + dir = &PROD_DIR(), + sep = &SEP, + output = output, + husk_name = config.kernel_name.clone() + )) { + Ok(file) => file, + Err(err) => { + Print::error(format!("Could not create husk file; {}", err).as_str()); + return Err(()); + } + }; + + match husk.write_all(format!("#!/bin/bash\n{}", stem_cmd).as_bytes()) { + Ok(()) => (), + Err(_err) => { + Print::error("An error occured while writing to husk file."); + return Err(()); + } + } + } + + // change permissions of file to be accessible by all + + let mut perms = fs::metadata(format!( + "{output_dir}{SEP}{husk_name}", + output_dir = output_dir.display(), + SEP = SEP, + husk_name = config.kernel_name.clone() + )) + .unwrap() + .permissions(); + set_permissions(&mut perms, 0o511); + + Print::info("Compiled successfully."); + + match update_path(config.kernel_type.clone(), output.clone()) { + Ok(()) => { + Print::success(format!("Successfully built {}", config.kernel_name).as_str()); + return Ok(()); + } + Err(err) => { + Print::error(err.as_str()); + return Err(()); + } + } +} + +pub async fn handle(options: Options) -> Result<()> { + let CONFIG: Config; + + match get_config() { + Ok(loaded_config) => CONFIG = loaded_config, + Err(_) => return Ok(()), // exit after error is printed + } + + let kernel_name = &CONFIG.kernel_name; + let kernel_type = &CONFIG.kernel_type; + let seed_cmd = &CONFIG.seed_cmd; + + if kernel_name == "" { + Print::error("Please include a kernel_name in the config file."); + return Ok(()); + } else if kernel_type == "" { + Print::error("Please include a kernel_type in the config file."); + return Ok(()); + } else if seed_cmd == "" { + Print::error("Please include a seed_cmd in the config file."); + return Ok(()); + } + + let dir = &PROD_DIR(); + let prod_path = Path::new(dir); + + if !prod_path.exists() { + create_dir_all(prod_path); + } + + let mut output = match options.output { + Some(path) => path, + None => kernel_name.to_string(), + }; + + if kernel_type.to_lowercase() == "unpacked" { + output = output + &SEP.to_string(); + } + + thread::spawn(|| build_thread(output, CONFIG, false, String::new())).join(); + + Ok(()) +} diff --git a/src/commands/dev/mod.rs b/src/commands/dev/mod.rs new file mode 100644 index 0000000..47da3a4 --- /dev/null +++ b/src/commands/dev/mod.rs @@ -0,0 +1,279 @@ +use anyhow::Result; +use clap::Parser; + +use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; +use regex::Regex; +use std::fs::{self, create_dir_all, remove_dir_all, File}; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::sync::mpsc::channel; +use std::time::Instant; +use std::{env, thread, time}; + +use crate::util::set_permissions; +use crate::util::{blame::BLAME, get_config, Config, Print, DEV_DIR, SEP}; + +use super::build::seed_cmd; + +#[derive(Debug, Parser)] +#[clap(about = "Run a kernel in the dev enviorment.")] +pub struct Options { + #[clap( + short = 'l', + long = "listen", + help = "Change the directory that listens for updates." + )] + listen: Option, +} + +fn dev_compile(config: Config) -> Result<(), ()> { + // ignore error purposefully as dir may not already exist. + remove_dir_all(DEV_DIR()); + create_dir_all(DEV_DIR()); + + // run seed_cmd + match seed_cmd( + config.seed_cmd.clone(), + config.kernel_type.clone(), + Path::new(&DEV_DIR()).to_path_buf(), + true, + ) { + Ok(()) => (), + Err(err) => { + Print::error(err); + return Err(()); + } + } + + // make unpacked kernel from unpacked husk + if config.kernel_type.to_lowercase() == "unpacked" { + let stem_cmd = config + .unpacked_husk + .expect("unpacked_husk not found for unpacked kernel.") + .to_lowercase() + .replace("@args", "$@") + .replace("@local", &DEV_DIR()); + + // enter stem command to unpacked husk (make file and enter data) + let mut husk = match File::create(format!( + "{dir}{sep}{husk_name}", + dir = &DEV_DIR(), + sep = &SEP, + husk_name = config.kernel_name.clone(), + )) { + Ok(file) => file, + Err(err) => { + Print::error(format!("Could not create husk file; {}", err).as_str()); + return Err(()); + } + }; + + match husk.write_all(format!("#!/bin/bash\n{}", stem_cmd).as_bytes()) { + Ok(()) => (), + Err(_err) => { + Print::error("An error occured while writing to husk file."); + return Err(()); + } + } + } + + let dev_node = if config.advanced.is_some() && config.advanced.clone().unwrap().os.is_some() { + config.advanced.unwrap().dev_node.unwrap() + } else { + "-dev".to_string() + }; + + // rename husk_file to husk_file- + match fs::rename( + format!( + "{dir}{sep}{husk_name}", + dir = &DEV_DIR(), + sep = &SEP, + husk_name = config.kernel_name.clone() + ), + format!( + "{dir}{sep}{husk_name}{dev_node}", + dir = &DEV_DIR(), + sep = &SEP, + husk_name = config.kernel_name.clone(), + dev_node = dev_node + ), + ) { + Ok(()) => (), + Err(_) => { + Print::error("Unable to create dev_node"); + return Err(()); + } + } + + // change permissions of file to be accessible by all + let mut perms = fs::metadata(format!( + "{dir}{sep}{husk_name}{dev_node}", + dir = &DEV_DIR(), + sep = &SEP, + husk_name = config.kernel_name.clone(), + dev_node = dev_node + )) + .unwrap() + .permissions(); + set_permissions(&mut perms, 0o511); + Ok(()) +} + +fn thread_compile(config: Config) -> () { + let compile_start = Instant::now(); + + // compile logic -> bool success state + let completed = match dev_compile(config) { + Ok(()) => true, + Err(()) => false, + }; + + // format ending time + let compile_end = format!("{:?}", compile_start.elapsed()); + let regex = Regex::new(r"\d+").unwrap(); + let time_regex = Regex::new(r"(?i)[a-z]").unwrap(); + + let units = regex.replace_all(&compile_end, ""); + let time = time_regex.replace(&compile_end, "").parse::().unwrap(); + + if completed { + Print::success(&format!("Compiled in {:.2}{}", time, units.split_at(1).1)); + } +} + +fn check_ignorance(path: PathBuf) -> Result { + // if path is dir ignore :) + if path.is_dir() { + return Err(()); + } + + // if path is .kernelrc print warning + if path == Path::new("./.kernelrc").to_path_buf() { + Print::warn("Updates to config detected, to see up-to-date changes re-run this command."); + return Err(()); + } + + let file = match Command::new("git") + .args(["check-ignore", &format!("{}", path.display())]) + .stdout(Stdio::piped()) + .spawn() + { + Ok(output) => output.wait_with_output().unwrap(), + Err(_) => { + Print::error("Unable to check git ignore"); + return Err(()); + } + }; + + if file.stdout.is_empty() { + return Ok(path); + } else { + return Err(()); + } +} + +fn event_handler(event: DebouncedEvent, config: Config) -> () { + // filter events + match event { + DebouncedEvent::Rescan + | DebouncedEvent::NoticeRemove(_) + | DebouncedEvent::NoticeWrite(_) => return, + DebouncedEvent::Error(err, path) => { + Print::error(&format!("{} with path {:?}", err, path)); + return; + } + DebouncedEvent::Chmod(event) + | DebouncedEvent::Create(event) + | DebouncedEvent::Remove(event) + | DebouncedEvent::Rename(_, event) + | DebouncedEvent::Write(event) => { + match check_ignorance(Path::new(&format!("{}", event.display())).to_path_buf()) { + Ok(path) => format!("{}", path.display()), + Err(_) => return, + } + } + }; + + Print::event("Received compile event."); + thread::spawn(|| thread_compile(config)).join(); +} + +pub async fn handle(options: Options) -> Result<()> { + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + if !PATH.contains(&DEV_DIR()) { + Print::warn("Development kernel not installed.\n\t\tplease try: popcorn install --dev"); + return Ok(()); + } + + let CONFIG: Config; + + match get_config() { + Ok(loaded_config) => CONFIG = loaded_config, + Err(_) => return Ok(()), // exit after error is printed + } + + let kernel_name = &CONFIG.kernel_name; + let kernel_type = &CONFIG.kernel_type; + let seed_cmd = &CONFIG.seed_cmd; + + if kernel_name == "" { + Print::error("Please include a kernel_name in the config file."); + return Ok(()); + } else if kernel_type == "" { + Print::error("Please include a kernel_type in the config file."); + return Ok(()); + } else if seed_cmd == "" { + Print::error("Please include a seed_cmd in the config file."); + return Ok(()); + } + + // define dir to watch + let dir = match options.listen { + Some(dir) => dir, + None => ".".to_string(), + }; + + let listen_dir = Path::new(dir.as_str()); + + if !listen_dir.exists() { + Print::error("File or directory does not exist."); + return Ok(()); + } + + Print::bold(&format!( + "{popcorn} dev v{ver}", + popcorn = BLAME.name, + ver = BLAME.version + )); + + // Create a channel to receive the events. + let (sender, receiver) = channel(); + + // Create a watcher object, delivering debounced events. + // The notification back-end is selected based on the platform. + let mut watcher = watcher(sender, time::Duration::from_secs(2)).unwrap(); + + // Add a path to be watched. All files and directories at that path and + // below will be monitored for changes. + watcher.watch(listen_dir, RecursiveMode::Recursive).unwrap(); + + Print::info(&format!("Listening to {}", dir)); + + let config = CONFIG.clone(); + + // initial run w/o waiting for event + thread::spawn(|| thread_compile(config)).join(); + + loop { + match receiver.recv() { + Ok(event) => event_handler(event, CONFIG.clone()), + Err(e) => println!("watch error: {:?}", e), + } + } +} diff --git a/src/commands/init/mod.rs b/src/commands/init/mod.rs new file mode 100644 index 0000000..8597fdc --- /dev/null +++ b/src/commands/init/mod.rs @@ -0,0 +1,50 @@ +use anyhow::Result; +use clap::Parser; + +use std::env; +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + +use crate::util::{Print, SEP}; + +#[derive(Debug, Parser)] +#[clap(about = "Initialize configuration file.")] +pub struct Options { + #[clap(short = 'f', long = "force", help = "Overwrite existing config file.")] + force: bool, + #[clap( + short = 'p', + long = "packed", + help = "Generate a packed configuration file." + )] + packed: bool, +} + +fn create_config(is_packed: bool) -> () { + let current = env::current_dir().unwrap(); + let binding = current.to_string_lossy(); + let cwd_name = binding.split(SEP).last().unwrap(); + let mut file = File::create(".kernelrc").unwrap(); + + let packed_json = format!("{{\n\"kernel\": \"{}\",\n\"kernel_type\": \"packed\",\n\"dev_cmd\": \"popcorn dev\",\n\"seed_cmd\": \"go build -o @dest\",\n\"advanced\": {{\n\"dev_node\": \"-dev\"\n}}\n}}", cwd_name).into_bytes(); + let unpacked_json = format!("{{\n\t\"kernel_name\": \"{}\",\n\t\"kernel_type\": \"unpacked\",\n\t\"unpacked_husk\": \"python @local/popcorn.py @args\",\n\t\"dev_cmd\": \"popcorn dev\",\n\t\"seed_cmd\": \"cp -r * @dest\",\n\t\"advanced\": {{\n\t\t\"dev_node\": \"-dev\"\n\t}}\n}}", cwd_name).into_bytes(); + + if is_packed { + file.write_all(&*packed_json).unwrap(); + } else { + file.write_all(&*unpacked_json).unwrap(); + } +} + +pub async fn handle(options: Options) -> Result<()> { + if !Path::new(".kernelrc").exists() { + create_config(options.packed) + } else if options.force { + create_config(options.packed) + } else { + Print::info(".kernelrc already exists; no changes made.") + }; + + Ok(()) +} diff --git a/src/commands/install/mod.rs b/src/commands/install/mod.rs new file mode 100644 index 0000000..561b9c5 --- /dev/null +++ b/src/commands/install/mod.rs @@ -0,0 +1,330 @@ +use anyhow::Result; +use clap::Parser; + +use std::fs::{create_dir_all, read_to_string, OpenOptions}; +use std::io::Write; +use std::path::PathBuf; +use std::process::Command; +use std::{env, path::Path}; + +use crate::util::{get_config, Config, Print, DEV_DIR, PATHSEP, PROD_DIR}; + +#[derive(Debug, Parser)] +#[clap(about = "Install a kernel into PATH.")] +pub struct Options { + #[clap(short = 'd', long = "dev", help = "Install development kernels.")] + dev: bool, +} + +pub fn apply_changes() -> Result<(), String> { + if cfg!(target_os = "windows") { + match Command::new(".").arg("$profile").output() { + Ok(_) => { + Print::success("Installed kernel!"); + return Ok(()); + } + Err(err) => return Err(err.to_string()), + } + } else { + // source from .shellrc and .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; + + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let profile_path = home.join(".profile"); + + if rc_path.exists() { + // source rc_path + if let Ok(mut child) = Command::new(shell.split("/").last().unwrap()) + .arg("-c") + .arg(format!("source {}", rc_path.display())) + .spawn() + { + child.wait().unwrap(); + Print::success("Succesfully installed kernel!"); + return Ok(()); + } else { + Print::info("Please restart terminal to apply changes"); + return Ok(()); + } + } else { + // source .profile + if let Ok(mut child) = Command::new(shell.split("/").last().unwrap()) + .arg("-c") + .arg(format!("source {}", profile_path.display())) + .spawn() + { + child.wait().unwrap(); + Print::success("Succesfully installed kernel!"); + return Ok(()); + } else { + Print::info("Please restart terminal to apply changes"); + return Ok(()); + } + } + } +} + +fn install_dev(PATH: String) -> Result<(), String> { + if DEV_DIR() == "" { + return Err("An error occurred while loading the DEV_DIR".to_string()); + } + + if !PATH.contains(&DEV_DIR()) { + if cfg!(target_os = "windows") { + // add to the path + match Command::new("[Environment]::SetEnvironmentVariable('PATH',") + .args([ + format!("$env:PATH{}{},", PATHSEP, DEV_DIR()), + "'User')".to_string(), + ]) + .output() + { + Ok(_) => (), + Err(e) => return Err(e.to_string()), + } + Command::new("$env:PATH") + .args(["+=", format!("'{}{}'", PATHSEP, DEV_DIR()).as_str()]) + .output() + .unwrap(); + + match apply_changes() { + Ok(()) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + // edit .shellrc or .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; + + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let profile_path = home.join(".profile"); + + if rc_path.exists() { + // edit rc_path + let mut file = match OpenOptions::new().write(true).append(true).open(rc_path) { + Ok(file) => file, + Err(_e) => return Err(".profile not found".to_string()), + }; + + file.write( + format!("\nexport PATH=$PATH{SEP}{}\n", DEV_DIR(), SEP = PATHSEP).as_bytes(), + ) + .unwrap(); + } else { + // edit .profile + let mut file = match OpenOptions::new() + .write(true) + .append(true) + .open(profile_path) + { + Ok(file) => file, + Err(_e) => return Err(".profile not found".to_string()), + }; + + file.write( + format!("\nexport PATH=$PATH{SEP}{}\n", DEV_DIR(), SEP = PATHSEP).as_bytes(), + ) + .unwrap(); + } + + match apply_changes() { + Ok(()) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } + } else { + Print::info("Kernel already installed"); + return Ok(()); + } +} + +fn install_prod(PATH: String, butter_file: PathBuf) -> Result<(), String> { + if PROD_DIR() == "" { + Print::error("An error occurred while loading the PROD_DIR"); + return Ok(()); + } + + if cfg!(target_os = "windows") { + // add system-wide + match Command::new("[Environment]::SetEnvironmentVariable('PATH',") + .args([ + format!("$env:PATH{}{},", PATHSEP, butter_file.display()), + "'User')".to_string(), + ]) + .output() + { + Ok(_) => (), + Err(e) => return Err(e.to_string()), + } + // add for current terminal session + Command::new("$env:PATH") + .args([ + "+=", + format!("'{}{}'", PATHSEP, butter_file.display()).as_str(), + ]) + .output() + .unwrap(); + + match apply_changes() { + Ok(()) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + // edit .shellrc or .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; + + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let rc_filepath = rc_path.clone(); + let profile_path = home.join(".profile"); + let profile_filepath = profile_path.clone(); + + if rc_path.exists() { + let contents = read_to_string(rc_path).expect("Unable to read rc file [prod:500]"); + if !contents.contains(". $HOME/.kernels/butter.sh") { + // open file in truncate + let mut file = match OpenOptions::new() + .write(true) + .truncate(true) + .open(rc_filepath) + { + Ok(file) => file, + Err(_e) => return Err("rc_path not found".to_string()), + }; + + file.write(format!(". $HOME/.kernels/butter.sh\n{}", contents).as_bytes()) + .unwrap(); + + match apply_changes() { + Ok(()) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + Print::info("Kernel already installed"); + return Ok(()); + } + } else { + let contents = + read_to_string(profile_path).expect("Unable to read profile file [prod:500]"); + if !contents.contains(". $HOME/.kernels/butter.sh") { + // open file in truncate + let mut file = match OpenOptions::new() + .write(true) + .truncate(true) + .open(profile_filepath) + { + Ok(file) => file, + Err(_e) => return Err("profile path not found".to_string()), + }; + + file.write(format!(". $HOME/.kernels/butter.sh\n{}", contents).as_bytes()) + .unwrap(); + + match apply_changes() { + Ok(()) => return Ok(()), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + Print::info("Kernel already installed!"); + return Ok(()); + } + } + } +} + +pub async fn handle(options: Options) -> Result<()> { + let CONFIG: Config; + + match get_config() { + Ok(loaded_config) => CONFIG = loaded_config, + Err(_) => return Ok(()), // exit after error is printed + } + + let kernel_name: &str = &CONFIG.kernel_name; + + if kernel_name == "" { + Print::error("Please include a kernel_name in the config file."); + return Ok(()); + } + + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + // CREATE BUTTER.SH FILE IF DOESNT EXIST + let dir = &PROD_DIR(); + let prod_directory = Path::new(dir); + let butter_file = prod_directory.join("butter.sh"); + + let butter_filepath = butter_file.clone(); + + if !butter_file.exists() { + if !prod_directory.exists() { + create_dir_all(PROD_DIR())?; + } + + let mut butter = OpenOptions::new() + .write(true) + .create(true) + .open(butter_file)?; + + write!(butter, "#!/bin/bash\n").unwrap(); + } + + if options.dev { + match install_dev(PATH) { + Ok(()) => (), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + match install_prod(PATH, butter_filepath) { + Ok(()) => (), + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } + + Ok(()) +} diff --git a/src/commands/issue/mod.rs b/src/commands/issue/mod.rs new file mode 100644 index 0000000..af44d5a --- /dev/null +++ b/src/commands/issue/mod.rs @@ -0,0 +1,19 @@ +use anyhow::Result; +use clap::Parser; + +use webbrowser; + +#[derive(Debug, Parser)] +#[clap(about = "Report an issue.")] +pub struct Options {} + +pub async fn handle(_options: Options) -> Result<()> { +// return match webbrowser::open("https://github.com/punctuations/popcorn/issues/new") { +// Ok(_) => Ok(()), +// Err(_) => Err(2), +// }; + + let _ = webbrowser::open("https://github.com/punctuations/popcorn/issues/new"); + + Ok(()) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..4532bc9 --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,49 @@ +pub mod build; +pub mod dev; +pub mod init; +pub mod install; +pub mod issue; +pub mod remove; +pub mod theatre; +pub mod update; +use anyhow::Result; + +use clap::Subcommand; + +// use crate::state::State; + +static SUBCOMMAND_HELP: &str = "\ + \n 🍿 Popcorn  -> {name}\n {about}\n\n 💻 https://github.com/punctuations/popcorn\n\n  Usage \n\n {usage}\n\n  Options \n\n{options}"; + +#[derive(Debug, Subcommand)] +pub enum Commands { + #[clap(help_template = SUBCOMMAND_HELP)] + Issue(issue::Options), + #[clap(help_template = SUBCOMMAND_HELP)] + Init(init::Options), + #[clap(help_template = SUBCOMMAND_HELP)] + Install(install::Options), + #[clap(help_template = SUBCOMMAND_HELP)] + Build(build::Options), + #[clap(help_template = SUBCOMMAND_HELP)] + Remove(remove::Options), + #[clap(help_template = SUBCOMMAND_HELP)] + Dev(dev::Options), + #[clap(help_template = SUBCOMMAND_HELP, alias = "add", alias = "theater")] + Theatre(theatre::Options), + #[clap(help_template = SUBCOMMAND_HELP)] + Update(update::Options), +} + +pub async fn handle_command(command: Commands) -> Result<()> { + match command { + Commands::Issue(options) => issue::handle(options).await, + Commands::Init(options) => init::handle(options).await, + Commands::Install(options) => install::handle(options).await, + Commands::Build(options) => build::handle(options).await, + Commands::Remove(options) => remove::handle(options).await, + Commands::Dev(options) => dev::handle(options).await, + Commands::Theatre(options) => theatre::handle(options).await, + Commands::Update(options) => update::handle(options).await, + } +} diff --git a/src/commands/remove/mod.rs b/src/commands/remove/mod.rs new file mode 100644 index 0000000..ddc240b --- /dev/null +++ b/src/commands/remove/mod.rs @@ -0,0 +1,269 @@ +use anyhow::Result; +use clap::Parser; + +use std::fs::{remove_dir_all, remove_file, OpenOptions}; +use std::io::{prelude::*, BufReader}; +use std::path::PathBuf; +use std::process::Command; +use std::{env, path::Path}; + +use crate::util::{Print, PATHSEP, PROD_DIR, SEP}; + +#[derive(Debug, Parser)] +#[clap(about = "Remove installed kernels.")] +pub struct Options { + #[clap(name = "name", help = "Name of kernel.")] + kernel_name: Option, + + #[clap(short = 'd', long = "dev", help = "Remove development kernels.")] + dev: bool, +} + +fn remove_windows( + removed_kernel: String, + PATH: String, + kernel_name: String, + is_dev: bool, +) -> Result { + // split paths + let paths: Vec<_> = PATH.split(PATHSEP).collect(); + + // filter array + let filter: Vec<_> = paths + .clone() + .into_iter() + .filter(|&x| !x.contains(&removed_kernel)) + .collect(); + + // join new filtered paths + let path_str = filter.join(&PATHSEP.to_string()); + + // set as the path + match Command::new("[Environment]::SetEnvironmentVariable('PATH',") + .arg(format!("'{}', 'User')", path_str)) + .output() + { + Ok(_) => (), + Err(e) => return Err(e.to_string()), + } + Command::new("$env:PATH") + .args(["=", path_str.as_str()]) + .output() + .unwrap(); + + Ok(format!( + "Removed{dev} kernel {}!", + kernel_name, + dev = if is_dev { " dev" } else { "" } + )) +} + +fn remove_dev(kernel_name: String, PATH: String) -> Result { + if PATH.contains(&format!("{}{SEP}.popcorn", kernel_name, SEP = SEP)) { + if cfg!(target_os = "windows") { + return match remove_windows( + format!("{}{SEP}.popcorn", kernel_name, SEP = SEP), + PATH, + kernel_name, + true, + ) { + Ok(success) => Ok(success), + Err(err) => Err(err), + }; + } else { + // remove line from .shellrc or .profile + let shell: String = match env::var("SHELL") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + let home: PathBuf = match dirs::home_dir() { + Some(path) => path, + None => return Err("Cannot find home_dir".to_string()), + }; + + let rc_path = home.join(format!(".{}rc", shell.split("/").last().unwrap())); + let profile_path = home.join(".profile"); + + // define removed kernel + let removed_kernel = format!("{}{SEP}.popcorn", kernel_name, SEP = SEP); + + if rc_path.exists() { + let mut file = match OpenOptions::new().read(true).write(true).open(rc_path) { + Ok(file) => file, + Err(_) => return Err("Unable to open rc file".to_string()), + }; + + let reader = BufReader::new(file.try_clone().unwrap()); + + // filter array + let filter: Vec<_> = reader + .lines() + .into_iter() + .map(|x| x.unwrap()) + .filter(|x| !x.contains(&removed_kernel)) + .collect(); + + // join contents into string + let contents_str = filter.join("\n"); + + // write to file + write!(file, "{}", contents_str); + } else { + let mut file = match OpenOptions::new().read(true).write(true).open(profile_path) { + Ok(file) => file, + Err(_) => return Err("Unable to open profile file".to_string()), + }; + + let reader = BufReader::new(file.try_clone().unwrap()); + + // filter array + let filter: Vec<_> = reader + .lines() + .into_iter() + .map(|x| x.unwrap()) + .filter(|x| !x.contains(&removed_kernel)) + .collect(); + + // join contents into string + let contents_str = filter.join("\n"); + + // write to file + write!(file, "{}", contents_str); + } + + Ok(format!("Removed dev kernel {}!", kernel_name)) + } + } else { + // kernel not in path: not installed, or has been installed but PATH not updated. + Err(format!("Kernel {} not installed.", kernel_name)) + } +} + +pub fn remove_prod(kernel_name: String, PATH: String) -> Result { + if PATH.contains(&format!( + "{PROD}{SEP}{}", + kernel_name, + SEP = SEP, + PROD = PROD_DIR() + )) || Path::new(&format!( + "{PROD}{SEP}{}", + kernel_name, + SEP = SEP, + PROD = PROD_DIR() + )) + .exists() + { + if cfg!(target_os = "windows") { + return match remove_windows( + format!("{PROD}{SEP}{}", kernel_name, SEP = SEP, PROD = PROD_DIR()), + PATH, + kernel_name, + false, + ) { + Ok(success) => Ok(success), + Err(err) => Err(err), + }; + } else { + if kernel_name == "butter.sh" || kernel_name == "versions.txt" { + return Err("Not a kernel".to_string()); + } + + // define removed kernel + let removed_kernel = + format!("{PROD}{SEP}{}", kernel_name, SEP = SEP, PROD = PROD_DIR()); + + let dir = &PROD_DIR(); + let prod_directory = Path::new(dir); + let butter_file = prod_directory.join("butter.sh").clone(); + + let butter = match OpenOptions::new().read(true).open(&butter_file) { + Ok(file) => file, + Err(_) => return Err("Unable to open butter file".to_string()), + }; + + let reader = BufReader::new(butter.try_clone().unwrap()); + + // filter array + let filter: Vec = reader + .lines() + .into_iter() + .map(|x| x.unwrap()) + .filter(|x| !x.contains(&removed_kernel)) + .collect(); + + // join contents into string + let contents_str = filter.join("\n"); + + // open and truncate + let mut butter_write = match OpenOptions::new() + .write(true) + .truncate(true) + .open(butter_file) + { + Ok(file) => file, + Err(_) => return Err("Unable to open butter file".to_string()), + }; + + // write to file + write!(butter_write, "{}\n", contents_str); + + // remove it + match remove_file(removed_kernel.clone()) { + Ok(()) => (), + Err(_) => match remove_dir_all(removed_kernel) { + Ok(()) => (), + Err(_) => (), + }, + } + + Ok(format!("Removed kernel {}!", kernel_name)) + } + } else { + // kernel not in path: not installed, or has been installed but PATH not updated. + Err(format!("Kernel {} not installed.", kernel_name)) + } +} + +pub async fn handle(options: Options) -> Result<()> { + let kernel_name = match options.kernel_name { + Some(name) => name, + None => { + Print::error("Please specify a kernel to remove."); + return Ok(()); + } + }; + + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + Print::info(format!("Removing {name}...", name = kernel_name).as_str()); + + if options.dev { + match remove_dev(kernel_name, PATH) { + Ok(success) => { + Print::success(success.as_str()); + Print::info("Please restart the terminal to apply changes."); + return Ok(()); + } + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } else { + match remove_prod(kernel_name, PATH) { + Ok(success) => { + Print::success(success.as_str()); + Print::info("Please restart the terminal to apply changes."); + return Ok(()); + } + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + } + } +} diff --git a/src/commands/theatre/mod.rs b/src/commands/theatre/mod.rs new file mode 100644 index 0000000..dc94729 --- /dev/null +++ b/src/commands/theatre/mod.rs @@ -0,0 +1,645 @@ +use flate2::read::GzDecoder; +use progress_bar::*; +use reqwest::header::USER_AGENT; +use reqwest::Url; +use std::fs::{self, create_dir_all, read_dir, remove_dir_all, DirEntry, OpenOptions}; +use std::io::{self, BufReader, Cursor, Read}; +use std::thread; +use std::{fs::File, path::Path, process::Command}; +use tar::Archive; + +use anyhow::Result; +use clap::Parser; + +use crate::util::blame::BLAME; +use crate::util::{ + calculate_hash, open_config, update_ver_cache, Config, GithubTags, Print, PROD_DIR, SEP, TMP, +}; + +use super::build::build_thread; + +#[derive(Debug, Parser)] +#[clap(about = "Used to install remote kernels.")] +pub struct Options { + #[clap(name = "link", help = "Name of kernel theatre.")] + kernel_link: Option, + + #[clap(long = "url", help = "Download directly from a url.")] + url: bool, +} + +pub fn download_kernel(url: String, file_name: String) -> Result { + create_dir_all(TMP()); + + init_progress_bar(2); + set_progress_bar_action("Downloading", Color::LightBlue, Style::Bold); + + let mut file_ext = ""; + + if cfg!(target_os = "windows") { + file_ext = ".zip"; + + if let Ok(mut child) = Command::new("Invoke-WebRequest") + .args([ + &url, + "-Out", + &format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + sep = SEP, + file_name = file_name, + TMP_DIR = TMP(), + file_ext = file_ext + ), + ]) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + return Err("An error occured while downloading the kernel".to_string()); + } + print_progress_bar_info( + "Success", + &format!("loading {}", url), + Color::Green, + Style::Bold, + ); + inc_progress_bar(); + } else { + return Err("Could not run command to download external kernel".to_string()); + } + } else { + file_ext = ".tar.gz"; + + if let Ok(mut child) = Command::new("curl") + .args([ + "--silent", + &url, + "-L", + "--output", + &format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + sep = SEP, + file_name = file_name, + TMP_DIR = TMP(), + file_ext = file_ext + ), + ]) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + print_progress_bar_info("Failed", "to download", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("An error occured while downloading the kernel".to_string()); + } + print_progress_bar_info( + "Success", + &format!("downloaded {}", url), + Color::Green, + Style::Bold, + ); + inc_progress_bar(); + } else { + print_progress_bar_info("Failed", "to run command", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err( + "Could not run command to download external kernel (is curl installed?)" + .to_string(), + ); + } + } + + let source = File::open(format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + file_name = file_name, + sep = SEP, + TMP_DIR = TMP(), + file_ext = file_ext + )); + + // used for version at end of download + let checksum = md5::compute(format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + file_name = file_name, + sep = SEP, + TMP_DIR = TMP(), + file_ext = file_ext + )); + + if cfg!(target_os = "windows") { + let mut deflate = source.unwrap(); + let mut data = Vec::new(); + deflate.read_to_end(&mut data); + + match zip_extract::extract( + Cursor::new(data), + Path::new(&format!("{}{}url-{}", TMP(), SEP, file_name)), + true, + ) { + Ok(()) => inc_progress_bar(), + _ => { + print_progress_bar_info("Failed", "to uncompress", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("Failed to uncompress deflate zip".to_string()); + } + } + } else { + let tar_gz = source.unwrap(); + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + + // have to put in url-download- so can later rename top-level dir to url- + match archive.unpack(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)) { + Ok(_) => print_progress_bar_info( + "Success", + "uncompressed tarball", + Color::Green, + Style::Bold, + ), + Err(_) => { + print_progress_bar_info("Failed", "to uncompress", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("Unable to uncompress tarball".to_string()); + } + }; + + // untaring will save it to incorrect dir, need to move contents of dir outside of it. + let files = read_dir(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)).unwrap(); + + // ensure the final dir is empty/non-existant so no errors are thrown during rename + remove_dir_all(format!("{}{sep}{}", TMP(), file_name, sep = SEP)); + + let file_vec = files.collect::>>(); + + // downloaded with only 1 file in it (most likely dir) + if file_vec.len() == 1 { + let dir = file_vec[0].as_ref().expect("unable to get entry"); + + match fs::rename( + dir.path(), + format!("{}{sep}{}", TMP(), file_name, sep = SEP), + ) { + Ok(()) => { + remove_dir_all(format!("{}{sep}{}-download", TMP(), file_name, sep = SEP)); + inc_progress_bar() + } + Err(_) => { + print_progress_bar_info("Failed", "to format dir", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("Unable to strip top-level dir".to_string()); + } + }; + } else { + match fs::rename( + format!("{}{sep}{}-download", TMP(), file_name, sep = SEP), + format!("{}{sep}{}", TMP(), file_name, sep = SEP), + ) { + Ok(()) => inc_progress_bar(), + Err(_) => { + print_progress_bar_info("Failed", "to format dir", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("Unable to rename dir".to_string()); + } + } + } + } + + let config = match open_config(&format!( + "{}{SEP}{}{SEP}.kernelrc", + TMP(), + file_name, + SEP = SEP + )) { + Ok(config) => config, + Err(_) => todo!(), + }; + + match update_ver_cache(config.kernel_name, checksum, &url) { + Ok(_) => (), + Err(err) => return Err(err), + } + + finalize_progress_bar(); + + return Ok(format!("{}{sep}{}", TMP(), file_name, sep = SEP)); +} + +pub fn ensure_os_compat(path: String) -> Result { + let config = match open_config(&format!("{}{SEP}.kernelrc", path, SEP = SEP)) { + Ok(config) => config, + Err(_) => { + remove_dir_all(path); + return Err("Unable to read config".to_string()); + } + }; + + let compat_os = if config.advanced.is_some() && config.clone().advanced.unwrap().os.is_some() { + config.clone().advanced.unwrap().os.unwrap() + } else { + ["".to_string(), "".to_string(), "".to_string()] + }; + + let os = if cfg!(target_os = "windows") { + "windows".to_string() + } else if cfg!(target_os = "macos") { + "mac".to_string() + } else { + "linux".to_string() + }; + + if !compat_os.contains(&os) { + remove_dir_all(path); + return Err("Unsupported os type.".to_string()); + } + + Ok(config) +} + +// async fn download_homebrew(pkg: &str) -> Result<(), String> { +// // get the ruby homebrew file +// let req = match reqwest::Client::new() +// .get(format!( +// "https://raw.githubusercontent.com/Homebrew/homebrew-core/master/Formula/{}.rb", +// pkg.to_lowercase() +// )) +// .header(USER_AGENT, format!("Popcorn {}", BLAME.version)) +// .send() +// .await +// { +// Ok(data) => data, +// Err(_) => { +// Print::error("Could not fetch data (URL not valid)"); +// return Ok(()); +// } +// }; + +// if req.status() != 200 { +// Print::error("Could not fetch data (does the homebrew pkg exist?)"); +// return Ok(()); +// } + +// let ruby = match req.text().await { +// Ok(txt) => txt, +// Err(err) => { +// Print::error(&format!("Unable to get text response; {}", err)); +// return Ok(()); +// } +// }; + +// // parse the ruby from the homebrew pkg + +// let (kernel_name, url) = match parse_class(ruby) { +// Ok((kernel_name, url)) => (kernel_name, url), +// Err(()) => return Ok(()), +// }; + +// // download + +// let mut file_ext = ""; + +// if cfg!(target_os = "windows") { +// file_ext = ".zip"; + +// if let Ok(mut child) = Command::new("Invoke-WebRequest") +// .args([ +// &url, +// "-Out", +// &format!( +// "{TMP_DIR}{sep}{file_name}{file_ext}", +// sep = SEP, +// file_name = kernel_name, +// TMP_DIR = TMP(), +// file_ext = file_ext +// ), +// ]) +// .spawn() +// { +// let finished = child.wait().unwrap(); +// if !finished.success() { +// return Err("An error occured while downloading the kernel".to_string()); +// } +// } else { +// return Err("Could not run command to download external kernel".to_string()); +// } +// } else { +// file_ext = ".tar.gz"; + +// if let Ok(mut child) = Command::new("curl") +// .args([ +// "--silent", +// &url, +// "-L", +// "--output", +// &format!( +// "{TMP_DIR}{sep}{file_name}{file_ext}", +// sep = SEP, +// file_name = kernel_name, +// TMP_DIR = TMP(), +// file_ext = file_ext +// ), +// ]) +// .spawn() +// { +// let finished = child.wait().unwrap(); +// if !finished.success() { +// return Err("An error occured while downloading the kernel".to_string()); +// } +// } else { +// return Err( +// "Could not run command to download external kernel (is curl installed?)" +// .to_string(), +// ); +// } +// } + +// // open tar file (to get compressed data) +// let f = File::open(format!( +// "{TMP_DIR}{sep}{file_name}{file_ext}", +// sep = SEP, +// file_name = kernel_name, +// TMP_DIR = TMP(), +// file_ext = file_ext +// )) +// .unwrap(); +// let mut reader = BufReader::new(f); + +// let mut compressed_contents = Vec::new(); + +// reader.read_to_end(&mut compressed_contents); + +// // calculate md5 hash from compressed bytes +// let checksum = md5::compute(compressed_contents); + +// println!("{} @ {}", kernel_name, url); +// println!("MD5 Checksum: {:?}", checksum); + +// // final success -> add stuff to versions.txt + +// Ok(()) +// } + +fn download_homebrew(pkg: &str) -> Result<(), String> { + println!("Homebrew download {}", pkg); + + Ok(()) +} + +fn download_yum(pkg: &str) -> Result<(), String> { + println!("yum download {}", pkg); + + Ok(()) +} + +pub async fn handle(options: Options) -> Result<()> { + // get kernel link from passed in args + let kernel_link = match options.kernel_link { + Some(link) => link, + None => { + Print::error("Please specify a theatre."); + return Ok(()); + } + }; + + // if kernel_link does not contain "/" exit. + if !options.url && !kernel_link.contains("/") { + Print::error("Please follow the scheme of user/repo."); + return Ok(()); + }; + + // declare version for downloading specific version. + let mut ver: Option = None; + + // kernel_link for non-url + let mut extern_kernel = kernel_link.clone(); + + // test if kernel_link contains a specific version. + if !options.url && kernel_link.contains("@") { + let kernel_ver_vec = kernel_link.split("@").collect::>(); + + ver = Some(kernel_ver_vec[1].to_string()); + extern_kernel = (kernel_ver_vec[0]).to_string() + } + + // if is --url flag is present + if options.url { + let url = kernel_link.clone(); + + // calculate hash for url as name + let hash_name = calculate_hash(&url).to_string(); + + // download files and move to /tmp/<...> + let download_dir = match download_kernel(url, format!("url-{}", hash_name)) { + Ok(path) => path, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + let config = match ensure_os_compat(download_dir.clone()) { + Ok(config) => config, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + if Path::new(&PROD_DIR()) + .join(config.kernel_name.clone()) + .exists() + { + Print::warn("A kernel with that name already exists."); + remove_dir_all(download_dir.clone()); + return Ok(()); + } + + // build here + let kernel_name = config.kernel_name.clone(); + let kernel_type = config.kernel_type.clone(); + + if &kernel_name == "" { + Print::error("No kernel_name in the config file."); + return Ok(()); + } else if &kernel_type == "" { + Print::error("No kernel_type in the config file."); + return Ok(()); + } else if &config.seed_cmd == "" { + Print::error("No seed_cmd in the config file."); + return Ok(()); + } + + let dir = &PROD_DIR(); + let prod_path = Path::new(dir); + + if !prod_path.exists() { + create_dir_all(prod_path); + } + + let mut output = kernel_name.clone(); + + if kernel_type.to_lowercase() == "unpacked" { + output += &SEP.to_string(); + }; + + thread::spawn(|| build_thread(output, config, true, download_dir)).join(); + } else { + // not --url, it is a github repo or external pkg manager pkg + + // filename for storage in /tmp + let file_name = extern_kernel.replace("/", "@"); + + // type of os (used for downloading files) + let os = if cfg!(target_os = "windows") { + "windows.zip".to_string() + } else if cfg!(target_os = "macos") { + "mac.tar.gz".to_string() + } else { + "linux.tar.gz".to_string() + }; + + // used to detect if is other pkg manager + let kernel_name_split = kernel_link.split("/").collect::>(); + let repo = kernel_name_split[0]; + let pkg = kernel_name_split[1]; + + // check if package with same name (could be same package) is already installed + let dir = &PROD_DIR(); + let prod_directory = Path::new(dir); + let butter_file = prod_directory.join("butter.sh").clone(); + + if butter_file.exists() { + let butter = OpenOptions::new() + .read(true) + .append(true) + .open(butter_file) + .unwrap(); + + let mut reader = BufReader::new(butter.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if contents.contains(pkg) { + Print::warn("A kernel with this name already exists!"); + return Ok(()); + } + } + + // allow compatibility! + if repo.to_lowercase() == "homebrew" || repo.to_lowercase() == "brew" { + download_homebrew(pkg); + return Ok(()); + } else if repo.to_lowercase() == "yum" { + download_yum(pkg); + return Ok(()); + } + + // get ver number + if ver.is_none() { + let github_tag_link = format!("https://api.github.com/repos/{}/tags", extern_kernel); + + let url = match Url::parse(&*github_tag_link) { + Ok(url) => url, + Err(_) => { + Print::error("Could not parse URL"); + return Ok(()); + } + }; + + let req = match reqwest::Client::new() + .get(url) + .header(USER_AGENT, format!("Popcorn {}", BLAME.version)) + .send() + .await + { + Ok(data) => data, + Err(_) => { + Print::error("Could not fetch data (URL not valid)"); + return Ok(()); + } + }; + + if req.status() != 200 { + Print::error("Could not fetch data (does the repo exist?)"); + return Ok(()); + } + + let release = match req.json::().await { + Ok(json) => json, + Err(err) => { + Print::error(&format!("Unable to jsonify response; {}", err)); + return Ok(()); + } + }; + + let tag = &release[0].name; + + ver = Some(tag.to_string()); + } + + // download files and move to /tmp/<...> + let download_dir = match download_kernel( + format!( + "https://github.com/{}/releases/download/{}/kernel-{}", + extern_kernel, + ver.unwrap(), + os + ), + pkg.to_string(), + ) { + Ok(path) => path, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + let config = match ensure_os_compat(download_dir.clone()) { + Ok(config) => config, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + if Path::new(&PROD_DIR()) + .join(config.kernel_name.clone()) + .exists() + { + Print::warn("A kernel with that name already exists."); + remove_dir_all(download_dir.clone()); + return Ok(()); + } + + // build here + let kernel_name = config.kernel_name.clone(); + let kernel_type = config.kernel_type.clone(); + + if &kernel_name == "" { + Print::error("No kernel_name in the config file."); + return Ok(()); + } else if &kernel_type == "" { + Print::error("No kernel_type in the config file."); + return Ok(()); + } else if &config.seed_cmd == "" { + Print::error("No seed_cmd in the config file."); + return Ok(()); + } + + let dir = &PROD_DIR(); + let prod_path = Path::new(dir); + + if !prod_path.exists() { + create_dir_all(prod_path); + } + + let mut output = kernel_name.clone(); + + if kernel_type.to_lowercase() == "unpacked" { + output += &SEP.to_string(); + }; + + thread::spawn(|| build_thread(output, config, true, download_dir)).join(); + } + + Ok(()) +} diff --git a/src/commands/update/mod.rs b/src/commands/update/mod.rs new file mode 100644 index 0000000..3d8fb07 --- /dev/null +++ b/src/commands/update/mod.rs @@ -0,0 +1,246 @@ +use anyhow::Result; +use clap::Parser; +use progress_bar::*; + +use std::fs::{create_dir_all, remove_dir_all}; +use std::io::{prelude::*, BufReader}; +use std::path::Path; +use std::process::Command; +use std::thread; +use std::{env, fs::OpenOptions}; + +use crate::util::{calculate_hash, Print, PROD_DIR, SEP, TMP}; + +use super::build::build_thread; +use super::remove::remove_prod; +use super::theatre::{download_kernel, ensure_os_compat}; + +#[derive(Debug, Parser)] +#[clap(about = "Update kernels.")] +pub struct Options { + #[clap(name = "kernel", help = "Name of kernel.")] + kernel: Option, +} + +fn download_and_checksum(url: String, file_name: String) -> Result { + create_dir_all(TMP()); + + init_progress_bar(2); + set_progress_bar_action("Downloading", Color::LightBlue, Style::Bold); + + let mut file_ext = ""; + + if cfg!(target_os = "windows") { + file_ext = ".zip"; + + if let Ok(mut child) = Command::new("Invoke-WebRequest") + .args([ + &url, + "-Out", + &format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + sep = SEP, + file_name = file_name, + TMP_DIR = TMP(), + file_ext = file_ext + ), + ]) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + return Err("An error occured while downloading the kernel".to_string()); + } + print_progress_bar_info( + "Success", + &format!("loading {}", url), + Color::Green, + Style::Bold, + ); + inc_progress_bar(); + } else { + return Err("Could not run command to download external kernel".to_string()); + } + } else { + file_ext = ".tar.gz"; + + if let Ok(mut child) = Command::new("curl") + .args([ + "--silent", + &url, + "-L", + "--output", + &format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + sep = SEP, + file_name = file_name, + TMP_DIR = TMP(), + file_ext = file_ext + ), + ]) + .spawn() + { + let finished = child.wait().unwrap(); + if !finished.success() { + print_progress_bar_info("Failed", "to download", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err("An error occured while downloading the kernel".to_string()); + } + print_progress_bar_info( + "Success", + &format!("downloaded {}", url), + Color::Green, + Style::Bold, + ); + inc_progress_bar(); + } else { + print_progress_bar_info("Failed", "to run command", Color::Red, Style::Normal); + finalize_progress_bar(); + return Err( + "Could not run command to download external kernel (is curl installed?)" + .to_string(), + ); + } + } + + let checksum = md5::compute(format!( + "{TMP_DIR}{sep}{file_name}{file_ext}", + file_name = file_name, + sep = SEP, + TMP_DIR = TMP(), + file_ext = file_ext + )); + + return Ok(format!("{:?}", checksum)); +} + +pub async fn handle(options: Options) -> Result<()> { + // redownload and calculate checksum, if eq then dont update. + // if not eq then remove kernel and reinstall. + + // get kernel from passed in args + let kernel_name = match options.kernel { + Some(kernel) => kernel, + None => { + Print::error("Please specify a theatre."); + return Ok(()); + } + }; + + // get link to redownload update *** note: updates only work for theatres *** + let prod_dir = &PROD_DIR(); + let ver = Path::new(prod_dir).join("versions.txt"); + + if !ver.exists() { + // version file does not exist + Print::error("No version file detected (no compat theatre kernels installed?)") + } + + let versions_file = OpenOptions::new() + .read(true) + .append(true) + .open(ver) + .unwrap(); + + let mut reader = BufReader::new(versions_file.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + let split_contents = contents.splitn(2, &kernel_name).collect::>(); // [/* pre-kernel versions */, " old_checksum url\n next_kernel etc.."] + let split_spaced_contents = split_contents[1] + .split(|c| c == ' ' || c == '\n') + .collect::>(); // [old_checksum, url] + + let old_checksum = split_spaced_contents[1]; + let url = split_spaced_contents[2].to_string(); + + let checksum = match download_and_checksum(url.clone(), kernel_name.clone()) { + Ok(checksum) => checksum, + Err(_) => { + Print::error("Unable to calculate new checksum."); + return Ok(()); + } + }; + + if old_checksum == checksum { + Print::info("No update required; no new version detected.") + } else { + Print::info("Update needed."); + + // remove old + let PATH: String = match env::var("PATH") { + Ok(path) => path, + Err(_) => "".to_string(), + }; + + match remove_prod(kernel_name.clone(), PATH) { + Ok(_) => { + Print::info("(1/2) Removed pre-existing kernel."); + } + Err(err) => { + Print::error(err.as_str()); + return Ok(()); + } + }; + + // download/reinstall files + let download_dir = match download_kernel(url, kernel_name.clone()) { + Ok(path) => path, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + let config = match ensure_os_compat(download_dir.clone()) { + Ok(config) => config, + Err(err) => { + Print::error(&err); + return Ok(()); + } + }; + + if Path::new(&PROD_DIR()) + .join(config.kernel_name.clone()) + .exists() + { + Print::warn("A kernel with that name already exists."); + remove_dir_all(download_dir.clone()); + return Ok(()); + } + + // build here + let kernel_name = config.kernel_name.clone(); + let kernel_type = config.kernel_type.clone(); + + if &kernel_name == "" { + Print::error("No kernel_name in the config file."); + return Ok(()); + } else if &kernel_type == "" { + Print::error("No kernel_type in the config file."); + return Ok(()); + } else if &config.seed_cmd == "" { + Print::error("No seed_cmd in the config file."); + return Ok(()); + } + + let dir = &PROD_DIR(); + let prod_path = Path::new(dir); + + if !prod_path.exists() { + create_dir_all(prod_path); + } + + let mut output = kernel_name.clone(); + + if kernel_type.to_lowercase() == "unpacked" { + output += &SEP.to_string(); + }; + + thread::spawn(|| build_thread(output, config, true, download_dir)).join(); + + Print::success(&format!("(2/2) Successfully updated {}.", kernel_name)); + } + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ad9ba6a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,23 @@ +pub mod commands; +pub mod util; + +use clap::Parser; +use commands::Commands; + +#[derive(Debug, Parser)] +#[clap( + name = "popcorn", + about = "An all-system package manager.", + version, + author +)] +#[clap(help_template = "\ +\n 🍿 Popcorn  v{version}\n {about}\n\n 💻 https://github.com/punctuations/popcorn\n\n  Usage \n\n {usage}\n\n  Options \n\n{options}\n\n  Subcommands \n\n{subcommands} +")] +pub struct CLI { + #[clap(subcommand)] + pub commands: Commands, + + #[clap(long = "debug", help = "Print debug information", global = true)] + pub debug: bool, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f3c2e36 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,20 @@ +use anyhow::Result; +use clap::Parser; + +use popcorn::commands::handle_command; +use popcorn::{util, CLI}; + +#[tokio::main] +async fn main() -> Result<()> { + // create a new CLI instance + let cli = CLI::parse(); + + if let Err(error) = handle_command(cli.commands).await { + log::error!("{}", error); + std::process::exit(1); + } + + util::clear(); + + Ok(()) +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..0eefc5e --- /dev/null +++ b/src/util.rs @@ -0,0 +1,349 @@ +use anyhow::Result; +use chrono::Local; +use console::style; +use dirs; +use md5::Digest; +use std::collections::hash_map::DefaultHasher; +use std::env; +use std::fs::{File, OpenOptions, Permissions}; +use std::hash::{Hash, Hasher}; +use std::io::{BufReader, Read, Write}; + +use serde_derive::{Deserialize, Serialize}; + +use std::any::{type_name, Any}; +use std::path::{Path, MAIN_SEPARATOR}; + +pub mod blame; + +pub type GithubTags = Vec; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Root { + pub name: String, + #[serde(rename = "zipball_url")] + pub zipball_url: String, + #[serde(rename = "tarball_url")] + pub tarball_url: String, + pub commit: Commit, + #[serde(rename = "node_id")] + pub node_id: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Commit { + pub sha: String, + pub url: String, +} + +pub const SEP: char = MAIN_SEPARATOR; +pub const PATHSEP: char = if cfg!(target_os = "windows") { + ';' +} else { + ':' +}; + +#[derive(Debug, Deserialize, Default, Clone)] +pub struct AdvancedConfig { + pub os: Option<[String; 3]>, + pub dev_node: Option, +} + +#[derive(Debug, Deserialize, Default, Clone)] +pub struct Config { + pub kernel_name: String, + pub kernel_type: String, + pub unpacked_husk: Option, + pub dev_cmd: String, + pub seed_cmd: String, + pub advanced: Option, +} + +pub fn get_config() -> Result { + let file: String; + + match read_file(".kernelrc") { + Ok(contents) => file = contents, + Err(error) => { + Print::error(format!("File not found: {}", error).as_str()); + return Err(error); + } + } + + let config: Config = match serde_json::from_str(&file) { + Ok(json) => json, + Err(_) => Config { + kernel_name: "".to_string(), + kernel_type: "".to_string(), + unpacked_husk: Some("".to_string()), + dev_cmd: "".to_string(), + seed_cmd: "".to_string(), + advanced: None, + }, + }; + + Ok(config) +} + +pub fn open_config(path: &str) -> Result { + let file: String; + + match read_file(path) { + Ok(contents) => file = contents, + Err(error) => { + return Err(error); + } + } + + let config: Config = match serde_json::from_str(&file) { + Ok(json) => json, + Err(_) => Config { + kernel_name: "".to_string(), + kernel_type: "".to_string(), + unpacked_husk: Some("".to_string()), + dev_cmd: "".to_string(), + seed_cmd: "".to_string(), + advanced: None, + }, + }; + + Ok(config) +} + +pub fn TMP() -> String { + if cfg!(target_os = "windows") { + return env::temp_dir() + .join("popcorn-kernel") + .into_os_string() + .into_string() + .unwrap(); + } else { + return Path::new("/tmp") + .join("popcorn-kernel") + .into_os_string() + .into_string() + .unwrap(); + }; +} + +pub fn DEV_DIR() -> String { + match env::current_dir() { + Ok(path) => { + return path + .join(".popcorn") + .into_os_string() + .into_string() + .unwrap() + } + Err(_) => return "".to_string(), + } +} + +pub fn PROD_DIR() -> String { + match dirs::home_dir() { + Some(path) => { + return path + .join(".kernels") + .into_os_string() + .into_string() + .unwrap() + } + None => return "".to_string(), + } +} + +pub fn clear() { + let term = console::Term::stdout(); + term.show_cursor().ok(); +} + +pub fn type_of(_: T) -> &'static str { + type_name::() +} + +pub fn read_file(file_name: &str) -> Result { + match std::fs::read_to_string(file_name) { + Ok(contents) => Ok(contents), + Err(error) => Err(error.to_string()), + } +} + +pub fn calculate_hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} + +pub fn update_ver_cache(kernel_name: String, checksum: Digest, url: &str) -> Result<(), String> { + let prod_dir = &PROD_DIR(); + let ver = Path::new(prod_dir).join("versions.txt"); + + if !ver.exists() { + // version file does not exist + match File::create(&ver) { + Ok(_) => (), + Err(_) => return Err("Unable to create versions file".to_string()), + } + } + + // check if kernel already there and if checksum matches then replace, if its same checksum then dont add. + let versions_file = OpenOptions::new() + .read(true) + .append(true) + .open(ver) + .unwrap(); + + let mut reader = BufReader::new(versions_file.try_clone().unwrap()); + let mut contents = String::new(); + reader.read_to_string(&mut contents); + + if !contents.contains(&kernel_name) { + let mut file = match OpenOptions::new() + .write(true) + .append(true) + .open(Path::new(prod_dir).join("versions.txt")) + { + Ok(file) => file, + Err(_e) => return Err("version file not found (unable to create?)".to_string()), + }; + + file.write( + format!( + "{kernel} {:?} {url}\n", + checksum, + kernel = kernel_name, + url = url + ) + .as_bytes(), + ) + .unwrap(); + } else if !contents.contains(&format!("{:x}", checksum)) && contents.contains(&kernel_name) { + let split_contents = contents.splitn(2, &kernel_name).collect::>(); + let check_split = split_contents[1].split(" ").collect::>(); + let old_checksum = check_split[1]; + let remaining_split = split_contents[1].split(old_checksum).collect::>(); + let updated_version_checksum = format!( + "{}{} {:x}{}", + &split_contents[0], kernel_name, checksum, remaining_split[1] + ); + + let mut file = match OpenOptions::new() + .write(true) + .truncate(true) + .open(Path::new(prod_dir).join("versions.txt")) + { + Ok(file) => file, + Err(_e) => return Err("version file not found (unable to create?)".to_string()), + }; + + file.write(updated_version_checksum.as_bytes()).unwrap(); + } + + Ok(()) +} + +// pub fn parse_class(ruby: String) -> Result<(String, String), ()> { +// let runtime = minutus::Evaluator::build(); + +// let parser = ruby.split("\n").collect::>(); + +// // fix ruby class +// let fixed_ruby: String = parser +// .iter() +// .map(|x| { +// if x.contains("Formula") { +// x.split(" < ").collect::>()[0].to_owned() + "\n" +// } else { +// x.to_string() + "\n" +// } +// }) +// .collect(); + +// println!("{}", fixed_ruby); + +// let out = runtime.evaluate(&fixed_ruby).unwrap(); + +// println!("{:?}", out); + +// // if above works then dont need this vv + +// let url = parser +// .iter() +// .filter(|x| x.contains("url")) +// .cloned() +// .collect::>()[0] +// .split("\"") +// .collect::>()[1]; + +// // return depends_on(s) +// // ... + +// // return install func as seed_cmd +// // ... + +// let kernel_name = "cowsay".to_string(); + +// if kernel_name == "" || url == "" { +// return Err(()); +// } + +// Ok((kernel_name, url.to_string())) +// } + +pub struct Print; + +impl Print { + fn print_color(notation: &str, message: &str, color: u8) -> () { + println!("{}: {}", style(notation).color256(color), message); + } + + pub fn bold(message: &str) -> () { + println!("{}", style(message).bold()); + } + + pub fn error(message: &str) -> () { + Self::print_color("[ERROR]", message, 1) + } + + pub fn success(message: &str) -> () { + Self::print_color("[SUCCESS]", message, 48) + } + + pub fn warn(message: &str) -> () { + Self::print_color("[WARN]", message, 10) + } + + pub fn info(message: &str) -> () { + Self::print_color("[INFO]", message, 4) + } + + pub fn event(message: &str) -> () { + Self::print_color( + &format!("[EVENT @ {}]", Local::now().format("%H:%M:%S")), + message, + 13, + ) + } +} + +#[cfg(unix)] +pub fn set_permissions(metadata_permissions: &mut Permissions, _mode: u32) -> Result<(), ()> { + use std::os::unix::prelude::PermissionsExt; + + metadata_permissions.set_mode(0o511); + + if metadata_permissions.mode() != 0o511 { + Print::error("Unable to change file permissions."); + return Err(()); + } + + Ok(()) +} + +#[cfg(not(unix))] +#[allow(unused)] +pub fn set_permissions(metadata_permissions: &mut Permissions, mode: u32) -> Result<(), ()> { + Ok(()) +} diff --git a/src/util/blame.rs b/src/util/blame.rs new file mode 100644 index 0000000..83f826b --- /dev/null +++ b/src/util/blame.rs @@ -0,0 +1,13 @@ +pub struct Blame<'a> { + pub name: &'a str, + pub description: &'a str, + pub version: &'a str, + pub source: &'a str, +} + +pub const BLAME: Blame = Blame { + name: "🍿 Popcorn", + description: env!("CARGO_PKG_DESCRIPTION"), + version: env!("CARGO_PKG_VERSION"), + source: env!("CARGO_PKG_REPOSITORY"), +};