Compare commits
239 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ed9cfa018 | ||
|
|
71791afca0 | ||
| a52eddfdac | |||
| 0474978ac0 | |||
| 4b94a0ce2a | |||
| 0d1eac7610 | |||
| b129e5d5a4 | |||
| 9f5c22890d | |||
|
|
74f4a19003 | ||
|
|
717d9872dc | ||
| 439270fe2d | |||
|
|
601fd55435 | ||
|
|
341c5254f1 | ||
| cb2701ada4 | |||
| 28fcccce98 | |||
| 2ba3e56772 | |||
|
|
f3fa3401f1 | ||
|
|
820f339982 | ||
| afd9dd34a7 | |||
| 4a4e5d2cf4 | |||
| 029b00532e | |||
|
|
0b052684c2 | ||
|
|
3dda80b50f | ||
| 9dede1e45d | |||
| 6dc64c6edf | |||
| c21748df1e | |||
| 27d024cca2 | |||
| b0dd2575d9 | |||
| 7a004c2cfc | |||
| 53ab164379 | |||
| bf4fb3a55d | |||
| 2235f1d0d9 | |||
| 17dcb2b10b | |||
| 4c7c62eb0b | |||
| 81dcf4b003 | |||
| 3fffdd20a8 | |||
| 078d75473b | |||
| 8b7ff58c7d | |||
| ff67eebcea | |||
|
|
e585eb46ea | ||
| 01507f88da | |||
| 6344b6dba2 | |||
| d3042cf724 | |||
| fd4b9a4f15 | |||
| 2879a2aa67 | |||
| 5cd8cb66f1 | |||
| 47c206ca1d | |||
| 8f22b64a0a | |||
| 7941b3d16c | |||
| e9ded4bde4 | |||
| 7b02472f67 | |||
| a370d67121 | |||
| 4f6e64083a | |||
| 276a672df4 | |||
| e55a157977 | |||
| e1e91d1add | |||
| 8102b5933f | |||
| bc60128679 | |||
| fc3a7ab789 | |||
| 93d3e6cec7 | |||
| 95779f1ac2 | |||
| 7bc57d7696 | |||
| 7e7b75f378 | |||
| a958350b2d | |||
| c502b3d08f | |||
|
|
e602b66188 | ||
| 12fba15bcf | |||
| 7e36ad4e8a | |||
| 33249f509f | |||
| ed645dd0d5 | |||
| b12c635c27 | |||
| c16ecfb188 | |||
| 18a8b81631 | |||
| 1d404d4d2c | |||
| 42479ced21 | |||
| 1193b8c848 | |||
| ec8d748991 | |||
| bafaf7ca7a | |||
| f7315a3bec | |||
| f655ca989d | |||
| fcb87a6779 | |||
| 924f8d5eff | |||
| 64ecc38073 | |||
| 5f94dbcabe | |||
| 2ecc591966 | |||
| 30ba1f3317 | |||
| 4fdf9b3df1 | |||
| 22fe1a8f73 | |||
| 38c0ad29dd | |||
| 89d106c03e | |||
| 3e36bcf307 | |||
| acf983c07c | |||
| fedb78fb88 | |||
| db64a0968b | |||
| aece20af47 | |||
| 6c5a73f78f | |||
| 906e093152 | |||
| 478b4ae3c0 | |||
| 23971cbb76 | |||
| 43410fac60 | |||
| cb8035a2ce | |||
| 8d071c7674 | |||
| 965c488468 | |||
| ede7f64c4b | |||
| ba38dcdc15 | |||
| 92d9222b05 | |||
| 4c396c3442 | |||
| e1d5139e36 | |||
| 1ad35652f8 | |||
| 9a9b13d604 | |||
| 77b8b61079 | |||
| bdf48d1bf4 | |||
| f8792ea012 | |||
| 4afde8b750 | |||
| f5614995c7 | |||
| 9ea6dbec20 | |||
| d73dfb9fc7 | |||
| a7da73300c | |||
| a308b8fe95 | |||
| 1d1e42aeb1 | |||
| 1f81061152 | |||
| 368d5d3db7 | |||
| 0612a02d68 | |||
| a0d470087b | |||
| 3ecaf04eb4 | |||
| df0811985d | |||
| 057ff0fef1 | |||
| 14c46f88ab | |||
| e38e430c77 | |||
|
|
93cd235aef | ||
|
|
df9bba32cb | ||
|
|
28a8f9b2fa | ||
| e49b366d77 | |||
| 9a0963ca2c | |||
| 17737a06a4 | |||
|
|
464779cc17 | ||
|
|
f25c0889a3 | ||
| 90170cb3d5 | |||
| 4dcb141f3a | |||
| 133721917f | |||
| 766e23d265 | |||
| 77d8e84e14 | |||
| 00c1cca412 | |||
| 4968833d05 | |||
| d172fa17f6 | |||
| 3c99b38db7 | |||
|
|
1128937cac | ||
| 6fc1228173 | |||
| b48a2efb7d | |||
| 412cb2408e | |||
| 682bc91855 | |||
| f03120e5a1 | |||
| 8dd63b30e8 | |||
| 54006c378f | |||
| 9269b66aa8 | |||
| cfac433861 | |||
| a28f8c3dd2 | |||
| 4001dee1bd | |||
| d1ffd0d77f | |||
| 91ad50350d | |||
| a88d43807e | |||
| 98619664cf | |||
| 39f8ad2106 | |||
| 82ce38d7b5 | |||
| 12eb453fc7 | |||
| a84324d3bc | |||
| ed2211586e | |||
| c09950d0af | |||
| e9a30382a3 | |||
| 7bf3311102 | |||
| cbad40245f | |||
| 75c4fcbb9e | |||
| f3b7f155b7 | |||
| 6427a80bd1 | |||
| 5b65e87225 | |||
| 1b8b19fde5 | |||
| 03d7aed258 | |||
| 23d149093f | |||
| 27f12716d9 | |||
| 048877bbb6 | |||
| 87a652d911 | |||
| d6863dc1fd | |||
| f1d934b0a6 | |||
| 5850f7a621 | |||
| dd23e84ccf | |||
| b060518778 | |||
| de95f13feb | |||
| 0205f13e53 | |||
| b4de97dfe2 | |||
| 35bc6cf31c | |||
| c58e8b1a00 | |||
| accdf99503 | |||
| 47b609369b | |||
| 23b1ca4371 | |||
| 73d666d1f5 | |||
| b27c13cf74 | |||
| bd1a4f0939 | |||
| 5abed23cf2 | |||
| 9d0948e124 | |||
| 678bc77a23 | |||
| 00cdeee5c6 | |||
| 2d251554ad | |||
| 1b5d70ae2d | |||
| 2d2901f6dc | |||
| a0b27ec105 | |||
| 093ef136e7 | |||
| 8660de530d | |||
| bda6f253e0 | |||
| 4eb974567f | |||
| 4f5bad5874 | |||
| 1c6e798632 | |||
| 3186fb42e7 | |||
| 4b7185fbb0 | |||
| f0d8555a8a | |||
| f338dfcb12 | |||
| 188d781b0d | |||
| adb1f07fd0 | |||
| 82e51be096 | |||
| d7f6d12f59 | |||
| 0db57fbff1 | |||
| b1bdc19afb | |||
| b75a95a708 | |||
| c3fb5dcd5f | |||
| 21911f93d1 | |||
| f7c96d81e9 | |||
| 9b2040059d | |||
| 08f190fc6e | |||
| 4d1b0fe301 | |||
| f139db07d9 | |||
|
|
73a4129000 | ||
| 1ddf797e28 | |||
|
|
18280f0478 | ||
|
|
4348705a0a | ||
| 5a1b92547d | |||
|
|
9e44713985 | ||
|
|
e93837fef7 | ||
| 6006c9d0e8 | |||
|
|
c93543186a | ||
|
|
e1b74d7a36 |
@@ -4,6 +4,9 @@ set -e
|
||||
|
||||
echo "Running pre-push hook:"
|
||||
|
||||
echo "Executing: cargo fmt"
|
||||
cargo fmt
|
||||
|
||||
echo "Executing: make lint"
|
||||
make lint
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ set -e
|
||||
|
||||
echo "Running pre-push hook:"
|
||||
|
||||
echo "Executing: cargo fmt --check"
|
||||
cargo fmt --check
|
||||
|
||||
echo "Executing: make lint"
|
||||
make lint
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ name: Check
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
# ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel
|
||||
# and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
@@ -24,14 +22,18 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
|
||||
- name: Run cargo fmt
|
||||
run: cargo fmt -- --check
|
||||
|
||||
- name: Cache Cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
clippy:
|
||||
name: ${{ matrix.toolchain }} / clippy
|
||||
runs-on: ubuntu-latest
|
||||
@@ -45,12 +47,15 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
|
||||
- name: Run clippy action
|
||||
uses: clechasseur/rs-clippy-check@v3
|
||||
|
||||
- name: Cache Cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
doc:
|
||||
@@ -61,8 +66,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust nightly
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
|
||||
- name: Run cargo doc
|
||||
run: cargo doc --no-deps --all-features
|
||||
env:
|
||||
@@ -73,9 +80,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install 1.82.0
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: 1.82.0
|
||||
|
||||
- name: cargo +1.82.0 check
|
||||
run: cargo check
|
||||
|
||||
@@ -18,7 +18,8 @@ on:
|
||||
- major
|
||||
|
||||
jobs:
|
||||
bump:
|
||||
bump-version:
|
||||
name: bump-version
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Configure SSH for Git
|
||||
@@ -70,9 +71,11 @@ jobs:
|
||||
- name: Get the new version tag
|
||||
id: version
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
NEW_TAG=$(cz version --project)
|
||||
echo "New version: $NEW_TAG"
|
||||
echo "version=$NEW_TAG" >> $GITHUB_ENV
|
||||
echo "$NEW_TAG" > artifacts/release-version
|
||||
|
||||
- name: Get the previous version tag
|
||||
id: prev_version
|
||||
@@ -85,19 +88,8 @@ jobs:
|
||||
id: changelog
|
||||
run: |
|
||||
changelog=$(conventional-changelog -p angular -i CHANGELOG.md -s --from ${{ env.prev_version }} --to ${{ env.version }})
|
||||
echo "$changelog" > changelog.md
|
||||
echo "changelog_body=$(cat changelog.md)" >> $GITHUB_ENV
|
||||
|
||||
- name: Create a GitHub Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: v${{ env.version }}
|
||||
name: "Release v${{ env.version }}"
|
||||
body: ${{ env.changelog_body }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
echo "$changelog" > artifacts/changelog.md
|
||||
echo "changelog_body=$(cat artifacts/changelog.md)" >> $GITHUB_ENV
|
||||
|
||||
- name: Push changes
|
||||
env:
|
||||
@@ -105,10 +97,37 @@ jobs:
|
||||
run: |
|
||||
git push origin --follow-tags
|
||||
|
||||
release-crate:
|
||||
needs: bump
|
||||
name: Release Crate
|
||||
runs-on: ubuntu-latest
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: artifacts
|
||||
|
||||
build-release-artifacts:
|
||||
name: build-release
|
||||
needs: [bump-version]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
# prettier-ignore
|
||||
job:
|
||||
- { name: "macOS-arm64", os: "macOS-latest", target: "aarch64-apple-darwin", artifact_suffix: "macos-arm64", use-cross: true }
|
||||
- { name: "macOS-amd64", os: "macOS-latest", target: "x86_64-apple-darwin", artifact_suffix: "macos" }
|
||||
- { name: "windows-amd64", os: "windows-latest", target: "x86_64-pc-windows-msvc", artifact_suffix: "windows" }
|
||||
- { name: "windows-aarch64", os: "windows-latest", target: "aarch64-pc-windows-msvc", artifact_suffix: "windows-aarch64", use-cross: true }
|
||||
- { name: "linux-gnu", os: "ubuntu-latest", target: "x86_64-unknown-linux-gnu", artifact_suffix: "linux" }
|
||||
- { name: "linux-musl", os: "ubuntu-latest", target: "x86_64-unknown-linux-musl", artifact_suffix: "linux-musl", use-cross: true, }
|
||||
- { name: "linux-aarch64-gnu", os: "ubuntu-latest", target: "aarch64-unknown-linux-gnu", artifact_suffix: "aarch64-gnu", use-cross: true, test-bin: "--bin managarr" }
|
||||
- { name: "linux-aarch64-musl", os: "ubuntu-latest", target: "aarch64-unknown-linux-musl", artifact_suffix: "aarch64-musl", use-cross: true, test-bin: "--bin managarr" }
|
||||
- { name: "linux-arm-gnu", os: "ubuntu-latest", target: "arm-unknown-linux-gnueabi", artifact_suffix: "armv6-gnu", use-cross: true, test-bin: "--bin managarr" }
|
||||
- { name: "linux-arm-musl", os: "ubuntu-latest", target: "arm-unknown-linux-musleabihf", artifact_suffix: "armv6-musl", use-cross: true, test-bin: "--bin managarr" }
|
||||
- { name: "linux-armv7-gnu", os: "ubuntu-latest", target: "armv7-unknown-linux-gnueabihf", artifact_suffix: "armv7-gnu", use-cross: true, test-bin: "--bin managarr" }
|
||||
- { name: "linux-armv7-musl", os: "ubuntu-latest", target: "armv7-unknown-linux-musleabihf", artifact_suffix: "armv7-musl", use-cross: true, test-bin: "--bin managarr" }
|
||||
rust: [stable]
|
||||
|
||||
steps:
|
||||
- name: Check if actor is repository owner
|
||||
if: ${{ github.actor != github.repository_owner }}
|
||||
@@ -116,12 +135,366 @@ jobs:
|
||||
echo "You are not authorized to run this workflow."
|
||||
exit 1
|
||||
|
||||
- name: Checkout
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- uses: katyo/publish-crates@v2
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Ensure repository is up-to-date
|
||||
run: |
|
||||
git fetch --all
|
||||
git pull
|
||||
|
||||
- uses: actions/cache@v3
|
||||
name: Cache Cargo registry
|
||||
with:
|
||||
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
- uses: actions/cache@v3
|
||||
if: startsWith(matrix.job.name, 'linux-')
|
||||
with:
|
||||
path: ~/.cargo/bin
|
||||
key: ${{ runner.os }}-cargo-bin-${{ hashFiles('.github/workflows/release.yml') }}
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
name: Set Rust toolchain
|
||||
with:
|
||||
targets: ${{ matrix.job.target }}
|
||||
|
||||
- uses: taiki-e/setup-cross-toolchain-action@v1
|
||||
with:
|
||||
# NB: sets CARGO_BUILD_TARGET evar - do not need --target flag in build
|
||||
target: ${{ matrix.job.target }}
|
||||
|
||||
- uses: taiki-e/install-action@cross
|
||||
if: ${{ matrix.job.use-cross }}
|
||||
|
||||
- name: Installing needed Ubuntu dependencies
|
||||
if: matrix.job.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
case ${{ matrix.job.target }} in
|
||||
arm*-linux-*) sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
||||
aarch64-*-linux-*) sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
||||
esac
|
||||
|
||||
- name: Build
|
||||
run: cargo build --release --verbose --target=${{ matrix.job.target }} --locked
|
||||
|
||||
- name: Verify file
|
||||
shell: bash
|
||||
run: |
|
||||
file target/${{ matrix.job.target }}/release/managarr
|
||||
|
||||
- name: Test
|
||||
if: matrix.job.target != 'aarch64-apple-darwin' && matrix.job.target != 'aarch64-pc-windows-msvc'
|
||||
run: cargo test --release --verbose --target=${{ matrix.job.target }} ${{ matrix.job.test-bin }}
|
||||
|
||||
- name: Packaging final binary (Windows)
|
||||
if: matrix.job.os == 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
cd target/${{ matrix.job.target }}/release
|
||||
BINARY_NAME=managarr.exe
|
||||
if [ "${{ matrix.job.target }}" != "aarch64-pc-windows-msvc" ]; then
|
||||
# strip the binary
|
||||
strip $BINARY_NAME
|
||||
fi
|
||||
RELEASE_NAME=managarr-${{ matrix.job.artifact_suffix }}
|
||||
mkdir -p artifacts
|
||||
tar czvf $RELEASE_NAME.tar.gz $BINARY_NAME
|
||||
# create sha checksum files
|
||||
certutil -hashfile $RELEASE_NAME.tar.gz sha256 | grep -E [A-Fa-f0-9]{64} > $RELEASE_NAME.sha256
|
||||
echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Packaging final binary (macOS and Linux)
|
||||
if: matrix.job.os != 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
# set the right strip executable
|
||||
STRIP="strip";
|
||||
case ${{ matrix.job.target }} in
|
||||
arm*-linux-*) STRIP="arm-linux-gnueabihf-strip" ;;
|
||||
aarch64-*-linux-*) STRIP="aarch64-linux-gnu-strip" ;;
|
||||
esac;
|
||||
cd target/${{ matrix.job.target }}/release
|
||||
BINARY_NAME=managarr
|
||||
# strip the binary
|
||||
"$STRIP" "$BINARY_NAME"
|
||||
RELEASE_NAME=managarr-${{ matrix.job.artifact_suffix }}
|
||||
tar czvf $RELEASE_NAME.tar.gz $BINARY_NAME
|
||||
# create sha checksum files
|
||||
shasum -a 256 $RELEASE_NAME.tar.gz > $RELEASE_NAME.sha256
|
||||
echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Add artifacts
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
cp target/${{ matrix.job.target }}/release/${{ env.RELEASE_NAME }}.tar.gz artifacts/
|
||||
cp target/${{ matrix.job.target }}/release/${{ env.RELEASE_NAME }}.sha256 artifacts/
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: artifacts
|
||||
|
||||
publish-github-release:
|
||||
name: publish-github-release
|
||||
needs: [build-release-artifacts]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: artifacts
|
||||
|
||||
- name: Ensure repository is up-to-date
|
||||
run: |
|
||||
git fetch --all
|
||||
git pull
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
release_version="$(cat ./artifacts/release-version)"
|
||||
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
|
||||
changelog_body="$(cat ./artifacts/changelog.md)"
|
||||
echo "changelog_body=$(cat artifacts/changelog.md)" >> $GITHUB_ENV
|
||||
|
||||
- name: Create a GitHub Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
files: |
|
||||
artifacts/managarr-macos-arm64.tar.gz
|
||||
artifacts/managarr-macos-arm64.sha256
|
||||
artifacts/managarr-macos.tar.gz
|
||||
artifacts/managarr-macos.sha256
|
||||
artifacts/managarr-windows.tar.gz
|
||||
artifacts/managarr-windows.sha256
|
||||
artifacts/managarr-windows-aarch64.tar.gz
|
||||
artifacts/managarr-windows-aarch64.sha256
|
||||
artifacts/managarr-linux.tar.gz
|
||||
artifacts/managarr-linux.sha256
|
||||
artifacts/managarr-linux-musl.tar.gz
|
||||
artifacts/managarr-linux-musl.sha256
|
||||
artifacts/managarr-aarch64-gnu.tar.gz
|
||||
artifacts/managarr-aarch64-gnu.sha256
|
||||
artifacts/managarr-aarch64-musl.tar.gz
|
||||
artifacts/managarr-aarch64-musl.sha256
|
||||
artifacts/managarr-armv6-gnu.tar.gz
|
||||
artifacts/managarr-armv6-gnu.sha256
|
||||
artifacts/managarr-armv6-musl.tar.gz
|
||||
artifacts/managarr-armv6-musl.sha256
|
||||
artifacts/managarr-armv7-gnu.tar.gz
|
||||
artifacts/managarr-armv7-gnu.sha256
|
||||
artifacts/managarr-armv7-musl.tar.gz
|
||||
artifacts/managarr-armv7-musl.sha256
|
||||
tag_name: v${{ env.RELEASE_VERSION }}
|
||||
name: "v${{ env.RELEASE_VERSION }}"
|
||||
body: ${{ env.changelog_body }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: artifacts
|
||||
|
||||
# publish-chocolatey-package:
|
||||
# needs: [publish-github-release]
|
||||
# name: Publish Chocolatey Package
|
||||
# runs-on: windows-latest
|
||||
# steps:
|
||||
# - name: Checkout repository
|
||||
# uses: actions/checkout@v4
|
||||
# with:
|
||||
# fetch-depth: 1
|
||||
|
||||
# - name: Get release artifacts
|
||||
# uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# name: artifacts
|
||||
# path: artifacts
|
||||
|
||||
# - name: Set release assets and version
|
||||
# shell: pwsh
|
||||
# run: |
|
||||
# # Read the first column from the SHA256 file
|
||||
# $windows_sha = Get-Content ./artifacts/managarr-windows.sha256 | ForEach-Object { $_.Split(' ')[0] }
|
||||
# Add-Content -Path $env:GITHUB_ENV -Value "WINDOWS_SHA=$windows_sha"
|
||||
|
||||
# # Read the release version from the release-version file
|
||||
# $release_version = Get-Content ./artifacts/release-version
|
||||
# Add-Content -Path $env:GITHUB_ENV -Value "RELEASE_VERSION=$release_version"
|
||||
|
||||
# - name: Validate release environment variables
|
||||
# run: |
|
||||
# echo "Release SHA windows: ${{ env.WINDOWS_SHA }}"
|
||||
# echo "Release version: ${{ env.RELEASE_VERSION }}"
|
||||
|
||||
# - name: Package and Publish package to Chocolatey
|
||||
# run: |
|
||||
# mkdir ./deployment/chocolatey/tools
|
||||
# # Run packaging script
|
||||
# python "./deployment/chocolatey/packager.py" ${{ env.RELEASE_VERSION }} "./deployment/chocolatey/managarr.nuspec.template" "./deployment/chocolatey/managarr.nuspec" ${{ env.WINDOWS_SHA }}
|
||||
# python "./deployment/chocolatey/packager.py" ${{ env.RELEASE_VERSION }} "./deployment/chocolatey/chocolateyinstall.ps1.template" "./deployment/chocolatey/tools/chocolateyinstall.ps1" ${{ env.WINDOWS_SHA }}
|
||||
|
||||
# # Publish to Chocolatey
|
||||
# cd ./deployment/chocolatey
|
||||
# choco pack
|
||||
# echo y | choco install managarr -dv -s .
|
||||
# $version = managarr --version
|
||||
# $version = $version -replace " ", "."
|
||||
# choco push $version.nupkg -s https://push.chocolatey.org/ --api-key ${{ secrets.CHOCOLATEY_API_KEY }};
|
||||
|
||||
# publish-homebrew-formula:
|
||||
# needs: [publish-github-release]
|
||||
# name: Update Homebrew formulas
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout repository
|
||||
# uses: actions/checkout@v4
|
||||
# with:
|
||||
# fetch-depth: 1
|
||||
|
||||
# - name: Get release artifacts
|
||||
# uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# name: artifacts
|
||||
# path: artifacts
|
||||
|
||||
# - name: Set release assets and version
|
||||
# shell: bash
|
||||
# run: |
|
||||
# # Set environment variables
|
||||
# macos_sha="$(cat ./artifacts/managarr-macos.sha256 | awk '{print $1}')"
|
||||
# echo "MACOS_SHA=$macos_sha" >> $GITHUB_ENV
|
||||
# macos_sha_arm="$(cat ./artifacts/managarr-macos-arm64.sha256 | awk '{print $1}')"
|
||||
# echo "MACOS_SHA_ARM=$macos_sha_arm" >> $GITHUB_ENV
|
||||
# linux_sha="$(cat ./artifacts/managarr-linux-musl.sha256 | awk '{print $1}')"
|
||||
# echo "LINUX_SHA=$linux_sha" >> $GITHUB_ENV
|
||||
# release_version="$(cat ./artifacts/release-version)"
|
||||
# echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
|
||||
|
||||
# - name: Validate release environment variables
|
||||
# run: |
|
||||
# echo "Release SHA macos: ${{ env.MACOS_SHA }}"
|
||||
# echo "Release SHA macos-arm: ${{ env.MACOS_SHA_ARM }}"
|
||||
# echo "Release SHA linux musl: ${{ env.LINUX_SHA }}"
|
||||
# echo "Release version: ${{ env.RELEASE_VERSION }}"
|
||||
|
||||
# - name: Execute Homebrew packaging script
|
||||
# run: |
|
||||
# # run packaging script
|
||||
# python "./deployment/homebrew/packager.py" ${{ env.RELEASE_VERSION }} "./deployment/homebrew/managarr.rb.template" "./managarr.rb" ${{ env.MACOS_SHA }} ${{ env.MACOS_SHA_ARM }} ${{ env.LINUX_SHA }}
|
||||
|
||||
# - name: Push changes to Homebrew tap
|
||||
# env:
|
||||
# TOKEN: ${{ secrets.MANAGARR_GITHUB_TOKEN }}
|
||||
# run: |
|
||||
# # push to Git
|
||||
# git config --global user.name "Dark-Alex-17"
|
||||
# git config --global user.email "alex.j.tusa@gmail.com"
|
||||
# git clone https://Dark-Alex-17:${{ secrets.MANAGARR_GITHUB_TOKEN }}@github.com/Dark-Alex-17/homebrew-managarr.git
|
||||
# rm homebrew-managarr/Formula/managarr.rb
|
||||
# cp managarr.rb homebrew-managarr/Formula
|
||||
# cd homebrew-managarr
|
||||
# git add .
|
||||
# git diff-index --quiet HEAD || git commit -am "Update formula for Managarr release ${{ env.RELEASE_VERSION }}"
|
||||
# git push https://$TOKEN@github.com/Dark-Alex-17/homebrew-managarr.git
|
||||
|
||||
# publish-docker-image:
|
||||
# needs: [publish-github-release]
|
||||
# name: Publishing Docker image to Docker Hub
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout repository
|
||||
# uses: actions/checkout@v4
|
||||
# with:
|
||||
# fetch-depth: 1
|
||||
|
||||
# - name: Get release artifacts
|
||||
# uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# name: artifacts
|
||||
# path: artifacts
|
||||
|
||||
# - name: Set version variable
|
||||
# run: |
|
||||
# version="$(cat artifacts/release-version)"
|
||||
# echo "version=$version" >> $GITHUB_ENV
|
||||
|
||||
# - name: Validate release environment variables
|
||||
# run: |
|
||||
# echo "Release version: ${{ env.version }}"
|
||||
|
||||
# - name: Set up QEMU
|
||||
# uses: docker/setup-qemu-action@v3
|
||||
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v3
|
||||
|
||||
# - name: Login to Docker Hub
|
||||
# uses: docker/login-action@v3
|
||||
# with:
|
||||
# username: ${{ secrets.DOCKER_USERNAME }}
|
||||
# password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
# - name: Push to Docker Hub
|
||||
# uses: docker/build-push-action@v5
|
||||
# with:
|
||||
# context: .
|
||||
# file: Dockerfile
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
# push: true
|
||||
# tags: darkalex17/managarr:latest, darkalex17/managarr:${{ env.version }}
|
||||
|
||||
# publish-crate:
|
||||
# needs: publish-github-release
|
||||
# name: Publish Crate
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Check if actor is repository owner
|
||||
# if: ${{ github.actor != github.repository_owner }}
|
||||
# run: |
|
||||
# echo "You are not authorized to run this workflow."
|
||||
# exit 1
|
||||
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
# with:
|
||||
# fetch-depth: 0
|
||||
|
||||
# - name: Ensure repository is up-to-date
|
||||
# run: |
|
||||
# git fetch --all
|
||||
# git pull
|
||||
|
||||
# - uses: actions/cache@v3
|
||||
# name: Cache Cargo registry
|
||||
# with:
|
||||
# path: ~/.cargo/registry
|
||||
# key: ${{ runner.os }}-cargo-registry-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
# - uses: actions/cache@v3
|
||||
# with:
|
||||
# path: ~/.cargo/bin
|
||||
# key: ${{ runner.os }}-cargo-bin-${{ hashFiles('.github/workflows/release.yml') }}
|
||||
|
||||
# - name: Install Rust stable
|
||||
# uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
# - uses: katyo/publish-crates@v2
|
||||
# with:
|
||||
# registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
|
||||
@@ -34,16 +34,20 @@ jobs:
|
||||
toolchain: [stable, beta]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install ${{ matrix.toolchain }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
|
||||
# enable this ci template to run regardless of whether the lockfile is checked in or not
|
||||
- name: cargo generate-lockfile
|
||||
if: hashFiles('Cargo.lock') == ''
|
||||
run: cargo generate-lockfile
|
||||
|
||||
- name: cargo test --locked
|
||||
run: cargo test --locked --all-features --all-targets
|
||||
|
||||
minimal-versions:
|
||||
# This action chooses the oldest version of the dependencies permitted by Cargo.toml to ensure
|
||||
# that this crate is compatible with the minimal version that this crate and its dependencies
|
||||
@@ -71,18 +75,25 @@ jobs:
|
||||
name: ubuntu / stable / minimal-versions
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install nightly for -Zdirect-minimal-versions
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
|
||||
- name: rustup default stable
|
||||
run: rustup default stable
|
||||
|
||||
- name: cargo update -Zdirect-minimal-versions
|
||||
run: cargo +nightly update -Zdirect-minimal-versions
|
||||
|
||||
- name: cargo test
|
||||
run: cargo test --locked --all-features --all-targets
|
||||
|
||||
- name: Cache Cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
os-check:
|
||||
# run cargo test on mac and windows
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -100,15 +111,20 @@ jobs:
|
||||
# if: runner.os == 'Windows'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: cargo generate-lockfile
|
||||
if: hashFiles('Cargo.lock') == ''
|
||||
run: cargo generate-lockfile
|
||||
|
||||
- name: cargo test
|
||||
run: cargo test --locked --all-features --all-targets
|
||||
|
||||
- name: Cache Cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
coverage:
|
||||
# use llvm-cov to build and collect coverage and outputs in a format that
|
||||
# is compatible with codecov.io
|
||||
@@ -136,21 +152,28 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: llvm-tools-preview
|
||||
|
||||
- name: cargo install cargo-llvm-cov
|
||||
uses: taiki-e/install-action@cargo-llvm-cov
|
||||
|
||||
- name: cargo generate-lockfile
|
||||
if: hashFiles('Cargo.lock') == ''
|
||||
run: cargo generate-lockfile
|
||||
|
||||
- name: cargo llvm-cov
|
||||
run: cargo llvm-cov --locked --all-features --lcov --output-path lcov.info
|
||||
|
||||
- name: Record Rust version
|
||||
run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Cache Cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
|
||||
@@ -5,6 +5,177 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v0.4.2 (2024-12-21)
|
||||
|
||||
### Fix
|
||||
|
||||
- Revert failed release [skip ci]
|
||||
- **sonarr**: Pass the series ID alongside all UpdateAndScan events when publishing to the networking channel
|
||||
- **sonarr**: pass the series ID alongside all TriggerAutomaticSeriesSearch events when publishing to the networking channel
|
||||
- **sonarr**: Pass the series ID and season number alongside all TriggerAutomaticSeasonSearch events when publishing to the networking channel
|
||||
- **sonarr**: Pass the episode ID alongside all TriggerAutomaticEpisodeSearch events when publishing to the networking channel
|
||||
- **sonarr**: Pass the episode ID alongside all ToggleEpisodeMonitoring events when publishing to the networking channel
|
||||
- **sonarr**: Pass the series ID and season number alongside all toggle season monitoring events when publishing to the networking channel
|
||||
- **sonarr**: Pass the indexer ID directly alongside all TestIndexer events when publishing to the networking channel
|
||||
- **sonarr**: Provide the task name directly alongside all StartTask events when publishing to the networking channel
|
||||
- **sonarr**: Pass the search query directly to the networking channel when searching for a new series
|
||||
- **sonarr**: Pass the series ID alongside all GetSeriesHistory events when publishing to the networking channel
|
||||
- **sonarr**: Pass the series ID alongside all GetSeriesDetails events when publishing to the networking channel
|
||||
- **sonarr**: Pass series ID and season number alongside all ManualSeasonSearch events when publishing to the networking channel
|
||||
- **sonarr**: Provide the series ID and season number alongside all GetSeasonHistory events when publishing to the networking channel
|
||||
- **sonarr**: Pass the episode ID alongside all ManualEpisodeSearch events when publishing to the networking channel
|
||||
- **sonarr**: Pass events alongside all GetLogs events when publishing to the networking channel
|
||||
- **sonarr**: Pass the episode ID alongside all GetEpisodeHistory events when publishing to the networking channel
|
||||
- **sonarr**: Pass series ID alongside all GetEpisodeFiles events when publishing to the networking channel
|
||||
- **sonarr**: Pass series ID alognside all GetEpisodes events when publishing to the networking channel
|
||||
- **sonarr**: Pass the episode ID alongside all GetEpisodeDetails events when publishing to the networking channel
|
||||
- **sonarr**: Pass history events alongside all GetHistory events when publishing to the networking channel
|
||||
- **sonarr**: Construct and pass edit series parameters alongside all EditSeries events when publishing to the networking channel
|
||||
- **sonarr**: Construct and pass edit indexer parameters alongside all EditIndexer events when publishing to the networking channel
|
||||
- **sonarr**: Construct and pass edit all indexer settings alongside all EditAllIndexerSettings events when publishing to the networking channel
|
||||
- **sonarr**: Construct and pass delete series params alongside all DeleteSeries events when publishing to the networking channel
|
||||
- **sonarr**: Corrected a bug that would cause a crash if a user spams the ESC key while searching for a new series and the search results are still loading
|
||||
- **sonarr**: Pass the root folder ID alongside all DeleteRootFolder events when publishing to the networking channel
|
||||
- **sonarr**: Pass the indexer ID alongside all DeleteIndexer events when publishing to the networking channel
|
||||
- **sonarr**: Pass the episode file ID alongside all DeleteEpisodeFile events when publishing to the networking channel
|
||||
- **sonarr**: Pass the download ID alongside all DeleteDownload events published to the networking channel
|
||||
- **sonarr**: Pass the blocklist item ID alongside the DeleteBlocklistItem event when publishing to the networking channel
|
||||
- **sonarr**: Construct and pass the add series body alongside AddSeries events when publishing to the networking channel
|
||||
- **sonarr**: Construct and pass the AddRootFolderBody alongside all AddRootFolder events when publishing to the networking channel
|
||||
- **radarr**: Pass the movie ID alongside all UpdateAndScan events published to the networking channel
|
||||
- **radarr**: Provide the movie ID alongside all TriggerAutomaticMovieSearch events when publishing to the networking channel
|
||||
- **radarr**: Pass in the indexer id with all TestIndexer events when publishing to the networking channel
|
||||
- **radarr**: Pass in the task name alongside the StartTask event when publishing to the networking channel
|
||||
- **radarr**: Pass in the search query for the SearchNewMovie event when publishing to the networking channel
|
||||
- **radarr**: Pass in the movie ID alongside the GetReleases event when publishing to the networking channel
|
||||
- **radarr**: Pass in the movie ID alongside the GetMovieHistory event when publishing to the networking channel
|
||||
- **radarr**: Pass the movie ID in alongside the GetMovieDetaisl event when publishing to the networking channel
|
||||
- **radarr**: Provide the movie id alongside the GetMovieCredits event when publishing to the networking channel
|
||||
- **radarr**: Pass the number of log events to fetch in with the GetLogs event when publishing to the networking channel
|
||||
- **radarr**: Construct and pass the edit movie parameters alongside the EditMovie event when publishing to the networking channel
|
||||
- **radarr**: Construct and pass params when publishing the EditIndexer event to the networking channel
|
||||
- **radarr**: Construct and pass edit collection parameters alongside the EditCollection event when publishing to the networking channel
|
||||
- **radarr**: Build and pass the edit indexer settings body with the EditAllIndexerSettings event when publishing to the networking channel
|
||||
- **radarr**: Send the parameters alongside the DownloadRelease event when publishing to the networking channel
|
||||
- **radarr**: Pass the root folder ID in with the DeleteRootFolder event when publishing to the networking channel
|
||||
- Pass the delete movie params in with the DeleteMovie event when publishing to the networking channel
|
||||
- Pass the indexer ID in with the DeleteIndexer event when sending to the networking channel
|
||||
- Pass the download ID directly in the DeleteDownload event when publishing into the networking channel
|
||||
- Blocklist Item ID passed in the DeleteBlocklistItem event when sent to the networking channel
|
||||
- AddRootFolderBody now constructed prior to AddRootFolder event being sent down the network channel
|
||||
- Cancel all requests when switching Servarr tabs to both improve performance and fix issue #15
|
||||
- **add_movie_handler_tests**: Added in a forgotten test for the build_add_movie_body function
|
||||
- Missing tagged version of docker builds in release flow
|
||||
- AddMovie Radarr event is now populated in the dispatch thread before being sent to the network thread
|
||||
- dynamically load servarrs in UI based on what configs are provided
|
||||
|
||||
## v0.4.1 (2024-12-14)
|
||||
|
||||
### Feat
|
||||
|
||||
- **docs**: Updated the README with new screeshots for the Sonarr release
|
||||
- **handler**: Support for toggling the monitoring status of a specified episode in the Sonarr UI
|
||||
- **handlers**: Support for toggling the monitoring status of a season in the Sonarr UI
|
||||
- **keybindings**: Added a new keybinding for toggling the monitoring of a highlighted table item
|
||||
- **cli**: Support for toggling monitoring on a specific episode in Sonarr
|
||||
- **network**: Support for toggling the monitoring status of an episode in Sonarr
|
||||
- **cli**: Support for toggling monitoring for a specific season in Sonarr
|
||||
- **network**: Support for toggling monitoring/unmonitoring a season
|
||||
- **handlers**: Support for the episode details popup
|
||||
- **ui**: Support for the episode details UI
|
||||
- **handler**: Full handler support for the Season details UI in Sonarr
|
||||
- **ui**: Sonarr support for viewing season details
|
||||
- **cli**: Sonarr support for fetching a list of all episode files for a given series ID
|
||||
- **app**: Dispatch support for Season Details to fetch both the current downloads as well as the episode files to match qualities to them
|
||||
- **network**: Support for fetching all episode files for a given series
|
||||
- **app**: Model and modal support for the season and episode details popups
|
||||
- **cli**: Sonarr support for fetching season history events
|
||||
- **network**: Sonarr support for fetching season history
|
||||
- **ui**: Sonarr support for the series details popup
|
||||
- **ui**: Sonarr support for editing a series from within the series details popup
|
||||
- **ui**: Sonarr Series details UI is now available
|
||||
- **ui**: Full Sonarr system tab support
|
||||
- **handler**: System handler support for Sonarr
|
||||
- **ui**: Full Sonarr support for the indexer tab
|
||||
- **ui**: Support for modifying the indexer priority in Radarr
|
||||
- **handler**: Full indexer tab handler support
|
||||
- **ui**: Root folder tab support
|
||||
- **handlers**: Support for root folder actions
|
||||
- **ui**: History tab support
|
||||
- **handler**: History tab support
|
||||
- **ui**: Blocklist UI support
|
||||
- **handler**: Wired in the blocklist handler to the main handlers
|
||||
- **handler**: Blocklist handler support
|
||||
- **ui**: Downloads tab support
|
||||
- **handler**: Download tab support
|
||||
- **ui**: Edit series support
|
||||
- **handler**: Edit series support
|
||||
- **ui**: Add series support Sonarr
|
||||
- **handler**: Add series support for Sonarr
|
||||
- **ui**: Delete a series
|
||||
- **handler**: Support for deleting a series in Sonarr
|
||||
- **ui**: Support for the Series table
|
||||
- **handlers**: Sonarr key support for the Series table
|
||||
- **models**: Added the necessary contextual help and tabs for the Sonarr UI
|
||||
- **ui**: Initial UI support for switching to Sonarr tabs
|
||||
- **app**: Dispatch support for all relevant Sonarr blocks
|
||||
|
||||
### Fix
|
||||
|
||||
- **blocklist_handler**: Fixed a breaking change between Sonarr v3 and v4
|
||||
- **style**: Addressed linter complaints on formatting
|
||||
- Implemented a handful of fixes that are breaking changes between Sonarr v3 and v4
|
||||
- **handler_tests**: Fixed all delegation tests to have initial conditions set properly
|
||||
- **ui**: Fixed a bug that requires a minimum height for all popups so all error messages and other simple popups appear
|
||||
- **handler**: Fixed a bug in the history handler that wouldn't reset the filter or search if a user hit 'esc' on the History tab
|
||||
- **ui**: Fix the System Details Tasks popup to be navigable in both Sonarr and Radarr
|
||||
- **ui**: Fixed a potential rare bug in the UI where the application would panic if the height of the downloads window is 0.
|
||||
|
||||
### Refactor
|
||||
|
||||
- **network**: Changed the toggle episode monitoring handler to simply return empty since the response is always empty from Sonarr
|
||||
- **ui**: Tweaked some of the color schemes in the series table
|
||||
- Fixed a couple of typos in some test function names
|
||||
- **handlers**: Refactored the handlers to all use the handle_table_events macro when appropriate and created tests for the macro so tests don't have to be duplicated across each handler
|
||||
- **ui**: Simplified the popup delegation so all future UI is easier to implement
|
||||
- **indexers_handler**: Use the new handle_table_events macro
|
||||
- **root_folders_handler**: Use the new handle_table_events macro
|
||||
- **blocklist_handler**: Use the new handle_table_events macro
|
||||
- **downloads_handler**: Use the new handle_table_events macro
|
||||
- **collection_details_handler**: use the new handle_table_events macro
|
||||
- **collections_handler**: Use the new handle_table_events macro
|
||||
- **movie_details_handler**: Use the new handle_table_events macro
|
||||
- **library_handler**: Radarr use the new handle_table_events macro
|
||||
- **indexers_handler**: Use the new handle_table_events macro
|
||||
- **indexers_handler**: Use the new handle_table_events macro
|
||||
- **root_folder_handler**: Use the new handle_table_events macro
|
||||
- **history_handler**: Use the new handle_table_event macro
|
||||
- **blocklist_handler**: Use the new handle_table_events macro
|
||||
- **downloads_handler**: Use the new handle_table_events macro
|
||||
- **series_details_handler**: Use the new handle_table_events macro
|
||||
- **handler**: Created a macro to handle all table key events to reduce code duplication and make future implementations faster; Only refactored the Sonarr library to use it thus far
|
||||
- **ui**: all table search and filter functionality is now available directly through the ManagarrTable widget to make life easier moving forward
|
||||
- **keys**: Created a auto search key instead of reusing the existing search key to make things easier
|
||||
- **BlockSelectionState**: Refactored so selection of blocks in 2x2 grids is more intuitive and added left() and right() methods to aid this effort.
|
||||
|
||||
### Perf
|
||||
|
||||
- Improved performance by optimizing API calls to only refresh when the tick prompts a refresh. All UI is now significantly faster
|
||||
|
||||
## v0.3.7 (2024-11-26)
|
||||
|
||||
### Fix
|
||||
|
||||
- **ci**: Forgot to also pull in the most recent changes [skip ci]
|
||||
|
||||
## v0.3.6 (2024-11-26)
|
||||
|
||||
### Fix
|
||||
|
||||
- **ci**: Ensure the Release Crate job fetches the most recent commit before publishing the crate [skip ci]
|
||||
|
||||
## v0.3.4 (2024-11-26)
|
||||
|
||||
## v0.3.3 (2024-11-26)
|
||||
|
||||
### Fix
|
||||
|
||||
@@ -28,9 +28,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
@@ -98,9 +98,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.93"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -142,7 +142,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -195,9 +195,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22"
|
||||
checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
@@ -218,9 +218,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
|
||||
[[package]]
|
||||
name = "cargo-husky"
|
||||
@@ -245,9 +245,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.1"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
|
||||
checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -266,9 +266,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
@@ -281,9 +281,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.21"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -291,9 +291,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.21"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -303,9 +303,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.38"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01"
|
||||
checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
@@ -319,14 +319,14 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
@@ -336,12 +336,12 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -372,15 +372,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.8"
|
||||
version = "0.15.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"unicode-width 0.1.14",
|
||||
"windows-sys 0.52.0",
|
||||
"once_cell",
|
||||
"unicode-width 0.2.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -455,7 +455,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -466,7 +466,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -489,12 +489,30 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_setters"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "destructure_traitobject"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
|
||||
|
||||
[[package]]
|
||||
name = "deunicode"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
@@ -557,7 +575,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -580,9 +598,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
@@ -601,19 +619,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.9"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
@@ -623,9 +641,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
@@ -713,7 +731,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -811,17 +829,11 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@@ -887,9 +899,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.5.1"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f"
|
||||
checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -908,9 +920,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.3"
|
||||
version = "0.27.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
|
||||
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http",
|
||||
@@ -1096,7 +1108,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1128,9 +1140,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -1157,16 +1169,16 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "instability"
|
||||
version = "0.3.3"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b829f37dead9dc39df40c2d3376c179fdfd2ac771f53f55d3c30dc096a3c0c6e"
|
||||
checksum = "898e106451f7335950c9cc64f8ec67b5f65698679ac67ed00619aeef14e1cf75"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"indoc",
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1198,10 +1210,11 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.72"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
||||
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -1213,9 +1226,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.165"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb4d3d38eab6c5239a362fa8bae48c03baf980a6e7079f063942d563ef3533e"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@@ -1303,7 +1316,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "managarr"
|
||||
version = "0.3.3"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
@@ -1319,6 +1332,8 @@ dependencies = [
|
||||
"crossterm",
|
||||
"ctrlc",
|
||||
"derivative",
|
||||
"derive_setters",
|
||||
"deunicode",
|
||||
"dirs-next",
|
||||
"human-panic",
|
||||
"indicatif",
|
||||
@@ -1329,6 +1344,8 @@ dependencies = [
|
||||
"managarr-tree-widget",
|
||||
"mockall",
|
||||
"mockito",
|
||||
"openssl",
|
||||
"paste",
|
||||
"pretty_assertions",
|
||||
"ratatui",
|
||||
"regex",
|
||||
@@ -1368,20 +1385,19 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
@@ -1411,7 +1427,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1499,9 +1515,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.5"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -1535,7 +1551,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1544,6 +1560,15 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.4.1+3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.104"
|
||||
@@ -1552,6 +1577,7 @@ checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
@@ -1573,9 +1599,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.8.2"
|
||||
version = "3.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092"
|
||||
checksum = "e5ca711d8b83edbb00b44d504503cd247c9c0bd8b0fa2694f2a1a3d8165379ce"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
@@ -1658,9 +1684,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.2"
|
||||
version = "3.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97"
|
||||
checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"difflib",
|
||||
@@ -1669,15 +1695,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "predicates-core"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931"
|
||||
checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
|
||||
|
||||
[[package]]
|
||||
name = "predicates-tree"
|
||||
version = "1.0.11"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13"
|
||||
checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
|
||||
dependencies = [
|
||||
"predicates-core",
|
||||
"termtree",
|
||||
@@ -1774,9 +1800,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -1911,7 +1937,7 @@ dependencies = [
|
||||
"regex",
|
||||
"relative-path",
|
||||
"rustc_version",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
@@ -1932,22 +1958,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.41"
|
||||
version = "0.38.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.18"
|
||||
version = "0.23.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f"
|
||||
checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustls-pki-types",
|
||||
@@ -1967,9 +1993,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.10.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
@@ -2024,9 +2050,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.12.1"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2"
|
||||
checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -2034,15 +2060,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.215"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -2059,20 +2085,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.133"
|
||||
version = "1.0.134"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -2173,9 +2199,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.7"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
@@ -2224,7 +2250,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2246,9 +2272,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.89"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2272,7 +2298,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2311,9 +2337,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.4.1"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
|
||||
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
@@ -2332,7 +2358,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2347,9 +2373,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
version = "0.3.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"libc",
|
||||
@@ -2378,9 +2404,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.41.1"
|
||||
version = "1.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
|
||||
checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -2402,7 +2428,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2417,20 +2443,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.0"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.12"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
|
||||
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -2481,9 +2506,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
@@ -2645,9 +2670,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -2656,36 +2681,36 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
||||
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.45"
|
||||
version = "0.4.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
|
||||
checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
||||
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -2693,28 +2718,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.72"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
|
||||
checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -2986,7 +3011,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -3008,7 +3033,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3028,7 +3053,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -3057,5 +3082,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "managarr"
|
||||
version = "0.3.3"
|
||||
version = "0.4.2"
|
||||
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||
description = "A TUI and CLI to manage your Servarrs"
|
||||
keywords = ["managarr", "ratatui", "dashboard", "servarr", "tui"]
|
||||
@@ -36,7 +36,10 @@ strum = { version = "0.26.3", features = ["derive"] }
|
||||
strum_macros = "0.26.4"
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
tokio-util = "0.7.8"
|
||||
ratatui = { version = "0.29.0", features = ["all-widgets"] }
|
||||
ratatui = { version = "0.29.0", features = [
|
||||
"all-widgets",
|
||||
"unstable-widget-ref",
|
||||
] }
|
||||
urlencoding = "2.1.2"
|
||||
clap = { version = "4.5.20", features = ["derive", "cargo", "env"] }
|
||||
clap_complete = "4.5.33"
|
||||
@@ -47,6 +50,10 @@ async-trait = "0.1.83"
|
||||
dirs-next = "2.0.0"
|
||||
managarr-tree-widget = "0.24.0"
|
||||
indicatif = "0.17.9"
|
||||
derive_setters = "0.1.6"
|
||||
deunicode = "1.6.0"
|
||||
paste = "1.0.15"
|
||||
openssl = { version = "0.10.68", features = ["vendored"] }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0.16"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM clux/muslrust:stable AS builder
|
||||
FROM rust:1.82 AS builder
|
||||
WORKDIR /usr/src
|
||||
|
||||
# Download and compile Rust dependencies in an empty project and cache as a separate Docker layer
|
||||
@@ -6,17 +6,17 @@ RUN USER=root cargo new --bin managarr-temp
|
||||
|
||||
WORKDIR /usr/src/managarr-temp
|
||||
COPY Cargo.* .
|
||||
RUN cargo build --release --target x86_64-unknown-linux-musl
|
||||
RUN cargo build --release
|
||||
# remove src from empty project
|
||||
RUN rm -r src
|
||||
COPY src ./src
|
||||
# remove previous deps
|
||||
RUN rm ./target/x86_64-unknown-linux-musl/release/deps/managarr*
|
||||
RUN rm ./target/release/deps/managarr*
|
||||
|
||||
RUN --mount=type=cache,target=/volume/target \
|
||||
--mount=type=cache,target=/root/.cargo/registry \
|
||||
cargo build --release --target x86_64-unknown-linux-musl --bin managarr
|
||||
RUN mv target/x86_64-unknown-linux-musl/release/managarr .
|
||||
cargo build --release --bin managarr
|
||||
RUN mv target/release/managarr .
|
||||
|
||||
FROM debian:stable-slim
|
||||
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||

|
||||
[](https://codecov.io/gh/Dark-Alex-17/managarr)
|
||||

|
||||
[](https://github.com/Dark-Alex-17/managarr/releases)
|
||||

|
||||
|
||||
Managarr is a TUI and CLI to help you manage your HTPC (Home Theater PC). Built with 🤎 in Rust!
|
||||
|
||||

|
||||

|
||||
|
||||
## What Servarrs are supported?
|
||||
|
||||
@@ -32,7 +34,10 @@ Simply run the following command to start a demo:
|
||||
curl https://raw.githubusercontent.com/Dark-Alex-17/managarr-demo/main/managarr-demo.sh > /tmp/managarr-demo.sh && bash /tmp/managarr-demo.sh
|
||||
```
|
||||
|
||||
Alternatively, you can try out the demo container without downloading anything by visiting the [Managarr Demo site](https://managarr-demo.alexjclarke.com).
|
||||
|
||||
## Installation
|
||||
|
||||
### Cargo
|
||||
If you have Cargo installed, then you can install Managarr from Crates.io:
|
||||
|
||||
@@ -46,13 +51,74 @@ cargo install --locked managarr
|
||||
### Docker
|
||||
Run Managarr as a docker container by mounting your `config.yml` file to `/root/.config/managarr/config.yml`. For example:
|
||||
```shell
|
||||
docker run --rm -it -v ~/.config/managarr/config.yml:/root/.config/managarr/config.yml darkalex17/managarr
|
||||
docker run --rm -it -v /home/aclarke/.config/managarr/config.yml:/root/.config/managarr/config.yml darkalex17/managarr:latest
|
||||
```
|
||||
|
||||
You can also clone this repo and run `make docker` to build a docker image locally and run it using the above command.
|
||||
|
||||
Please note that you will need to create and popular your configuration file first before starting the container. Otherwise, the container will fail to start.
|
||||
|
||||
**Note:** If you run into errors using relative file paths when mounting the volume with the configuration file, try using an absolute path.
|
||||
|
||||
### Homebrew (Mac and Linux)
|
||||
To install Managarr from Homebrew, install the Managarr tap and then you'll be able to install Managarr:
|
||||
|
||||
```shell
|
||||
brew tap Dark-Alex-17/managarr
|
||||
brew install managarr
|
||||
|
||||
# If you need to be more specific, use the following:
|
||||
brew install Dark-Alex-17/managarr/managarr
|
||||
```
|
||||
|
||||
To upgrade to a newer version of Managarr:
|
||||
```shell
|
||||
brew upgrade managarr
|
||||
```
|
||||
|
||||
### Chocolatey (Windows)
|
||||
The Managarr Chocolatey package is located [here](https://community.chocolatey.org/packages/managarr). Please note that validation
|
||||
of Chocolatey packages take quite some time, and thus the package may not be available immediately after a new release.
|
||||
|
||||
```powershell
|
||||
choco install managarr
|
||||
|
||||
# Some newer releases may require a version number, so you can specify it like so:
|
||||
choco install managarr --version=0.4.1
|
||||
```
|
||||
|
||||
To upgrade to the latest and greatest version of Managarr:
|
||||
```powershell
|
||||
choco upgrade managarr
|
||||
|
||||
# To upgrade to a specific version:
|
||||
choco upgrade managarr --version=0.4.2
|
||||
```
|
||||
|
||||
### Manual
|
||||
Binaries are available on the [releases](https://github.com/Dark-Alex-17/managarr/releases) page for the following platforms:
|
||||
|
||||
| Platform | Architecture(s) |
|
||||
|----------------|----------------------------|
|
||||
| macOS | x86_64, arm64 |
|
||||
| Linux GNU/MUSL | x86_64,armv6,armv7,aarch64 |
|
||||
| Windows | x86_64,aarch64 |
|
||||
|
||||
#### Windows Instructions
|
||||
To use a binary from the releases page on Windows, do the following:
|
||||
|
||||
1. Download the latest binary [binary](https://github.com/Dark-Alex-17/managarr/releases) for your OS.
|
||||
2. Use 7-Zip or TarTool to unpack the Tar file.
|
||||
3. Run the executable `managarr.exe`!
|
||||
|
||||
#### Linux/MacOS Instructions
|
||||
To use a binary from the releases page on Linux/MacOS, do the following:
|
||||
|
||||
1. Download the latest binary [binary](https://github.com/Dark-Alex-17/managarr/releases) for your OS.
|
||||
2. `cd` to the directory where you downloaded the binary.
|
||||
3. Extract the binary with `tar -C /usr/local/bin -xzf managarr-<arch>.tar.gz` (NB: This may require `sudo`)
|
||||
4. Now you can run `managarr`!
|
||||
|
||||
## Features
|
||||
Key:
|
||||
|
||||
@@ -88,22 +154,21 @@ Key:
|
||||
|
||||
| TUI | CLI | Feature |
|
||||
|-----|-----|--------------------------------------------------------------------------------------------------------------------|
|
||||
| 🕒 | ✅ | View your library, downloads, blocklist, episodes |
|
||||
| 🕒 | ✅ | View details of a specific series, or episode including description, history, downloaded file info, or the credits |
|
||||
| 🕒 | ✅ | View your host and security configs from the CLI to programmatically fetch the API token, among other settings |
|
||||
| 🕒 | ✅ | Search your library |
|
||||
| 🕒 | ✅ | Add series to your library |
|
||||
| 🕒 | ✅ | Delete series, downloads, indexers, root folders, and episode files |
|
||||
| 🕒 | ✅ | Mark history events as failed |
|
||||
| 🕒 | ✅ | Trigger automatic searches for series, seasons, or episodes |
|
||||
| 🕒 | ✅ | Trigger refresh and disk scan for series and downloads |
|
||||
| 🕒 | ✅ | Manually search for series, seasons, or episodes |
|
||||
| 🕒 | ✅ | Edit your series and indexers |
|
||||
| 🕒 | ✅ | Manage your tags |
|
||||
| 🕒 | ✅ | Manage your root folders |
|
||||
| 🕒 | ✅ | Manage your blocklist |
|
||||
| 🕒 | ✅ | View and browse logs, tasks, events queues, and updates |
|
||||
| 🕒 | ✅ | Manually trigger scheduled tasks |
|
||||
| ✅ | ✅ | View your library, downloads, blocklist, episodes |
|
||||
| ✅ | ✅ | View details of a specific series, or episode including description, history, downloaded file info, or the credits |
|
||||
| 🚫 | ✅ | View your host and security configs from the CLI to programmatically fetch the API token, among other settings |
|
||||
| ✅ | ✅ | Search your library |
|
||||
| ✅ | ✅ | Add series to your library |
|
||||
| ✅ | ✅ | Delete series, downloads, indexers, root folders, and episode files |
|
||||
| ✅ | ✅ | Trigger automatic searches for series, seasons, or episodes |
|
||||
| ✅ | ✅ | Trigger refresh and disk scan for series and downloads |
|
||||
| ✅ | ✅ | Manually search for series, seasons, or episodes |
|
||||
| ✅ | ✅ | Edit your series and indexers |
|
||||
| ✅ | ✅ | Manage your tags |
|
||||
| ✅ | ✅ | Manage your root folders |
|
||||
| ✅ | ✅ | Manage your blocklist |
|
||||
| ✅ | ✅ | View and browse logs, tasks, events queues, and updates |
|
||||
| ✅ | ✅ | Manually trigger scheduled tasks |
|
||||
|
||||
### Readarr
|
||||
|
||||
@@ -141,7 +206,7 @@ To see all available commands, simply run `managarr --help`:
|
||||
|
||||
```shell
|
||||
$ managarr --help
|
||||
managarr 0.3.0
|
||||
managarr 0.4.0
|
||||
Alex Clarke <alex.j.tusa@gmail.com>
|
||||
|
||||
A TUI and CLI to manage your Servarrs
|
||||
@@ -186,6 +251,8 @@ Commands:
|
||||
start-task Start the specified Sonarr task
|
||||
test-indexer Test the indexer with the given ID. Note that a successful test returns an empty JSON body; i.e. '{}'
|
||||
test-all-indexers Test all Sonarr indexers
|
||||
toggle-episode-monitoring Toggle monitoring for the specified episode
|
||||
toggle-season-monitoring Toggle monitoring for the specified season that corresponds to the specified series ID
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Options:
|
||||
@@ -275,21 +342,28 @@ Managarr supports using environment variables on startup so you don't have to al
|
||||
|-----------------------------------------|--------------------------------------------------------------------------------|----------------------------------|
|
||||
| `MANAGARR_CONFIG_FILE` | Set the path to the config file | `--config` |
|
||||
| `MANAGARR_DISABLE_SPINNER` | Disable the CLI spinner (this can be useful when scripting and parsing output) | `--disable-spinner` |
|
||||
|-----------------------------------------|--------------------------------------------------------------------------------|----------------------------------|
|
||||
|
||||
## Track My Progress for the Beta release (With Sonarr Support!)
|
||||
Progress for the beta release can be followed on my [Wekan Board](https://wekan.alexjclarke.com/b/dHoGjBb44MHM9HSv4/managarr)
|
||||
with all items tagged `Beta`.
|
||||
## Track What I'm Currently Working On
|
||||
To see what feature(s) I'm currently working on, check out my [Wekan Board](https://wekan.alexjclarke.com/b/dHoGjBb44MHM9HSv4/managarr).
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
### Radarr
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### Sonarr
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### General
|
||||

|
||||

|
||||
|
||||
## Dependencies
|
||||
* [ratatui](https://github.com/tui-rs-revival/ratatui)
|
||||
@@ -301,7 +375,7 @@ with all items tagged `Beta`.
|
||||
|
||||
## Servarr Requirements
|
||||
* [Radarr >= 5.3.6.8612](https://radarr.video/docs/api/)
|
||||
* [Sonarr >= v3](https://sonarr.tv/docs/api/)
|
||||
* [Sonarr >= v4](https://sonarr.tv/docs/api/)
|
||||
* [Readarr v1](https://readarr.com/docs/api/)
|
||||
* [Lidarr v1](https://lidarr.audio/docs/api/)
|
||||
* [Whisparr >= v3](https://whisparr.com/docs/api/)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
$ErrorActionPreference = 'Stop';
|
||||
|
||||
$PackageName = 'managarr'
|
||||
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
|
||||
$url64 = 'https://github.com/Dark-Alex-17/managarr/releases/download/v$version/managarr-windows.tar.gz'
|
||||
$checksum64 = '$hash_64'
|
||||
|
||||
$packageArgs = @{
|
||||
packageName = $packageName
|
||||
softwareName = $packageName
|
||||
unzipLocation = $toolsDir
|
||||
fileType = 'exe'
|
||||
url = $url64
|
||||
checksum = $checksum64
|
||||
checksumType = 'sha256'
|
||||
|
||||
}
|
||||
Install-ChocolateyZipPackage @packageArgs
|
||||
$File = Get-ChildItem -File -Path $env:ChocolateyInstall\lib\$packageName\tools\ -Filter *.tar
|
||||
Get-ChocolateyUnzip -fileFullPath $File.FullName -destination $env:ChocolateyInstall\lib\$packageName\tools\
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Read this before creating packages: https://chocolatey.org/docs/create-packages -->
|
||||
<!-- It is especially important to read the above link to understand additional requirements when publishing packages to the community feed aka dot org (https://chocolatey.org/packages). -->
|
||||
|
||||
<!-- Test your packages in a test environment: https://github.com/chocolatey/chocolatey-test-environment -->
|
||||
|
||||
<!--
|
||||
This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Reference. Chocolatey uses a special version of NuGet.Core that allows us to do more than was initially possible. As such there are certain things to be aware of:
|
||||
|
||||
* the package xmlns schema url may cause issues with nuget.exe
|
||||
* Any of the following elements can ONLY be used by choco tools - projectSourceUrl, docsUrl, mailingListUrl, bugTrackerUrl, packageSourceUrl, provides, conflicts, replaces
|
||||
* nuget.exe can still install packages with those elements but they are ignored. Any authoring tools or commands will error on those elements
|
||||
-->
|
||||
|
||||
<!-- You can embed software files directly into packages, as long as you are not bound by distribution rights. -->
|
||||
<!-- * If you are an organization making private packages, you probably have no issues here -->
|
||||
<!-- * If you are releasing to the community feed, you need to consider distribution rights. -->
|
||||
<!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. -->
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
||||
<metadata>
|
||||
<!-- == PACKAGE SPECIFIC SECTION == -->
|
||||
<id>managarr</id>
|
||||
<version>$version</version>
|
||||
|
||||
<!-- == SOFTWARE SPECIFIC SECTION == -->
|
||||
<!-- This section is about the software itself -->
|
||||
<title>Managarr</title>
|
||||
<authors>Alex Clarke</authors>
|
||||
<projectUrl>https://github.com/Dark-Alex-17/managarr</projectUrl>
|
||||
<licenseUrl>https://github.com/Dark-Alex-17/managarr/blob/main/LICENSE</licenseUrl>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<projectSourceUrl>https://github.com/Dark-Alex-17/managarr</projectSourceUrl>
|
||||
<docsUrl>https://github.com/Dark-Alex-17/managarr/blob/main/README.md</docsUrl>
|
||||
<bugTrackerUrl>https://github.com/Dark-Alex-17/managarr/issues</bugTrackerUrl>
|
||||
<tags>cli cross-platform terminal servarr tui sonarr radarr rust</tags>
|
||||
<summary>A TUI and CLI for managing *arr servers.</summary>
|
||||
<description>
|
||||
A TUI and CLI for managing *arr servers. Build with love in Rust!
|
||||
|
||||
**Usage**
|
||||
To use, run `managarr` in a terminal.
|
||||
|
||||
For more [documentation and usage](https://github.com/Dark-Alex-17/managarr/blob/main/README.md), see the [official repo](https://github.com/Dark-Alex-17/managarr).
|
||||
|
||||
</description>
|
||||
<releaseNotes>https://github.com/Dark-Alex-17/managarr/releases/tag/v$version/</releaseNotes>
|
||||
</metadata>
|
||||
<files>
|
||||
<!-- this section controls what actually gets packaged into the Chocolatey package -->
|
||||
<file src="tools\**" target="tools" />
|
||||
<!--Building from Linux? You may need this instead: <file src="tools/**" target="tools" />-->
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,28 @@
|
||||
import hashlib
|
||||
import sys
|
||||
from string import Template
|
||||
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
args = sys.argv
|
||||
version = args[1].replace("v", "")
|
||||
template_file_path = args[2]
|
||||
generated_file_path = args[3]
|
||||
|
||||
# Deployment files
|
||||
hash_64 = args[4].strip()
|
||||
|
||||
print("Generating formula")
|
||||
print(" VERSION: %s" % version)
|
||||
print(" TEMPLATE PATH: %s" % template_file_path)
|
||||
print(" SAVING AT: %s" % generated_file_path)
|
||||
print(" HASH: %s" % hash_64)
|
||||
|
||||
with open(template_file_path, "r", encoding="utf-8") as template_file:
|
||||
template = Template(template_file.read())
|
||||
substitute = template.safe_substitute(version=version, hash_64=hash_64)
|
||||
print("\n================== Generated package file ==================\n")
|
||||
print(substitute)
|
||||
print("\n============================================================\n")
|
||||
|
||||
with open(generated_file_path, "w", encoding="utf-8") as generated_file:
|
||||
generated_file.write(substitute)
|
||||
@@ -0,0 +1,24 @@
|
||||
# Documentation: https://docs.brew.sh/Formula-Cookbook
|
||||
# https://rubydoc.brew.sh/Formula
|
||||
class Managarr < Formula
|
||||
desc "A fast and simple dashboard for Kubernetes written in Rust"
|
||||
homepage "https://github.com/Dark-Alex-17/managarr"
|
||||
if OS.mac? and Hardware::CPU.arm?
|
||||
url "https://github.com/Dark-Alex-17/managarr/releases/download/v$version/managarr-macos-arm64.tar.gz"
|
||||
sha256 "$hash_mac_arm"
|
||||
elsif OS.mac? and Hardware::CPU.intel?
|
||||
url "https://github.com/Dark-Alex-17/managarr/releases/download/v$version/managarr-macos.tar.gz"
|
||||
sha256 "$hash_mac"
|
||||
else
|
||||
url "https://github.com/Dark-Alex-17/managarr/releases/download/v$version/managarr-linux-musl.tar.gz"
|
||||
sha256 "$hash_linux"
|
||||
end
|
||||
version "$version"
|
||||
license "MIT"
|
||||
|
||||
def install
|
||||
bin.install "managarr"
|
||||
ohai "You're done! Run with \"managarr\""
|
||||
ohai "For runtime flags, see \"managarr --help\""
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
import hashlib
|
||||
import sys
|
||||
from string import Template
|
||||
|
||||
args = sys.argv
|
||||
version = args[1]
|
||||
template_file_path = args[2]
|
||||
generated_file_path = args[3]
|
||||
|
||||
# Deployment files
|
||||
hash_mac = args[4].strip()
|
||||
hash_mac_arm = args[5].strip()
|
||||
hash_linux = args[6].strip()
|
||||
|
||||
print("Generating formula")
|
||||
print(" VERSION: %s" % version)
|
||||
print(" TEMPLATE PATH: %s" % template_file_path)
|
||||
print(" SAVING AT: %s" % generated_file_path)
|
||||
print(" MAC HASH: %s" % hash_mac)
|
||||
print(" MAC ARM HASH: %s" % hash_mac_arm)
|
||||
print(" LINUX HASH: %s" % hash_linux)
|
||||
|
||||
with open(template_file_path, "r") as template_file:
|
||||
template = Template(template_file.read())
|
||||
substitute = template.safe_substitute(version=version, hash_mac=hash_mac, hash_mac_arm=hash_mac_arm, hash_linux=hash_linux)
|
||||
print("\n================== Generated package file ==================\n")
|
||||
print(substitute)
|
||||
print("\n============================================================\n")
|
||||
|
||||
with open(generated_file_path, "w") as generated_file:
|
||||
generated_file.write(substitute)
|
||||
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 374 KiB After Width: | Height: | Size: 374 KiB |
|
Before Width: | Height: | Size: 382 KiB After Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 305 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 203 KiB |
@@ -1,24 +1,84 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::models::Route;
|
||||
use anyhow::anyhow;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::app::context_clues::{build_context_clue_string, SERVARR_CONTEXT_CLUES};
|
||||
use crate::app::{App, AppConfig, Data, ServarrConfig, DEFAULT_ROUTE};
|
||||
use crate::app::{App, AppConfig, Data, ServarrConfig};
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
|
||||
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData};
|
||||
use crate::models::{HorizontallyScrollableText, TabRoute};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::network::NetworkEvent;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
#[rstest]
|
||||
fn test_app_new(
|
||||
#[values(ActiveRadarrBlock::default(), ActiveSonarrBlock::default())] servarr: impl Into<Route>
|
||||
+ Copy,
|
||||
) {
|
||||
let (title, config) = match servarr.into() {
|
||||
Route::Radarr(_, _) => (
|
||||
"Radarr",
|
||||
AppConfig {
|
||||
radarr: Some(ServarrConfig::default()),
|
||||
..AppConfig::default()
|
||||
},
|
||||
),
|
||||
Route::Sonarr(_, _) => (
|
||||
"Sonarr",
|
||||
AppConfig {
|
||||
sonarr: Some(ServarrConfig::default()),
|
||||
..AppConfig::default()
|
||||
},
|
||||
),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let tab_route = |title: &'static str| TabRoute {
|
||||
title,
|
||||
route: servarr.into(),
|
||||
help: format!(
|
||||
"<↑↓> scroll | ←→ change tab | {} ",
|
||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
||||
),
|
||||
contextual_help: None,
|
||||
};
|
||||
|
||||
let app = App::new(
|
||||
mpsc::channel::<NetworkEvent>(500).0,
|
||||
config,
|
||||
CancellationToken::new(),
|
||||
);
|
||||
|
||||
assert!(app.navigation_stack.is_empty());
|
||||
assert_eq!(app.get_current_route(), servarr.into());
|
||||
assert!(app.network_tx.is_some());
|
||||
assert!(!app.cancellation_token.is_cancelled());
|
||||
assert!(app.is_first_render);
|
||||
assert_eq!(app.error, HorizontallyScrollableText::default());
|
||||
assert_eq!(app.server_tabs.index, 0);
|
||||
assert_eq!(app.server_tabs.tabs, vec![tab_route(title)]);
|
||||
assert_eq!(app.tick_until_poll, 400);
|
||||
assert_eq!(app.ticks_until_scroll, 4);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
assert!(!app.is_loading);
|
||||
assert!(!app.is_routing);
|
||||
assert!(!app.should_refresh);
|
||||
assert!(!app.should_ignore_quit_key);
|
||||
assert!(!app.cli_mode);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_app_default() {
|
||||
let app = App::default();
|
||||
|
||||
assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]);
|
||||
assert!(app.navigation_stack.is_empty());
|
||||
assert!(app.network_tx.is_none());
|
||||
assert!(!app.cancellation_token.is_cancelled());
|
||||
assert!(app.is_first_render);
|
||||
assert_eq!(app.error, HorizontallyScrollableText::default());
|
||||
assert_eq!(app.server_tabs.index, 0);
|
||||
assert_eq!(
|
||||
@@ -36,7 +96,10 @@ mod tests {
|
||||
TabRoute {
|
||||
title: "Sonarr",
|
||||
route: ActiveSonarrBlock::Series.into(),
|
||||
help: format!("{} ", build_context_clue_string(&SERVARR_CONTEXT_CLUES)),
|
||||
help: format!(
|
||||
"<↑↓> scroll | ←→ change tab | {} ",
|
||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
||||
),
|
||||
contextual_help: None,
|
||||
},
|
||||
]
|
||||
@@ -54,15 +117,13 @@ mod tests {
|
||||
#[test]
|
||||
fn test_navigation_stack_methods() {
|
||||
let mut app = App::default();
|
||||
let default_route = app.server_tabs.tabs.first().unwrap().route;
|
||||
|
||||
assert_eq!(app.get_current_route(), &DEFAULT_ROUTE);
|
||||
assert_eq!(app.get_current_route(), default_route);
|
||||
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Downloads.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
|
||||
assert!(app.is_routing);
|
||||
|
||||
app.is_routing = false;
|
||||
@@ -70,20 +131,20 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Collections.into()
|
||||
ActiveRadarrBlock::Collections.into()
|
||||
);
|
||||
assert!(app.is_routing);
|
||||
|
||||
app.is_routing = false;
|
||||
app.pop_navigation_stack();
|
||||
|
||||
assert_eq!(app.get_current_route(), &DEFAULT_ROUTE);
|
||||
assert_eq!(app.get_current_route(), default_route);
|
||||
assert!(app.is_routing);
|
||||
|
||||
app.is_routing = false;
|
||||
app.pop_navigation_stack();
|
||||
|
||||
assert_eq!(app.get_current_route(), &DEFAULT_ROUTE);
|
||||
assert_eq!(app.get_current_route(), default_route);
|
||||
assert!(app.is_routing);
|
||||
}
|
||||
|
||||
@@ -120,19 +181,23 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_reset() {
|
||||
let radarr_data = RadarrData {
|
||||
version: "test".into(),
|
||||
..RadarrData::default()
|
||||
};
|
||||
let sonarr_data = SonarrData {
|
||||
version: "test".into(),
|
||||
..SonarrData::default()
|
||||
};
|
||||
let data = Data {
|
||||
radarr_data,
|
||||
sonarr_data,
|
||||
};
|
||||
let mut app = App {
|
||||
tick_count: 2,
|
||||
error: "Test error".to_owned().into(),
|
||||
data: Data {
|
||||
radarr_data: RadarrData {
|
||||
version: "test".to_owned(),
|
||||
..RadarrData::default()
|
||||
},
|
||||
sonarr_data: SonarrData {
|
||||
version: "test".to_owned(),
|
||||
..SonarrData::default()
|
||||
},
|
||||
},
|
||||
is_first_render: false,
|
||||
data,
|
||||
..App::default()
|
||||
};
|
||||
|
||||
@@ -140,6 +205,7 @@ mod tests {
|
||||
|
||||
assert_eq!(app.tick_count, 0);
|
||||
assert_eq!(app.error, HorizontallyScrollableText::default());
|
||||
assert!(app.is_first_render);
|
||||
assert!(app.data.radarr_data.version.is_empty());
|
||||
assert!(app.data.sonarr_data.version.is_empty());
|
||||
}
|
||||
@@ -188,12 +254,13 @@ mod tests {
|
||||
let mut app = App {
|
||||
tick_until_poll: 2,
|
||||
network_tx: Some(sync_network_tx),
|
||||
is_first_render: true,
|
||||
..App::default()
|
||||
};
|
||||
|
||||
assert_eq!(app.tick_count, 0);
|
||||
|
||||
app.on_tick(true).await;
|
||||
app.on_tick().await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
@@ -219,6 +286,14 @@ mod tests {
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetStatus.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovies.into()
|
||||
@@ -237,7 +312,7 @@ mod tests {
|
||||
..App::default()
|
||||
};
|
||||
|
||||
app.on_tick(false).await;
|
||||
app.on_tick().await;
|
||||
assert!(!app.is_routing);
|
||||
}
|
||||
|
||||
@@ -250,7 +325,7 @@ mod tests {
|
||||
..App::default()
|
||||
};
|
||||
|
||||
app.on_tick(false).await;
|
||||
app.on_tick().await;
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,77 @@ pub fn build_context_clue_string(context_clues: &[(KeyBinding, &str)]) -> String
|
||||
.join(" | ")
|
||||
}
|
||||
|
||||
pub static SERVARR_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
(DEFAULT_KEYBINDINGS.tab, "change servarr"),
|
||||
pub static SERVARR_CONTEXT_CLUES: [ContextClue; 3] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.next_servarr,
|
||||
DEFAULT_KEYBINDINGS.next_servarr.desc,
|
||||
),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.previous_servarr,
|
||||
DEFAULT_KEYBINDINGS.previous_servarr.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.quit, DEFAULT_KEYBINDINGS.quit.desc),
|
||||
];
|
||||
|
||||
pub static BARE_POPUP_CONTEXT_CLUES: [ContextClue; 1] =
|
||||
[(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc)];
|
||||
|
||||
pub static BLOCKLIST_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||
(DEFAULT_KEYBINDINGS.clear, "clear blocklist"),
|
||||
];
|
||||
|
||||
pub static CONFIRMATION_PROMPT_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
(DEFAULT_KEYBINDINGS.confirm, "submit"),
|
||||
(DEFAULT_KEYBINDINGS.esc, "cancel"),
|
||||
];
|
||||
|
||||
pub static DOWNLOADS_CONTEXT_CLUES: [ContextClue; 3] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||
(DEFAULT_KEYBINDINGS.update, "update downloads"),
|
||||
];
|
||||
|
||||
pub static ROOT_FOLDERS_CONTEXT_CLUES: [ContextClue; 3] = [
|
||||
(DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc),
|
||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
];
|
||||
|
||||
pub static INDEXERS_CONTEXT_CLUES: [ContextClue; 6] = [
|
||||
(DEFAULT_KEYBINDINGS.submit, "edit indexer"),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.settings,
|
||||
DEFAULT_KEYBINDINGS.settings.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||
(DEFAULT_KEYBINDINGS.test, "test indexer"),
|
||||
(DEFAULT_KEYBINDINGS.test_all, "test all indexers"),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
];
|
||||
|
||||
pub static SYSTEM_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||
(DEFAULT_KEYBINDINGS.tasks, "open tasks"),
|
||||
(DEFAULT_KEYBINDINGS.events, "open events"),
|
||||
(DEFAULT_KEYBINDINGS.logs, "open logs"),
|
||||
(DEFAULT_KEYBINDINGS.update, "open updates"),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
];
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
mod test {
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
|
||||
use crate::app::context_clues::{BARE_POPUP_CONTEXT_CLUES, SERVARR_CONTEXT_CLUES};
|
||||
use crate::app::context_clues::{
|
||||
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
||||
DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES,
|
||||
SERVARR_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||
};
|
||||
use crate::app::{context_clues::build_context_clue_string, key_binding::DEFAULT_KEYBINDINGS};
|
||||
|
||||
#[test]
|
||||
@@ -24,8 +28,13 @@ mod test {
|
||||
|
||||
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.tab);
|
||||
assert_str_eq!(*description, "change servarr");
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.next_servarr);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.next_servarr.desc);
|
||||
|
||||
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.previous_servarr);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.previous_servarr.desc);
|
||||
|
||||
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||
|
||||
@@ -44,4 +53,160 @@ mod test {
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||
assert_eq!(bare_popup_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downloads_context_clues() {
|
||||
let mut downloads_context_clues_iter = DOWNLOADS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
|
||||
|
||||
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
|
||||
assert_str_eq!(*description, "update downloads");
|
||||
assert_eq!(downloads_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blocklist_context_clues() {
|
||||
let mut blocklist_context_clues_iter = BLOCKLIST_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "details");
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.clear);
|
||||
assert_str_eq!(*description, "clear blocklist");
|
||||
assert_eq!(blocklist_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_confirmation_prompt_context_clues() {
|
||||
let mut confirmation_prompt_context_clues_iter = CONFIRMATION_PROMPT_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.confirm);
|
||||
assert_str_eq!(*description, "submit");
|
||||
|
||||
let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, "cancel");
|
||||
assert_eq!(confirmation_prompt_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_context_clues() {
|
||||
let mut root_folders_context_clues_iter = ROOT_FOLDERS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.add);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.add.desc);
|
||||
|
||||
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
|
||||
|
||||
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
assert_eq!(root_folders_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_indexers_context_clues() {
|
||||
let mut indexers_context_clues_iter = INDEXERS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "edit indexer");
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.settings);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.settings.desc);
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test);
|
||||
assert_str_eq!(*description, "test indexer");
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test_all);
|
||||
assert_str_eq!(*description, "test all indexers");
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
assert_eq!(indexers_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_context_clues() {
|
||||
let mut system_context_clues_iter = SYSTEM_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.tasks);
|
||||
assert_str_eq!(*description, "open tasks");
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.events);
|
||||
assert_str_eq!(*description, "open events");
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.logs);
|
||||
assert_str_eq!(*description, "open logs");
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
|
||||
assert_str_eq!(*description, "open updates");
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
assert_eq!(system_context_clues_iter.next(), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,11 @@ generate_keybindings! {
|
||||
left,
|
||||
right,
|
||||
backspace,
|
||||
next_servarr,
|
||||
previous_servarr,
|
||||
clear,
|
||||
search,
|
||||
auto_search,
|
||||
settings,
|
||||
filter,
|
||||
sort,
|
||||
@@ -25,12 +28,12 @@ generate_keybindings! {
|
||||
tasks,
|
||||
test,
|
||||
test_all,
|
||||
toggle_monitoring,
|
||||
refresh,
|
||||
update,
|
||||
events,
|
||||
home,
|
||||
end,
|
||||
tab,
|
||||
delete,
|
||||
submit,
|
||||
confirm,
|
||||
@@ -69,16 +72,28 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
|
||||
key: Key::Backspace,
|
||||
desc: "backspace",
|
||||
},
|
||||
next_servarr: KeyBinding {
|
||||
key: Key::Tab,
|
||||
desc: "next servarr",
|
||||
},
|
||||
previous_servarr: KeyBinding {
|
||||
key: Key::BackTab,
|
||||
desc: "previous servarr",
|
||||
},
|
||||
clear: KeyBinding {
|
||||
key: Key::Char('c'),
|
||||
desc: "clear",
|
||||
},
|
||||
auto_search: KeyBinding {
|
||||
key: Key::Char('S'),
|
||||
desc: "auto search",
|
||||
},
|
||||
search: KeyBinding {
|
||||
key: Key::Char('s'),
|
||||
desc: "search",
|
||||
},
|
||||
settings: KeyBinding {
|
||||
key: Key::Char('s'),
|
||||
key: Key::Char('S'),
|
||||
desc: "settings",
|
||||
},
|
||||
filter: KeyBinding {
|
||||
@@ -113,6 +128,10 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
|
||||
key: Key::Char('T'),
|
||||
desc: "test all",
|
||||
},
|
||||
toggle_monitoring: KeyBinding {
|
||||
key: Key::Char('m'),
|
||||
desc: "toggle monitoring",
|
||||
},
|
||||
refresh: KeyBinding {
|
||||
key: Key::Ctrl('r'),
|
||||
desc: "refresh",
|
||||
@@ -129,10 +148,6 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
|
||||
key: Key::End,
|
||||
desc: "end",
|
||||
},
|
||||
tab: KeyBinding {
|
||||
key: Key::Tab,
|
||||
desc: "tab",
|
||||
},
|
||||
delete: KeyBinding {
|
||||
key: Key::Delete,
|
||||
desc: "delete",
|
||||
|
||||
@@ -13,9 +13,12 @@ mod test {
|
||||
#[case(DEFAULT_KEYBINDINGS.left, Key::Left, "left")]
|
||||
#[case(DEFAULT_KEYBINDINGS.right, Key::Right, "right")]
|
||||
#[case(DEFAULT_KEYBINDINGS.backspace, Key::Backspace, "backspace")]
|
||||
#[case(DEFAULT_KEYBINDINGS.next_servarr, Key::Tab, "next servarr")]
|
||||
#[case(DEFAULT_KEYBINDINGS.previous_servarr, Key::BackTab, "previous servarr")]
|
||||
#[case(DEFAULT_KEYBINDINGS.clear, Key::Char('c'), "clear")]
|
||||
#[case(DEFAULT_KEYBINDINGS.auto_search, Key::Char('S'), "auto search")]
|
||||
#[case(DEFAULT_KEYBINDINGS.search, Key::Char('s'), "search")]
|
||||
#[case(DEFAULT_KEYBINDINGS.settings, Key::Char('s'), "settings")]
|
||||
#[case(DEFAULT_KEYBINDINGS.settings, Key::Char('S'), "settings")]
|
||||
#[case(DEFAULT_KEYBINDINGS.filter, Key::Char('f'), "filter")]
|
||||
#[case(DEFAULT_KEYBINDINGS.sort, Key::Char('o'), "sort")]
|
||||
#[case(DEFAULT_KEYBINDINGS.edit, Key::Char('e'), "edit")]
|
||||
@@ -24,11 +27,11 @@ mod test {
|
||||
#[case(DEFAULT_KEYBINDINGS.tasks, Key::Char('t'), "tasks")]
|
||||
#[case(DEFAULT_KEYBINDINGS.test, Key::Char('t'), "test")]
|
||||
#[case(DEFAULT_KEYBINDINGS.test_all, Key::Char('T'), "test all")]
|
||||
#[case(DEFAULT_KEYBINDINGS.toggle_monitoring, Key::Char('m'), "toggle monitoring")]
|
||||
#[case(DEFAULT_KEYBINDINGS.refresh, Key::Ctrl('r'), "refresh")]
|
||||
#[case(DEFAULT_KEYBINDINGS.update, Key::Char('u'), "update")]
|
||||
#[case(DEFAULT_KEYBINDINGS.home, Key::Home, "home")]
|
||||
#[case(DEFAULT_KEYBINDINGS.end, Key::End, "end")]
|
||||
#[case(DEFAULT_KEYBINDINGS.tab, Key::Tab, "tab")]
|
||||
#[case(DEFAULT_KEYBINDINGS.delete, Key::Delete, "delete")]
|
||||
#[case(DEFAULT_KEYBINDINGS.submit, Key::Enter, "submit")]
|
||||
#[case(DEFAULT_KEYBINDINGS.confirm, Key::Ctrl('s'), "submit")]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::process;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use anyhow::{anyhow, Error};
|
||||
use colored::Colorize;
|
||||
use log::{debug, error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -21,13 +21,13 @@ pub mod context_clues;
|
||||
pub mod key_binding;
|
||||
mod key_binding_tests;
|
||||
pub mod radarr;
|
||||
|
||||
const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies, None);
|
||||
pub mod sonarr;
|
||||
|
||||
pub struct App<'a> {
|
||||
navigation_stack: Vec<Route>,
|
||||
network_tx: Option<Sender<NetworkEvent>>,
|
||||
cancellation_token: CancellationToken,
|
||||
pub cancellation_token: CancellationToken,
|
||||
pub is_first_render: bool,
|
||||
pub server_tabs: TabState,
|
||||
pub error: HorizontallyScrollableText,
|
||||
pub tick_until_poll: u64,
|
||||
@@ -48,10 +48,37 @@ impl<'a> App<'a> {
|
||||
config: AppConfig,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> Self {
|
||||
let mut server_tabs = Vec::new();
|
||||
|
||||
if config.radarr.is_some() {
|
||||
server_tabs.push(TabRoute {
|
||||
title: "Radarr",
|
||||
route: ActiveRadarrBlock::Movies.into(),
|
||||
help: format!(
|
||||
"<↑↓> scroll | ←→ change tab | {} ",
|
||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
||||
),
|
||||
contextual_help: None,
|
||||
});
|
||||
}
|
||||
|
||||
if config.sonarr.is_some() {
|
||||
server_tabs.push(TabRoute {
|
||||
title: "Sonarr",
|
||||
route: ActiveSonarrBlock::Series.into(),
|
||||
help: format!(
|
||||
"<↑↓> scroll | ←→ change tab | {} ",
|
||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
||||
),
|
||||
contextual_help: None,
|
||||
});
|
||||
}
|
||||
|
||||
App {
|
||||
network_tx: Some(network_tx),
|
||||
config,
|
||||
cancellation_token,
|
||||
server_tabs: TabState::new(server_tabs),
|
||||
..App::default()
|
||||
}
|
||||
}
|
||||
@@ -76,26 +103,26 @@ impl<'a> App<'a> {
|
||||
self.tick_count = 0;
|
||||
}
|
||||
|
||||
// Allowing this code for now since we'll eventually be implementing additional Servarr support and we'll need it then
|
||||
#[allow(dead_code)]
|
||||
pub fn reset(&mut self) {
|
||||
self.reset_tick_count();
|
||||
self.error = HorizontallyScrollableText::default();
|
||||
self.is_first_render = true;
|
||||
self.data = Data::default();
|
||||
}
|
||||
|
||||
pub fn handle_error(&mut self, error: anyhow::Error) {
|
||||
pub fn handle_error(&mut self, error: Error) {
|
||||
if self.error.text.is_empty() {
|
||||
self.error = error.to_string().into();
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn on_tick(&mut self, is_first_render: bool) {
|
||||
pub async fn on_tick(&mut self) {
|
||||
if self.tick_count % self.tick_until_poll == 0 || self.is_routing || self.should_refresh {
|
||||
if let Route::Radarr(active_radarr_block, _) = self.get_current_route() {
|
||||
self
|
||||
.radarr_on_tick(*active_radarr_block, is_first_render)
|
||||
.await;
|
||||
match self.get_current_route() {
|
||||
Route::Radarr(active_radarr_block, _) => self.radarr_on_tick(active_radarr_block).await,
|
||||
Route::Sonarr(active_sonarr_block, _) => self.sonarr_on_tick(active_sonarr_block).await,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.is_routing = false;
|
||||
@@ -112,7 +139,7 @@ impl<'a> App<'a> {
|
||||
|
||||
pub fn pop_navigation_stack(&mut self) {
|
||||
self.is_routing = true;
|
||||
if self.navigation_stack.len() > 1 {
|
||||
if !self.navigation_stack.is_empty() {
|
||||
self.navigation_stack.pop();
|
||||
}
|
||||
}
|
||||
@@ -130,18 +157,22 @@ impl<'a> App<'a> {
|
||||
self.push_navigation_stack(route);
|
||||
}
|
||||
|
||||
pub fn get_current_route(&self) -> &Route {
|
||||
self.navigation_stack.last().unwrap_or(&DEFAULT_ROUTE)
|
||||
pub fn get_current_route(&self) -> Route {
|
||||
*self
|
||||
.navigation_stack
|
||||
.last()
|
||||
.unwrap_or(&self.server_tabs.tabs.first().unwrap().route)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for App<'a> {
|
||||
fn default() -> Self {
|
||||
App {
|
||||
navigation_stack: vec![DEFAULT_ROUTE],
|
||||
navigation_stack: Vec::new(),
|
||||
network_tx: None,
|
||||
cancellation_token: CancellationToken::new(),
|
||||
error: HorizontallyScrollableText::default(),
|
||||
is_first_render: true,
|
||||
server_tabs: TabState::new(vec![
|
||||
TabRoute {
|
||||
title: "Radarr",
|
||||
@@ -155,7 +186,10 @@ impl<'a> Default for App<'a> {
|
||||
TabRoute {
|
||||
title: "Sonarr",
|
||||
route: ActiveSonarrBlock::Series.into(),
|
||||
help: format!("{} ", build_context_clue_string(&SERVARR_CONTEXT_CLUES)),
|
||||
help: format!(
|
||||
"<↑↓> scroll | ←→ change tab | {} ",
|
||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
||||
),
|
||||
contextual_help: None,
|
||||
},
|
||||
]),
|
||||
@@ -176,7 +210,7 @@ impl<'a> Default for App<'a> {
|
||||
#[derive(Default)]
|
||||
pub struct Data<'a> {
|
||||
pub radarr_data: RadarrData<'a>,
|
||||
pub sonarr_data: SonarrData,
|
||||
pub sonarr_data: SonarrData<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||
@@ -187,6 +221,13 @@ pub struct AppConfig {
|
||||
|
||||
impl AppConfig {
|
||||
pub fn validate(&self) {
|
||||
if self.radarr.is_none() && self.sonarr.is_none() {
|
||||
log_and_print_error(
|
||||
"No Servarr configuration provided in the specified configuration file".to_owned(),
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
if let Some(radarr_config) = &self.radarr {
|
||||
radarr_config.validate();
|
||||
}
|
||||
|
||||
@@ -17,11 +17,23 @@ impl<'a> App<'a> {
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::Collections => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetCollections.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetMovies.into())
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::CollectionDetails => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetTags.into())
|
||||
.await;
|
||||
self.is_loading = true;
|
||||
self.populate_movie_collection_table().await;
|
||||
self.is_loading = false;
|
||||
@@ -37,6 +49,12 @@ impl<'a> App<'a> {
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::Movies => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetTags.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetMovies.into())
|
||||
.await;
|
||||
@@ -45,6 +63,9 @@ impl<'a> App<'a> {
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::Indexers => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetTags.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetIndexers.into())
|
||||
.await;
|
||||
@@ -56,7 +77,9 @@ impl<'a> App<'a> {
|
||||
}
|
||||
ActiveRadarrBlock::TestIndexer => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::TestIndexer(None).into())
|
||||
.dispatch_network_event(
|
||||
RadarrEvent::TestIndexer(self.extract_radarr_indexer_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::TestAllIndexers => {
|
||||
@@ -72,7 +95,7 @@ impl<'a> App<'a> {
|
||||
.dispatch_network_event(RadarrEvent::GetQueuedEvents.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetLogs(None).into())
|
||||
.dispatch_network_event(RadarrEvent::GetLogs(500).into())
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::SystemUpdates => {
|
||||
@@ -82,17 +105,23 @@ impl<'a> App<'a> {
|
||||
}
|
||||
ActiveRadarrBlock::AddMovieSearchResults => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::SearchNewMovie(None).into())
|
||||
.dispatch_network_event(
|
||||
RadarrEvent::SearchNewMovie(self.extract_movie_search_query().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::MovieDetails | ActiveRadarrBlock::FileInfo => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetMovieDetails(None).into())
|
||||
.dispatch_network_event(
|
||||
RadarrEvent::GetMovieDetails(self.extract_movie_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::MovieHistory => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetMovieHistory(None).into())
|
||||
.dispatch_network_event(
|
||||
RadarrEvent::GetMovieHistory(self.extract_movie_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::Cast | ActiveRadarrBlock::Crew => {
|
||||
@@ -102,7 +131,9 @@ impl<'a> App<'a> {
|
||||
|| movie_details_modal.movie_crew.items.is_empty() =>
|
||||
{
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetMovieCredits(None).into())
|
||||
.dispatch_network_event(
|
||||
RadarrEvent::GetMovieCredits(self.extract_movie_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
_ => (),
|
||||
@@ -111,7 +142,7 @@ impl<'a> App<'a> {
|
||||
ActiveRadarrBlock::ManualSearch => match self.data.radarr_data.movie_details_modal.as_ref() {
|
||||
Some(movie_details_modal) if movie_details_modal.movie_releases.items.is_empty() => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetReleases(None).into())
|
||||
.dispatch_network_event(RadarrEvent::GetReleases(self.extract_movie_id().await).into())
|
||||
.await;
|
||||
}
|
||||
_ => (),
|
||||
@@ -119,11 +150,11 @@ impl<'a> App<'a> {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.check_for_prompt_action().await;
|
||||
self.check_for_radarr_prompt_action().await;
|
||||
self.reset_tick_count();
|
||||
}
|
||||
|
||||
async fn check_for_prompt_action(&mut self) {
|
||||
async fn check_for_radarr_prompt_action(&mut self) {
|
||||
if self.data.radarr_data.prompt_confirm {
|
||||
self.data.radarr_data.prompt_confirm = false;
|
||||
if let Some(radarr_event) = &self.data.radarr_data.prompt_confirm_action {
|
||||
@@ -136,19 +167,17 @@ impl<'a> App<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn radarr_on_tick(
|
||||
&mut self,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
is_first_render: bool,
|
||||
) {
|
||||
if is_first_render {
|
||||
self.refresh_metadata().await;
|
||||
pub(super) async fn radarr_on_tick(&mut self, active_radarr_block: ActiveRadarrBlock) {
|
||||
if self.is_first_render {
|
||||
self.refresh_radarr_metadata().await;
|
||||
self.dispatch_by_radarr_block(&active_radarr_block).await;
|
||||
self.is_first_render = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if self.should_refresh {
|
||||
self.dispatch_by_radarr_block(&active_radarr_block).await;
|
||||
self.refresh_metadata().await;
|
||||
self.refresh_radarr_metadata().await;
|
||||
}
|
||||
|
||||
if self.is_routing {
|
||||
@@ -156,16 +185,15 @@ impl<'a> App<'a> {
|
||||
self.cancellation_token.cancel();
|
||||
} else {
|
||||
self.dispatch_by_radarr_block(&active_radarr_block).await;
|
||||
self.refresh_metadata().await;
|
||||
}
|
||||
}
|
||||
|
||||
if self.tick_count % self.tick_until_poll == 0 {
|
||||
self.refresh_metadata().await;
|
||||
self.refresh_radarr_metadata().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn refresh_metadata(&mut self) {
|
||||
async fn refresh_radarr_metadata(&mut self) {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
|
||||
.await;
|
||||
@@ -201,4 +229,29 @@ impl<'a> App<'a> {
|
||||
.collection_movies
|
||||
.set_items(collection_movies);
|
||||
}
|
||||
|
||||
async fn extract_movie_id(&self) -> i64 {
|
||||
self.data.radarr_data.movies.current_selection().clone().id
|
||||
}
|
||||
|
||||
async fn extract_movie_search_query(&self) -> String {
|
||||
self
|
||||
.data
|
||||
.radarr_data
|
||||
.add_movie_search
|
||||
.as_ref()
|
||||
.expect("Add movie search is empty")
|
||||
.text
|
||||
.clone()
|
||||
}
|
||||
|
||||
async fn extract_radarr_indexer_id(&self) -> i64 {
|
||||
self
|
||||
.data
|
||||
.radarr_data
|
||||
.indexers
|
||||
.current_selection()
|
||||
.clone()
|
||||
.id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,60 +35,6 @@ pub static COLLECTIONS_CONTEXT_CLUES: [ContextClue; 8] = [
|
||||
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
|
||||
];
|
||||
|
||||
pub static DOWNLOADS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||
];
|
||||
|
||||
pub static BLOCKLIST_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||
(DEFAULT_KEYBINDINGS.clear, "clear blocklist"),
|
||||
];
|
||||
|
||||
pub static ROOT_FOLDERS_CONTEXT_CLUES: [ContextClue; 3] = [
|
||||
(DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc),
|
||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
];
|
||||
|
||||
pub static INDEXERS_CONTEXT_CLUES: [ContextClue; 6] = [
|
||||
(DEFAULT_KEYBINDINGS.submit, "edit indexer"),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.settings,
|
||||
DEFAULT_KEYBINDINGS.settings.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||
(DEFAULT_KEYBINDINGS.test, "test indexer"),
|
||||
(DEFAULT_KEYBINDINGS.test_all, "test all indexers"),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
];
|
||||
|
||||
pub static SYSTEM_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||
(DEFAULT_KEYBINDINGS.tasks, "open tasks"),
|
||||
(DEFAULT_KEYBINDINGS.events, "open events"),
|
||||
(DEFAULT_KEYBINDINGS.logs, "open logs"),
|
||||
(DEFAULT_KEYBINDINGS.update, "open updates"),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
];
|
||||
|
||||
pub static MOVIE_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
@@ -96,7 +42,10 @@ pub static MOVIE_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
|
||||
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
|
||||
(DEFAULT_KEYBINDINGS.search, "auto search"),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.auto_search,
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
|
||||
@@ -108,7 +57,10 @@ pub static MANUAL_MOVIE_SEARCH_CONTEXT_CLUES: [ContextClue; 6] = [
|
||||
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
|
||||
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
|
||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||
(DEFAULT_KEYBINDINGS.search, "auto search"),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.auto_search,
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
|
||||
@@ -120,17 +72,13 @@ pub static ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
(DEFAULT_KEYBINDINGS.esc, "edit search"),
|
||||
];
|
||||
|
||||
pub static CONFIRMATION_PROMPT_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
(DEFAULT_KEYBINDINGS.confirm, "submit"),
|
||||
(DEFAULT_KEYBINDINGS.esc, "cancel"),
|
||||
];
|
||||
|
||||
pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
(DEFAULT_KEYBINDINGS.submit, "start task"),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
|
||||
pub static COLLECTION_DETAILS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
pub static COLLECTION_DETAILS_CONTEXT_CLUES: [ContextClue; 3] = [
|
||||
(DEFAULT_KEYBINDINGS.submit, "show overview/add movie"),
|
||||
(DEFAULT_KEYBINDINGS.edit, "edit collection"),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
|
||||
@@ -4,11 +4,10 @@ mod tests {
|
||||
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::radarr::radarr_context_clues::{
|
||||
ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES,
|
||||
COLLECTION_DETAILS_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES,
|
||||
INDEXERS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES,
|
||||
MANUAL_MOVIE_SEARCH_CONTEXT_CLUES, MOVIE_DETAILS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES,
|
||||
SYSTEM_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
|
||||
ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES,
|
||||
COLLECTION_DETAILS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES,
|
||||
MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
|
||||
MOVIE_DETAILS_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -113,141 +112,6 @@ mod tests {
|
||||
assert_eq!(collections_context_clues.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downloads_context_clues() {
|
||||
let mut downloads_context_clues_iter = DOWNLOADS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
|
||||
assert_eq!(downloads_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blocklist_context_clues() {
|
||||
let mut blocklist_context_clues_iter = BLOCKLIST_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "details");
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
|
||||
|
||||
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.clear);
|
||||
assert_str_eq!(*description, "clear blocklist");
|
||||
assert_eq!(blocklist_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_context_clues() {
|
||||
let mut root_folders_context_clues_iter = ROOT_FOLDERS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.add);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.add.desc);
|
||||
|
||||
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
|
||||
|
||||
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
assert_eq!(root_folders_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_indexers_context_clues() {
|
||||
let mut indexers_context_clues_iter = INDEXERS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "edit indexer");
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.settings);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.settings.desc);
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test);
|
||||
assert_str_eq!(*description, "test indexer");
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test_all);
|
||||
assert_str_eq!(*description, "test all indexers");
|
||||
|
||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
assert_eq!(indexers_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_context_clues() {
|
||||
let mut system_context_clues_iter = SYSTEM_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.tasks);
|
||||
assert_str_eq!(*description, "open tasks");
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.events);
|
||||
assert_str_eq!(*description, "open events");
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.logs);
|
||||
assert_str_eq!(*description, "open logs");
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
|
||||
assert_str_eq!(*description, "open updates");
|
||||
|
||||
let (key_binding, description) = system_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
assert_eq!(system_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_movie_details_context_clues() {
|
||||
let mut movie_details_context_clues_iter = MOVIE_DETAILS_CONTEXT_CLUES.iter();
|
||||
@@ -269,8 +133,8 @@ mod tests {
|
||||
|
||||
let (key_binding, description) = movie_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
|
||||
assert_str_eq!(*description, "auto search");
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||
|
||||
let (key_binding, description) = movie_details_context_clues_iter.next().unwrap();
|
||||
|
||||
@@ -305,8 +169,8 @@ mod tests {
|
||||
|
||||
let (key_binding, description) = manual_movie_search_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
|
||||
assert_str_eq!(*description, "auto search");
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||
|
||||
let (key_binding, description) = manual_movie_search_context_clues_iter.next().unwrap();
|
||||
|
||||
@@ -349,22 +213,6 @@ mod tests {
|
||||
assert_eq!(add_movie_search_results_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_confirmation_prompt_context_clues() {
|
||||
let mut confirmation_prompt_context_clues_iter = CONFIRMATION_PROMPT_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.confirm);
|
||||
assert_str_eq!(*description, "submit");
|
||||
|
||||
let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, "cancel");
|
||||
assert_eq!(confirmation_prompt_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_tasks_context_clues() {
|
||||
let mut system_tasks_context_clues_iter = SYSTEM_TASKS_CONTEXT_CLUES.iter();
|
||||
@@ -392,6 +240,11 @@ mod tests {
|
||||
|
||||
let (key_binding, description) = collection_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.edit);
|
||||
assert_str_eq!(*description, "edit collection");
|
||||
|
||||
let (key_binding, description) = collection_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||
assert_eq!(collection_details_context_clues_iter.next(), None);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod radarr_tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::app::radarr::ActiveRadarrBlock;
|
||||
use crate::app::App;
|
||||
use crate::models::radarr_models::{Collection, CollectionMovie, Credit, RadarrRelease};
|
||||
use crate::models::radarr_models::{
|
||||
AddMovieBody, AddMovieOptions, Collection, CollectionMovie, Credit, Movie, RadarrRelease,
|
||||
};
|
||||
use crate::models::servarr_data::radarr::modals::MovieDetailsModal;
|
||||
|
||||
use crate::models::servarr_models::Indexer;
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::network::NetworkEvent;
|
||||
|
||||
@@ -38,17 +39,25 @@ mod tests {
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetCollections.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovies.into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_collection_details_block() {
|
||||
let (mut app, _) = construct_app_unit();
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app.data.radarr_data.collections.set_items(vec![Collection {
|
||||
movies: Some(vec![CollectionMovie::default()]),
|
||||
@@ -60,6 +69,14 @@ mod tests {
|
||||
.await;
|
||||
|
||||
assert!(!app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetTags.into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.collection_movies.items.is_empty());
|
||||
assert_eq!(app.tick_count, 0);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
@@ -67,8 +84,23 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_collection_details_block_with_add_movie() {
|
||||
let add_movie_body = AddMovieBody {
|
||||
tmdb_id: 1234,
|
||||
title: "Test".to_owned(),
|
||||
root_folder_path: "/nfs2".to_owned(),
|
||||
minimum_availability: "announced".to_owned(),
|
||||
monitored: true,
|
||||
quality_profile_id: 2222,
|
||||
tags: vec![1, 2],
|
||||
tag_input_string: None,
|
||||
add_options: AddMovieOptions {
|
||||
monitor: "movieOnly".to_owned(),
|
||||
search_for_movie: true,
|
||||
},
|
||||
};
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None));
|
||||
app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::AddMovie(add_movie_body.clone()));
|
||||
|
||||
app.data.radarr_data.collections.set_items(vec![Collection {
|
||||
movies: Some(vec![CollectionMovie::default()]),
|
||||
@@ -82,7 +114,15 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::AddMovie(None).into()
|
||||
RadarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::AddMovie(add_movie_body).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.collection_movies.items.is_empty());
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -132,6 +172,14 @@ mod tests {
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovies.into()
|
||||
@@ -153,6 +201,10 @@ mod tests {
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetIndexers.into()
|
||||
@@ -181,6 +233,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_test_indexer_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.indexers.set_items(vec![Indexer {
|
||||
id: 1,
|
||||
..Indexer::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::TestIndexer)
|
||||
@@ -189,7 +245,7 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::TestIndexer(None).into()
|
||||
RadarrEvent::TestIndexer(1).into()
|
||||
);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
@@ -229,7 +285,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetLogs(None).into()
|
||||
RadarrEvent::GetLogs(500).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -255,6 +311,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_add_movie_search_results_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.add_movie_search = Some("test".into());
|
||||
|
||||
app
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::AddMovieSearchResults)
|
||||
@@ -263,7 +320,7 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::SearchNewMovie(None).into()
|
||||
RadarrEvent::SearchNewMovie("test".into()).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -272,6 +329,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_movie_details_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.movies.set_items(vec![Movie {
|
||||
id: 1,
|
||||
..Movie::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::MovieDetails)
|
||||
@@ -280,7 +341,7 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovieDetails(None).into()
|
||||
RadarrEvent::GetMovieDetails(1).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -289,6 +350,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_file_info_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.movies.set_items(vec![Movie {
|
||||
id: 1,
|
||||
..Movie::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::FileInfo)
|
||||
@@ -297,7 +362,7 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovieDetails(None).into()
|
||||
RadarrEvent::GetMovieDetails(1).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -306,6 +371,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_movie_history_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.movies.set_items(vec![Movie {
|
||||
id: 1,
|
||||
..Movie::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::MovieHistory)
|
||||
@@ -314,7 +383,7 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovieHistory(None).into()
|
||||
RadarrEvent::GetMovieHistory(1).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -323,6 +392,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_cast_crew_blocks() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.movies.set_items(vec![Movie {
|
||||
id: 1,
|
||||
..Movie::default()
|
||||
}]);
|
||||
|
||||
for active_radarr_block in &[ActiveRadarrBlock::Cast, ActiveRadarrBlock::Crew] {
|
||||
app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default());
|
||||
@@ -331,7 +404,7 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovieCredits(None).into()
|
||||
RadarrEvent::GetMovieCredits(1).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -341,6 +414,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_cast_crew_blocks_movie_cast_non_empty() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.movies.set_items(vec![Movie {
|
||||
id: 1,
|
||||
..Movie::default()
|
||||
}]);
|
||||
|
||||
for active_radarr_block in &[ActiveRadarrBlock::Cast, ActiveRadarrBlock::Crew] {
|
||||
let mut movie_details_modal = MovieDetailsModal::default();
|
||||
@@ -354,7 +431,7 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovieCredits(None).into()
|
||||
RadarrEvent::GetMovieCredits(1).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -364,6 +441,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_cast_crew_blocks_movie_crew_non_empty() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.movies.set_items(vec![Movie {
|
||||
id: 1,
|
||||
..Movie::default()
|
||||
}]);
|
||||
|
||||
for active_radarr_block in &[ActiveRadarrBlock::Cast, ActiveRadarrBlock::Crew] {
|
||||
let mut movie_details_modal = MovieDetailsModal::default();
|
||||
@@ -377,7 +458,7 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetMovieCredits(None).into()
|
||||
RadarrEvent::GetMovieCredits(1).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -409,6 +490,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_manual_search_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.movies.set_items(vec![Movie {
|
||||
id: 1,
|
||||
..Movie::default()
|
||||
}]);
|
||||
app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default());
|
||||
|
||||
app
|
||||
@@ -418,7 +503,7 @@ mod tests {
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetReleases(None).into()
|
||||
RadarrEvent::GetReleases(1).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
@@ -459,22 +544,22 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_for_prompt_action_no_prompt_confirm() {
|
||||
async fn test_check_for_radarr_prompt_action_no_prompt_confirm() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.prompt_confirm = false;
|
||||
|
||||
app.check_for_prompt_action().await;
|
||||
app.check_for_radarr_prompt_action().await;
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_for_prompt_action() {
|
||||
async fn test_check_for_radarr_prompt_action() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::GetStatus);
|
||||
|
||||
app.check_for_prompt_action().await;
|
||||
app.check_for_radarr_prompt_action().await;
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(
|
||||
@@ -490,7 +575,7 @@ mod tests {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.is_routing = true;
|
||||
|
||||
app.refresh_metadata().await;
|
||||
app.refresh_radarr_metadata().await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
@@ -522,8 +607,9 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_radarr_on_tick_first_render() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.is_first_render = true;
|
||||
|
||||
app.radarr_on_tick(ActiveRadarrBlock::Downloads, true).await;
|
||||
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
@@ -551,6 +637,7 @@ mod tests {
|
||||
);
|
||||
assert!(app.is_loading);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert!(!app.is_first_render);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -559,26 +646,8 @@ mod tests {
|
||||
app.is_routing = true;
|
||||
app.should_refresh = true;
|
||||
|
||||
app
|
||||
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
|
||||
.await;
|
||||
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetDownloads.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetRootFolders.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetDownloads.into()
|
||||
@@ -592,9 +661,7 @@ mod tests {
|
||||
app.is_routing = true;
|
||||
app.should_refresh = false;
|
||||
|
||||
app
|
||||
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
|
||||
.await;
|
||||
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
|
||||
|
||||
assert!(app.cancellation_token.is_cancelled());
|
||||
}
|
||||
@@ -604,9 +671,7 @@ mod tests {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.should_refresh = true;
|
||||
|
||||
app
|
||||
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
|
||||
.await;
|
||||
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
@@ -623,9 +688,7 @@ mod tests {
|
||||
app.is_routing = true;
|
||||
app.should_refresh = true;
|
||||
|
||||
app
|
||||
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
|
||||
.await;
|
||||
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
@@ -643,9 +706,7 @@ mod tests {
|
||||
app.tick_count = 2;
|
||||
app.tick_until_poll = 2;
|
||||
|
||||
app
|
||||
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
|
||||
.await;
|
||||
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
@@ -696,11 +757,34 @@ mod tests {
|
||||
assert!(!app.data.radarr_data.collection_movies.items.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_movie_id() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.movies.set_items(vec![Movie {
|
||||
id: 1,
|
||||
..Movie::default()
|
||||
}]);
|
||||
|
||||
assert_eq!(app.extract_movie_id().await, 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_radarr_indexer_id() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.indexers.set_items(vec![Indexer {
|
||||
id: 1,
|
||||
..Indexer::default()
|
||||
}]);
|
||||
|
||||
assert_eq!(app.extract_radarr_indexer_id().await, 1);
|
||||
}
|
||||
|
||||
fn construct_app_unit<'a>() -> (App<'a>, mpsc::Receiver<NetworkEvent>) {
|
||||
let (sync_network_tx, sync_network_rx) = mpsc::channel::<NetworkEvent>(500);
|
||||
let mut app = App {
|
||||
network_tx: Some(sync_network_tx),
|
||||
tick_count: 1,
|
||||
is_first_render: false,
|
||||
..App::default()
|
||||
};
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
@@ -708,4 +792,3 @@ mod tests {
|
||||
(app, sync_network_rx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
use crate::{
|
||||
models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock,
|
||||
network::sonarr_network::SonarrEvent,
|
||||
};
|
||||
|
||||
use super::App;
|
||||
|
||||
pub mod sonarr_context_clues;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "sonarr_tests.rs"]
|
||||
mod sonarr_tests;
|
||||
|
||||
impl<'a> App<'a> {
|
||||
pub(super) async fn dispatch_by_sonarr_block(&mut self, active_sonarr_block: &ActiveSonarrBlock) {
|
||||
match active_sonarr_block {
|
||||
ActiveSonarrBlock::Series => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetQualityProfiles.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetLanguageProfiles.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetTags.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::ListSeries.into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::SeriesDetails => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::ListSeries.into())
|
||||
.await;
|
||||
self.is_loading = true;
|
||||
self.populate_seasons_table().await;
|
||||
self.is_loading = false;
|
||||
}
|
||||
ActiveSonarrBlock::SeriesHistory => {
|
||||
self
|
||||
.dispatch_network_event(
|
||||
SonarrEvent::GetSeriesHistory(self.extract_series_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::SeasonDetails => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetEpisodes(self.extract_series_id().await).into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(
|
||||
SonarrEvent::GetEpisodeFiles(self.extract_series_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetDownloads.into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::SeasonHistory => {
|
||||
if !self.data.sonarr_data.seasons.is_empty() {
|
||||
self
|
||||
.dispatch_network_event(
|
||||
SonarrEvent::GetSeasonHistory(self.extract_series_id_season_number_tuple().await)
|
||||
.into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
ActiveSonarrBlock::ManualSeasonSearch => {
|
||||
match self.data.sonarr_data.season_details_modal.as_ref() {
|
||||
Some(season_details_modal) if season_details_modal.season_releases.is_empty() => {
|
||||
self
|
||||
.dispatch_network_event(
|
||||
SonarrEvent::GetSeasonReleases(self.extract_series_id_season_number_tuple().await)
|
||||
.into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
ActiveSonarrBlock::EpisodeDetails | ActiveSonarrBlock::EpisodeFile => {
|
||||
self
|
||||
.dispatch_network_event(
|
||||
SonarrEvent::GetEpisodeDetails(self.extract_episode_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::EpisodeHistory => {
|
||||
self
|
||||
.dispatch_network_event(
|
||||
SonarrEvent::GetEpisodeHistory(self.extract_episode_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::ManualEpisodeSearch => {
|
||||
if let Some(season_details_modal) = self.data.sonarr_data.season_details_modal.as_ref() {
|
||||
if let Some(episode_details_modal) = season_details_modal.episode_details_modal.as_ref() {
|
||||
if episode_details_modal.episode_releases.is_empty() {
|
||||
self
|
||||
.dispatch_network_event(
|
||||
SonarrEvent::GetEpisodeReleases(self.extract_episode_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ActiveSonarrBlock::Downloads => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetDownloads.into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::Blocklist => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::ListSeries.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetBlocklist.into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::History => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetHistory(500).into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::RootFolders => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetRootFolders.into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::Indexers => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetTags.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetIndexers.into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetAllIndexerSettings.into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::TestIndexer => {
|
||||
self
|
||||
.dispatch_network_event(
|
||||
SonarrEvent::TestIndexer(self.extract_sonarr_indexer_id().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::TestAllIndexers => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::TestAllIndexers.into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::System => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetTasks.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetQueuedEvents.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetLogs(500).into())
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::AddSeriesSearchResults => {
|
||||
self
|
||||
.dispatch_network_event(
|
||||
SonarrEvent::SearchNewSeries(self.extract_add_new_series_search_query().await).into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActiveSonarrBlock::SystemUpdates => {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetUpdates.into())
|
||||
.await;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.check_for_sonarr_prompt_action().await;
|
||||
self.reset_tick_count();
|
||||
}
|
||||
|
||||
async fn check_for_sonarr_prompt_action(&mut self) {
|
||||
if self.data.sonarr_data.prompt_confirm {
|
||||
self.data.sonarr_data.prompt_confirm = false;
|
||||
if let Some(sonarr_event) = &self.data.sonarr_data.prompt_confirm_action {
|
||||
self
|
||||
.dispatch_network_event(sonarr_event.clone().into())
|
||||
.await;
|
||||
self.should_refresh = true;
|
||||
self.data.sonarr_data.prompt_confirm_action = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn sonarr_on_tick(&mut self, active_sonarr_block: ActiveSonarrBlock) {
|
||||
if self.is_first_render {
|
||||
self.refresh_sonarr_metadata().await;
|
||||
self.dispatch_by_sonarr_block(&active_sonarr_block).await;
|
||||
self.is_first_render = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if self.should_refresh {
|
||||
self.dispatch_by_sonarr_block(&active_sonarr_block).await;
|
||||
self.refresh_sonarr_metadata().await;
|
||||
}
|
||||
|
||||
if self.is_routing {
|
||||
if !self.should_refresh {
|
||||
self.cancellation_token.cancel();
|
||||
} else {
|
||||
self.dispatch_by_sonarr_block(&active_sonarr_block).await;
|
||||
}
|
||||
}
|
||||
|
||||
if self.tick_count % self.tick_until_poll == 0 {
|
||||
self.refresh_sonarr_metadata().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn refresh_sonarr_metadata(&mut self) {
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetQualityProfiles.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetLanguageProfiles.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetTags.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetRootFolders.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetDownloads.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetDiskSpace.into())
|
||||
.await;
|
||||
self
|
||||
.dispatch_network_event(SonarrEvent::GetStatus.into())
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn populate_seasons_table(&mut self) {
|
||||
let seasons = self
|
||||
.data
|
||||
.sonarr_data
|
||||
.series
|
||||
.current_selection()
|
||||
.clone()
|
||||
.seasons
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|mut season| {
|
||||
season.title = Some(format!("Season {}", season.season_number));
|
||||
season
|
||||
})
|
||||
.collect();
|
||||
self.data.sonarr_data.seasons.set_items(seasons);
|
||||
}
|
||||
|
||||
async fn extract_episode_id(&self) -> i64 {
|
||||
self
|
||||
.data
|
||||
.sonarr_data
|
||||
.season_details_modal
|
||||
.as_ref()
|
||||
.expect("Season details have not been loaded")
|
||||
.episodes
|
||||
.current_selection()
|
||||
.id
|
||||
}
|
||||
|
||||
async fn extract_series_id(&self) -> i64 {
|
||||
self.data.sonarr_data.series.current_selection().id
|
||||
}
|
||||
|
||||
async fn extract_series_id_season_number_tuple(&self) -> (i64, i64) {
|
||||
let series_id = self.data.sonarr_data.series.current_selection().id;
|
||||
let season_number = self
|
||||
.data
|
||||
.sonarr_data
|
||||
.seasons
|
||||
.current_selection()
|
||||
.season_number;
|
||||
(series_id, season_number)
|
||||
}
|
||||
|
||||
async fn extract_add_new_series_search_query(&self) -> String {
|
||||
self
|
||||
.data
|
||||
.sonarr_data
|
||||
.add_series_search
|
||||
.as_ref()
|
||||
.expect("Add series search is empty")
|
||||
.text
|
||||
.clone()
|
||||
}
|
||||
|
||||
async fn extract_sonarr_indexer_id(&self) -> i64 {
|
||||
self.data.sonarr_data.indexers.current_selection().id
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
use crate::app::{context_clues::ContextClue, key_binding::DEFAULT_KEYBINDINGS};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "sonarr_context_clues_tests.rs"]
|
||||
mod sonarr_context_clues_tests;
|
||||
|
||||
pub static ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||
(DEFAULT_KEYBINDINGS.esc, "edit search"),
|
||||
];
|
||||
|
||||
pub static SERIES_CONTEXT_CLUES: [ContextClue; 10] = [
|
||||
(DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc),
|
||||
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
|
||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.update, "update all"),
|
||||
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
|
||||
];
|
||||
|
||||
pub static SERIES_DETAILS_CONTEXT_CLUES: [ContextClue; 8] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.toggle_monitoring,
|
||||
DEFAULT_KEYBINDINGS.toggle_monitoring.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.submit, "season details"),
|
||||
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.auto_search,
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
|
||||
pub static HISTORY_CONTEXT_CLUES: [ContextClue; 6] = [
|
||||
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
|
||||
];
|
||||
|
||||
pub static SERIES_HISTORY_CONTEXT_CLUES: [ContextClue; 9] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
|
||||
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.auto_search,
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
|
||||
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
|
||||
];
|
||||
|
||||
pub static SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
(DEFAULT_KEYBINDINGS.submit, "episode details"),
|
||||
(DEFAULT_KEYBINDINGS.delete, "delete episode"),
|
||||
];
|
||||
|
||||
pub static SEASON_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.toggle_monitoring,
|
||||
DEFAULT_KEYBINDINGS.toggle_monitoring.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.auto_search,
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
|
||||
pub static SEASON_HISTORY_CONTEXT_CLUES: [ContextClue; 6] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.auto_search,
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
|
||||
];
|
||||
|
||||
pub static MANUAL_SEASON_SEARCH_CONTEXT_CLUES: [ContextClue; 4] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.auto_search,
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
|
||||
pub static MANUAL_EPISODE_SEARCH_CONTEXT_CLUES: [ContextClue; 4] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.auto_search,
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
|
||||
pub static DETAILS_CONTEXTUAL_CONTEXT_CLUES: [ContextClue; 1] =
|
||||
[(DEFAULT_KEYBINDINGS.submit, "details")];
|
||||
|
||||
pub static EPISODE_DETAILS_CONTEXT_CLUES: [ContextClue; 3] = [
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.refresh,
|
||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||
),
|
||||
(
|
||||
DEFAULT_KEYBINDINGS.auto_search,
|
||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||
),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
|
||||
pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||
(DEFAULT_KEYBINDINGS.submit, "start task"),
|
||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||
];
|
||||
@@ -0,0 +1,402 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
|
||||
use crate::app::{
|
||||
key_binding::DEFAULT_KEYBINDINGS,
|
||||
sonarr::sonarr_context_clues::{
|
||||
ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES, DETAILS_CONTEXTUAL_CONTEXT_CLUES,
|
||||
EPISODE_DETAILS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, MANUAL_EPISODE_SEARCH_CONTEXT_CLUES,
|
||||
MANUAL_SEASON_SEARCH_CONTEXT_CLUES, SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES,
|
||||
SEASON_DETAILS_CONTEXT_CLUES, SEASON_HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES,
|
||||
SERIES_DETAILS_CONTEXT_CLUES, SERIES_HISTORY_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
|
||||
},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_add_series_search_results_context_clues() {
|
||||
let mut add_series_search_results_context_clues_iter =
|
||||
ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = add_series_search_results_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "details");
|
||||
|
||||
let (key_binding, description) = add_series_search_results_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, "edit search");
|
||||
assert_eq!(add_series_search_results_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_series_context_clues() {
|
||||
let mut series_context_clues_iter = SERIES_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.add);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.add.desc);
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.edit);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.edit.desc);
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.filter);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.filter.desc);
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
|
||||
assert_str_eq!(*description, "update all");
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "details");
|
||||
|
||||
let (key_binding, description) = series_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, "cancel filter");
|
||||
assert_eq!(series_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_series_history_context_clues() {
|
||||
let mut series_history_context_clues_iter = SERIES_HISTORY_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.edit);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.edit.desc);
|
||||
|
||||
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "details");
|
||||
|
||||
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
|
||||
|
||||
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
|
||||
|
||||
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.filter);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.filter.desc);
|
||||
|
||||
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||
|
||||
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.update.desc);
|
||||
|
||||
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, "cancel filter/close");
|
||||
assert_eq!(series_history_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_context_clues() {
|
||||
let mut history_context_clues_iter = HISTORY_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "details");
|
||||
|
||||
let (key_binding, description) = history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
|
||||
|
||||
let (key_binding, description) = history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
|
||||
|
||||
let (key_binding, description) = history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.filter);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.filter.desc);
|
||||
|
||||
let (key_binding, description) = history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, "cancel filter");
|
||||
assert_eq!(history_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_series_details_context_clues() {
|
||||
let mut series_details_context_clues_iter = SERIES_DETAILS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.edit);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.edit.desc);
|
||||
|
||||
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.toggle_monitoring);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.toggle_monitoring.desc);
|
||||
|
||||
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "season details");
|
||||
|
||||
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
|
||||
|
||||
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.update.desc);
|
||||
|
||||
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||
|
||||
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||
assert_eq!(series_details_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_season_details_context_clues() {
|
||||
let mut season_details_context_clues_iter = SEASON_DETAILS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.toggle_monitoring);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.toggle_monitoring.desc);
|
||||
|
||||
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
|
||||
|
||||
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||
|
||||
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||
assert_eq!(season_details_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_season_details_contextual_context_clues() {
|
||||
let mut season_details_contextual_context_clues_iter =
|
||||
SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES.iter();
|
||||
let (key_binding, description) = season_details_contextual_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "episode details");
|
||||
|
||||
let (key_binding, description) = season_details_contextual_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||
assert_str_eq!(*description, "delete episode");
|
||||
assert_eq!(season_details_contextual_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_season_history_context_clues() {
|
||||
let mut season_history_context_clues_iter = SEASON_HISTORY_CONTEXT_CLUES.iter();
|
||||
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
|
||||
|
||||
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
|
||||
|
||||
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.filter);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.filter.desc);
|
||||
|
||||
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||
|
||||
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, "cancel filter/close");
|
||||
assert_eq!(season_history_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_manual_season_search_context_clues() {
|
||||
let mut manual_season_search_context_clues_iter = MANUAL_SEASON_SEARCH_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||
|
||||
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
|
||||
|
||||
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||
assert_eq!(manual_season_search_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_manual_episode_search_context_clues() {
|
||||
let mut manual_episode_search_context_clues_iter = MANUAL_EPISODE_SEARCH_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||
|
||||
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
|
||||
|
||||
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||
assert_eq!(manual_episode_search_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn details_contextual_context_clues() {
|
||||
let mut manual_search_contextual_context_clues_iter = DETAILS_CONTEXTUAL_CONTEXT_CLUES.iter();
|
||||
let (key_binding, description) = manual_search_contextual_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "details");
|
||||
assert_eq!(manual_search_contextual_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_episode_details_context_clues() {
|
||||
let mut episode_details_context_clues_iter = EPISODE_DETAILS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = episode_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||
|
||||
let (key_binding, description) = episode_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||
|
||||
let (key_binding, description) = episode_details_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||
assert_eq!(episode_details_context_clues_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_tasks_context_clues() {
|
||||
let mut system_tasks_context_clues_iter = SYSTEM_TASKS_CONTEXT_CLUES.iter();
|
||||
|
||||
let (key_binding, description) = system_tasks_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||
assert_str_eq!(*description, "start task");
|
||||
|
||||
let (key_binding, description) = system_tasks_context_clues_iter.next().unwrap();
|
||||
|
||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||
assert_eq!(system_tasks_context_clues_iter.next(), None);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,881 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod sonarr_tests {
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::models::servarr_data::sonarr::sonarr_data::sonarr_test_utils::utils::create_test_sonarr_data;
|
||||
use crate::models::servarr_models::Indexer;
|
||||
use crate::models::sonarr_models::Episode;
|
||||
use crate::{
|
||||
app::App,
|
||||
models::{
|
||||
servarr_data::sonarr::{
|
||||
modals::{EpisodeDetailsModal, SeasonDetailsModal},
|
||||
sonarr_data::ActiveSonarrBlock,
|
||||
},
|
||||
sonarr_models::{Season, Series, SonarrRelease},
|
||||
},
|
||||
network::{sonarr_network::SonarrEvent, NetworkEvent},
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_blocklist_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::Blocklist)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::ListSeries.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetBlocklist.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_series_history_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.sonarr_data.series.set_items(vec![Series {
|
||||
id: 1,
|
||||
..Series::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeriesHistory)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetSeriesHistory(1).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_series_details_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app.data.sonarr_data.series.set_items(vec![Series {
|
||||
seasons: Some(vec![Season::default()]),
|
||||
..Series::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeriesDetails)
|
||||
.await;
|
||||
|
||||
assert!(!app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::ListSeries.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.seasons.items.is_empty());
|
||||
assert_eq!(app.tick_count, 0);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_season_details_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.sonarr_data.series.set_items(vec![Series {
|
||||
id: 1,
|
||||
..Series::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonDetails)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetEpisodes(1).into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetEpisodeFiles(1).into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDownloads.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_season_history_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.sonarr_data.series.set_items(vec![Series {
|
||||
id: 1,
|
||||
..Series::default()
|
||||
}]);
|
||||
app.data.sonarr_data.seasons.set_items(vec![Season {
|
||||
season_number: 1,
|
||||
..Season::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonHistory)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetSeasonHistory((1, 1)).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_season_history_block_no_op_when_seasons_table_is_empty() {
|
||||
let (mut app, _) = construct_app_unit();
|
||||
app.data.sonarr_data.series.set_items(vec![Series {
|
||||
id: 1,
|
||||
..Series::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonHistory)
|
||||
.await;
|
||||
|
||||
assert!(!app.is_loading);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_manual_season_search_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
|
||||
app.data.sonarr_data.series.set_items(vec![Series {
|
||||
id: 1,
|
||||
..Series::default()
|
||||
}]);
|
||||
app.data.sonarr_data.seasons.set_items(vec![Season {
|
||||
season_number: 1,
|
||||
..Season::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualSeasonSearch)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetSeasonReleases((1, 1)).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_manual_season_search_block_is_loading() {
|
||||
let mut app = App {
|
||||
is_loading: true,
|
||||
..App::default()
|
||||
};
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualSeasonSearch)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_manual_season_search_block_season_releases_non_empty() {
|
||||
let mut app = App::default();
|
||||
let mut season_details_modal = SeasonDetailsModal::default();
|
||||
season_details_modal
|
||||
.season_releases
|
||||
.set_items(vec![SonarrRelease::default()]);
|
||||
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualSeasonSearch)
|
||||
.await;
|
||||
|
||||
assert!(!app.is_loading);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_episode_details_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.sonarr_data = create_test_sonarr_data();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeDetails)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetEpisodeDetails(0).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_episode_file_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.sonarr_data = create_test_sonarr_data();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeFile)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetEpisodeDetails(0).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_episode_history_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
let mut season_details_modal = SeasonDetailsModal::default();
|
||||
season_details_modal.episodes.set_items(vec![Episode {
|
||||
id: 1,
|
||||
..Episode::default()
|
||||
}]);
|
||||
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeHistory)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetEpisodeHistory(1).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_manual_episode_search_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
let mut season_details_modal = SeasonDetailsModal {
|
||||
episode_details_modal: Some(EpisodeDetailsModal::default()),
|
||||
..SeasonDetailsModal::default()
|
||||
};
|
||||
season_details_modal.episodes.set_items(vec![Episode {
|
||||
id: 1,
|
||||
..Episode::default()
|
||||
}]);
|
||||
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualEpisodeSearch)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetEpisodeReleases(1).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_manual_episode_search_block_is_loading() {
|
||||
let mut app = App {
|
||||
is_loading: true,
|
||||
..App::default()
|
||||
};
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualEpisodeSearch)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_manual_episode_search_block_episode_releases_non_empty() {
|
||||
let mut app = App::default();
|
||||
let mut episode_details_modal = EpisodeDetailsModal::default();
|
||||
episode_details_modal
|
||||
.episode_releases
|
||||
.set_items(vec![SonarrRelease::default()]);
|
||||
let season_details_modal = SeasonDetailsModal {
|
||||
episode_details_modal: Some(episode_details_modal),
|
||||
..SeasonDetailsModal::default()
|
||||
};
|
||||
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualEpisodeSearch)
|
||||
.await;
|
||||
|
||||
assert!(!app.is_loading);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_history_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::History)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetHistory(500).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_downloads_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::Downloads)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDownloads.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_root_folders_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::RootFolders)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetRootFolders.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_series_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::Series)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetLanguageProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::ListSeries.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_indexers_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::Indexers)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetIndexers.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_all_indexer_settings_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::AllIndexerSettingsPrompt)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetAllIndexerSettings.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_test_indexer_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.sonarr_data.indexers.set_items(vec![Indexer {
|
||||
id: 1,
|
||||
..Indexer::default()
|
||||
}]);
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::TestIndexer)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::TestIndexer(1).into()
|
||||
);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_test_all_indexers_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::TestAllIndexers)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::TestAllIndexers.into()
|
||||
);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_system_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::System)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetTasks.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetQueuedEvents.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetLogs(500).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_system_updates_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SystemUpdates)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetUpdates.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_add_series_search_results_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.sonarr_data.add_series_search = Some("test search".into());
|
||||
|
||||
app
|
||||
.dispatch_by_sonarr_block(&ActiveSonarrBlock::AddSeriesSearchResults)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::SearchNewSeries("test search".into()).into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_for_sonarr_prompt_action_no_prompt_confirm() {
|
||||
let mut app = App::default();
|
||||
app.data.sonarr_data.prompt_confirm = false;
|
||||
|
||||
app.check_for_sonarr_prompt_action().await;
|
||||
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_for_sonarr_prompt_action() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::GetStatus);
|
||||
|
||||
app.check_for_sonarr_prompt_action().await;
|
||||
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetStatus.into()
|
||||
);
|
||||
assert!(app.should_refresh);
|
||||
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sonarr_refresh_metadata() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.is_routing = true;
|
||||
|
||||
app.refresh_sonarr_metadata().await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetLanguageProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetRootFolders.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDownloads.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDiskSpace.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetStatus.into()
|
||||
);
|
||||
assert!(app.is_loading);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sonarr_on_tick_first_render() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.is_first_render = true;
|
||||
|
||||
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetLanguageProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetRootFolders.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDownloads.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDiskSpace.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetStatus.into()
|
||||
);
|
||||
assert!(app.is_loading);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert!(!app.is_first_render);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sonarr_on_tick_routing() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.is_routing = true;
|
||||
app.should_refresh = true;
|
||||
|
||||
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDownloads.into()
|
||||
);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sonarr_on_tick_routing_while_long_request_is_running_should_cancel_request() {
|
||||
let (mut app, _) = construct_app_unit();
|
||||
app.is_routing = true;
|
||||
app.should_refresh = false;
|
||||
|
||||
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
|
||||
|
||||
assert!(app.cancellation_token.is_cancelled());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sonarr_on_tick_should_refresh() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.should_refresh = true;
|
||||
|
||||
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDownloads.into()
|
||||
);
|
||||
assert!(app.should_refresh);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sonarr_on_tick_should_refresh_does_not_cancel_prompt_requests() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.is_loading = true;
|
||||
app.is_routing = true;
|
||||
app.should_refresh = true;
|
||||
|
||||
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDownloads.into()
|
||||
);
|
||||
assert!(app.is_loading);
|
||||
assert!(app.should_refresh);
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
assert!(!app.cancellation_token.is_cancelled());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sonarr_on_tick_network_tick_frequency() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
app.tick_count = 2;
|
||||
app.tick_until_poll = 2;
|
||||
|
||||
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
|
||||
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetQualityProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetLanguageProfiles.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetTags.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetRootFolders.into()
|
||||
);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
SonarrEvent::GetDownloads.into()
|
||||
);
|
||||
assert!(app.is_loading);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_populate_seasons_table_unfiltered() {
|
||||
let mut app = App::default();
|
||||
app.data.sonarr_data.series.set_items(vec![Series {
|
||||
seasons: Some(vec![Season::default()]),
|
||||
..Series::default()
|
||||
}]);
|
||||
|
||||
app.populate_seasons_table().await;
|
||||
|
||||
assert!(!app.data.sonarr_data.seasons.items.is_empty());
|
||||
assert_str_eq!(
|
||||
app.data.sonarr_data.seasons.items[0]
|
||||
.title
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
"Season 0"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_populate_seasons_table_filtered() {
|
||||
let mut app = App::default();
|
||||
app.data.sonarr_data.series.set_filtered_items(vec![Series {
|
||||
seasons: Some(vec![Season::default()]),
|
||||
..Series::default()
|
||||
}]);
|
||||
|
||||
app.populate_seasons_table().await;
|
||||
|
||||
assert!(!app.data.sonarr_data.seasons.items.is_empty());
|
||||
assert_str_eq!(
|
||||
app.data.sonarr_data.seasons.items[0]
|
||||
.title
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
"Season 0"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_episode_id() {
|
||||
let mut app = App::default();
|
||||
let mut season_details_modal = SeasonDetailsModal::default();
|
||||
season_details_modal.episodes.set_items(vec![Episode {
|
||||
id: 1,
|
||||
..Episode::default()
|
||||
}]);
|
||||
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
|
||||
|
||||
assert_eq!(app.extract_episode_id().await, 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(expected = "Season details have not been loaded")]
|
||||
async fn test_extract_episode_id_requires_season_details_modal_to_be_some() {
|
||||
let app = App::default();
|
||||
|
||||
assert_eq!(app.extract_episode_id().await, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_series_id() {
|
||||
let mut app = App::default();
|
||||
app.data.sonarr_data.series.set_items(vec![Series {
|
||||
id: 1,
|
||||
..Series::default()
|
||||
}]);
|
||||
|
||||
assert_eq!(app.extract_series_id().await, 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_series_id_season_number_tuple() {
|
||||
let mut app = App::default();
|
||||
app.data.sonarr_data.series.set_items(vec![Series {
|
||||
id: 1,
|
||||
..Series::default()
|
||||
}]);
|
||||
app.data.sonarr_data.seasons.set_items(vec![Season {
|
||||
season_number: 1,
|
||||
..Season::default()
|
||||
}]);
|
||||
|
||||
assert_eq!(app.extract_series_id_season_number_tuple().await, (1, 1));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_add_new_series_search_query() {
|
||||
let mut app = App::default();
|
||||
app.data.sonarr_data.add_series_search = Some("test search".into());
|
||||
|
||||
assert_str_eq!(
|
||||
app.extract_add_new_series_search_query().await,
|
||||
"test search"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(expected = "Add series search is empty")]
|
||||
async fn test_extract_add_new_series_search_query_panics_when_the_query_is_not_set() {
|
||||
let app = App::default();
|
||||
|
||||
app.extract_add_new_series_search_query().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_sonarr_indexer_id() {
|
||||
let mut app = App::default();
|
||||
app.data.sonarr_data.indexers.set_items(vec![Indexer {
|
||||
id: 1,
|
||||
..Indexer::default()
|
||||
}]);
|
||||
|
||||
assert_eq!(app.extract_sonarr_indexer_id().await, 1);
|
||||
}
|
||||
|
||||
fn construct_app_unit<'a>() -> (App<'a>, mpsc::Receiver<NetworkEvent>) {
|
||||
let (sync_network_tx, sync_network_rx) = mpsc::channel::<NetworkEvent>(500);
|
||||
let mut app = App {
|
||||
network_tx: Some(sync_network_tx),
|
||||
tick_count: 1,
|
||||
is_first_render: false,
|
||||
..App::default()
|
||||
};
|
||||
app.data.sonarr_data.prompt_confirm = true;
|
||||
|
||||
(app, sync_network_rx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ use anyhow::Result;
|
||||
use clap::{arg, command, ArgAction, Subcommand};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::RadarrCommand;
|
||||
use crate::models::servarr_models::AddRootFolderBody;
|
||||
use crate::{
|
||||
app::App,
|
||||
cli::{CliCommandHandler, Command},
|
||||
@@ -11,8 +13,6 @@ use crate::{
|
||||
network::{radarr_network::RadarrEvent, NetworkTrait},
|
||||
};
|
||||
|
||||
use super::RadarrCommand;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "add_command_handler_tests.rs"]
|
||||
mod add_command_handler_tests;
|
||||
@@ -125,6 +125,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrAddCommand> for RadarrAddCommandHan
|
||||
minimum_availability: minimum_availability.to_string(),
|
||||
monitored: !disable_monitoring,
|
||||
tags,
|
||||
tag_input_string: None,
|
||||
add_options: AddMovieOptions {
|
||||
monitor: monitor.to_string(),
|
||||
search_for_movie: !no_search_for_movie,
|
||||
@@ -132,14 +133,17 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrAddCommand> for RadarrAddCommandHan
|
||||
};
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::AddMovie(Some(body)).into())
|
||||
.handle_network_event(RadarrEvent::AddMovie(body).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrAddCommand::RootFolder { root_folder_path } => {
|
||||
let add_root_folder_body = AddRootFolderBody {
|
||||
path: root_folder_path,
|
||||
};
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::AddRootFolder(Some(root_folder_path)).into())
|
||||
.handle_network_event(RadarrEvent::AddRootFolder(add_root_folder_body).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -368,6 +368,7 @@ mod tests {
|
||||
use super::*;
|
||||
use mockall::predicate::eq;
|
||||
|
||||
use crate::models::servarr_models::AddRootFolderBody;
|
||||
use serde_json::json;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@@ -381,6 +382,7 @@ mod tests {
|
||||
minimum_availability: "released".to_owned(),
|
||||
monitored: false,
|
||||
tags: vec![1, 2],
|
||||
tag_input_string: None,
|
||||
add_options: AddMovieOptions {
|
||||
monitor: "movieAndCollection".to_owned(),
|
||||
search_for_movie: false,
|
||||
@@ -390,7 +392,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::AddMovie(Some(expected_add_movie_body)).into(),
|
||||
RadarrEvent::AddMovie(expected_add_movie_body).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -420,11 +422,14 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_handle_add_root_folder_command() {
|
||||
let expected_root_folder_path = "/nfs/test".to_owned();
|
||||
let expected_add_root_folder_body = AddRootFolderBody {
|
||||
path: expected_root_folder_path.clone(),
|
||||
};
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::AddRootFolder(Some(expected_root_folder_path.clone())).into(),
|
||||
RadarrEvent::AddRootFolder(expected_add_root_folder_body).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -89,21 +89,21 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrDeleteCommand> for RadarrDeleteComm
|
||||
RadarrDeleteCommand::BlocklistItem { blocklist_item_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::DeleteBlocklistItem(Some(blocklist_item_id)).into())
|
||||
.handle_network_event(RadarrEvent::DeleteBlocklistItem(blocklist_item_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrDeleteCommand::Download { download_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::DeleteDownload(Some(download_id)).into())
|
||||
.handle_network_event(RadarrEvent::DeleteDownload(download_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrDeleteCommand::Indexer { indexer_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::DeleteIndexer(Some(indexer_id)).into())
|
||||
.handle_network_event(RadarrEvent::DeleteIndexer(indexer_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
@@ -119,14 +119,14 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrDeleteCommand> for RadarrDeleteComm
|
||||
};
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::DeleteMovie(Some(delete_movie_params)).into())
|
||||
.handle_network_event(RadarrEvent::DeleteMovie(delete_movie_params).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrDeleteCommand::RootFolder { root_folder_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::DeleteRootFolder(Some(root_folder_id)).into())
|
||||
.handle_network_event(RadarrEvent::DeleteRootFolder(root_folder_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
|
||||
RadarrEvent::DeleteBlocklistItem(expected_blocklist_item_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -299,7 +299,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::DeleteDownload(Some(expected_download_id)).into(),
|
||||
RadarrEvent::DeleteDownload(expected_download_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -325,7 +325,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::DeleteIndexer(Some(expected_indexer_id)).into(),
|
||||
RadarrEvent::DeleteIndexer(expected_indexer_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -355,7 +355,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::DeleteMovie(Some(expected_delete_movie_params)).into(),
|
||||
RadarrEvent::DeleteMovie(expected_delete_movie_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -385,7 +385,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::DeleteRootFolder(Some(expected_root_folder_id)).into(),
|
||||
RadarrEvent::DeleteRootFolder(expected_root_folder_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -390,7 +390,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrEditCommand> for RadarrEditCommandH
|
||||
};
|
||||
self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::EditAllIndexerSettings(Some(params)).into())
|
||||
.handle_network_event(RadarrEvent::EditAllIndexerSettings(params).into())
|
||||
.await?;
|
||||
"All indexer settings updated".to_owned()
|
||||
} else {
|
||||
@@ -420,7 +420,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrEditCommand> for RadarrEditCommandH
|
||||
};
|
||||
self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::EditCollection(Some(edit_collection_params)).into())
|
||||
.handle_network_event(RadarrEvent::EditCollection(edit_collection_params).into())
|
||||
.await?;
|
||||
"Collection updated".to_owned()
|
||||
}
|
||||
@@ -455,13 +455,14 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrEditCommand> for RadarrEditCommandH
|
||||
api_key,
|
||||
seed_ratio,
|
||||
tags: tag,
|
||||
tag_input_string: None,
|
||||
priority,
|
||||
clear_tags,
|
||||
};
|
||||
|
||||
self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::EditIndexer(Some(edit_indexer_params)).into())
|
||||
.handle_network_event(RadarrEvent::EditIndexer(edit_indexer_params).into())
|
||||
.await?;
|
||||
"Indexer updated".to_owned()
|
||||
}
|
||||
@@ -483,12 +484,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrEditCommand> for RadarrEditCommandH
|
||||
quality_profile_id,
|
||||
root_folder_path,
|
||||
tags: tag,
|
||||
tag_input_string: None,
|
||||
clear_tags,
|
||||
};
|
||||
|
||||
self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::EditMovie(Some(edit_movie_params)).into())
|
||||
.handle_network_event(RadarrEvent::EditMovie(edit_movie_params).into())
|
||||
.await?;
|
||||
"Movie Updated".to_owned()
|
||||
}
|
||||
|
||||
@@ -857,7 +857,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
|
||||
RadarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -928,7 +928,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
|
||||
RadarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1000,7 +1000,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
|
||||
RadarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1047,7 +1047,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditCollection(Some(expected_edit_collection_params)).into(),
|
||||
RadarrEvent::EditCollection(expected_edit_collection_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1089,7 +1089,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditCollection(Some(expected_edit_collection_params)).into(),
|
||||
RadarrEvent::EditCollection(expected_edit_collection_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1131,7 +1131,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditCollection(Some(expected_edit_collection_params)).into(),
|
||||
RadarrEvent::EditCollection(expected_edit_collection_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1171,6 +1171,7 @@ mod tests {
|
||||
api_key: Some("testKey".to_owned()),
|
||||
seed_ratio: Some("1.2".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
priority: Some(25),
|
||||
clear_tags: false,
|
||||
};
|
||||
@@ -1178,7 +1179,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditIndexer(Some(expected_edit_indexer_params)).into(),
|
||||
RadarrEvent::EditIndexer(expected_edit_indexer_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1224,6 +1225,7 @@ mod tests {
|
||||
api_key: Some("testKey".to_owned()),
|
||||
seed_ratio: Some("1.2".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
priority: Some(25),
|
||||
clear_tags: false,
|
||||
};
|
||||
@@ -1231,7 +1233,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditIndexer(Some(expected_edit_indexer_params)).into(),
|
||||
RadarrEvent::EditIndexer(expected_edit_indexer_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1277,6 +1279,7 @@ mod tests {
|
||||
api_key: Some("testKey".to_owned()),
|
||||
seed_ratio: Some("1.2".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
priority: Some(25),
|
||||
clear_tags: false,
|
||||
};
|
||||
@@ -1284,7 +1287,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditIndexer(Some(expected_edit_indexer_params)).into(),
|
||||
RadarrEvent::EditIndexer(expected_edit_indexer_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1327,13 +1330,14 @@ mod tests {
|
||||
quality_profile_id: Some(1),
|
||||
root_folder_path: Some("/nfs/test".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
clear_tags: false,
|
||||
};
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditMovie(Some(expected_edit_movie_params)).into(),
|
||||
RadarrEvent::EditMovie(expected_edit_movie_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1369,13 +1373,14 @@ mod tests {
|
||||
quality_profile_id: Some(1),
|
||||
root_folder_path: Some("/nfs/test".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
clear_tags: false,
|
||||
};
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditMovie(Some(expected_edit_movie_params)).into(),
|
||||
RadarrEvent::EditMovie(expected_edit_movie_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -1411,13 +1416,14 @@ mod tests {
|
||||
quality_profile_id: Some(1),
|
||||
root_folder_path: Some("/nfs/test".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
clear_tags: false,
|
||||
};
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditMovie(Some(expected_edit_movie_params)).into(),
|
||||
RadarrEvent::EditMovie(expected_edit_movie_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -90,14 +90,14 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrGetCommand> for RadarrGetCommandHan
|
||||
RadarrGetCommand::MovieDetails { movie_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::GetMovieDetails(Some(movie_id)).into())
|
||||
.handle_network_event(RadarrEvent::GetMovieDetails(movie_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrGetCommand::MovieHistory { movie_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::GetMovieHistory(Some(movie_id)).into())
|
||||
.handle_network_event(RadarrEvent::GetMovieHistory(movie_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::GetMovieDetails(Some(expected_movie_id)).into(),
|
||||
RadarrEvent::GetMovieDetails(expected_movie_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -208,7 +208,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::GetMovieHistory(Some(expected_movie_id)).into(),
|
||||
RadarrEvent::GetMovieHistory(expected_movie_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -131,7 +131,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrListCommand> for RadarrListCommandH
|
||||
} => {
|
||||
let logs = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::GetLogs(Some(events)).into())
|
||||
.handle_network_event(RadarrEvent::GetLogs(events).into())
|
||||
.await?;
|
||||
|
||||
if output_in_log_format {
|
||||
@@ -152,7 +152,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrListCommand> for RadarrListCommandH
|
||||
RadarrListCommand::MovieCredits { movie_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::GetMovieCredits(Some(movie_id)).into())
|
||||
.handle_network_event(RadarrEvent::GetMovieCredits(movie_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::GetMovieCredits(Some(expected_movie_id)).into(),
|
||||
RadarrEvent::GetMovieCredits(expected_movie_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -189,7 +189,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::GetLogs(Some(expected_events)).into(),
|
||||
RadarrEvent::GetLogs(expected_events).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -209,7 +209,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrCommand> for RadarrCliHandler<'a, '
|
||||
};
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::DownloadRelease(Some(params)).into())
|
||||
.handle_network_event(RadarrEvent::DownloadRelease(params).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
@@ -217,28 +217,28 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrCommand> for RadarrCliHandler<'a, '
|
||||
println!("Searching for releases. This may take a minute...");
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::GetReleases(Some(movie_id)).into())
|
||||
.handle_network_event(RadarrEvent::GetReleases(movie_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrCommand::SearchNewMovie { query } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::SearchNewMovie(Some(query)).into())
|
||||
.handle_network_event(RadarrEvent::SearchNewMovie(query).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrCommand::StartTask { task_name } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::StartTask(Some(task_name)).into())
|
||||
.handle_network_event(RadarrEvent::StartTask(task_name).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrCommand::TestIndexer { indexer_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::TestIndexer(Some(indexer_id)).into())
|
||||
.handle_network_event(RadarrEvent::TestIndexer(indexer_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
@@ -253,7 +253,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrCommand> for RadarrCliHandler<'a, '
|
||||
RadarrCommand::TriggerAutomaticSearch { movie_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::TriggerAutomaticSearch(Some(movie_id)).into())
|
||||
.handle_network_event(RadarrEvent::TriggerAutomaticSearch(movie_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -293,9 +293,9 @@ mod tests {
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||
let claer_blocklist_command = RadarrCommand::ClearBlocklist;
|
||||
let clear_blocklist_command = RadarrCommand::ClearBlocklist;
|
||||
|
||||
let result = RadarrCliHandler::with(&app_arc, claer_blocklist_command, &mut mock_network)
|
||||
let result = RadarrCliHandler::with(&app_arc, clear_blocklist_command, &mut mock_network)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
@@ -313,7 +313,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::DownloadRelease(Some(expected_release_download_body)).into(),
|
||||
RadarrEvent::DownloadRelease(expected_release_download_body).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -342,7 +342,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::GetReleases(Some(expected_movie_id)).into(),
|
||||
RadarrEvent::GetReleases(expected_movie_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -367,7 +367,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::SearchNewMovie(Some(expected_search_query)).into(),
|
||||
RadarrEvent::SearchNewMovie(expected_search_query).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -394,7 +394,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::StartTask(Some(expected_task_name)).into(),
|
||||
RadarrEvent::StartTask(expected_task_name).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -421,7 +421,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::TestIndexer(Some(expected_indexer_id)).into(),
|
||||
RadarrEvent::TestIndexer(expected_indexer_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -468,7 +468,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::TriggerAutomaticSearch(Some(expected_movie_id)).into(),
|
||||
RadarrEvent::TriggerAutomaticSearch(expected_movie_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -524,7 +524,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
|
||||
RadarrEvent::DeleteBlocklistItem(expected_blocklist_item_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -584,7 +584,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
|
||||
RadarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -654,7 +654,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::GetMovieCredits(Some(expected_movie_id)).into(),
|
||||
RadarrEvent::GetMovieCredits(expected_movie_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -680,7 +680,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::UpdateAndScan(Some(expected_movie_id)).into(),
|
||||
RadarrEvent::UpdateAndScan(expected_movie_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -88,7 +88,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrRefreshCommand>
|
||||
RadarrRefreshCommand::Movie { movie_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::UpdateAndScan(Some(movie_id)).into())
|
||||
.handle_network_event(RadarrEvent::UpdateAndScan(movie_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::UpdateAndScan(Some(expected_movie_id)).into(),
|
||||
RadarrEvent::UpdateAndScan(expected_movie_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -4,6 +4,8 @@ use anyhow::Result;
|
||||
use clap::{ArgAction, Subcommand};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::SonarrCommand;
|
||||
use crate::models::servarr_models::AddRootFolderBody;
|
||||
use crate::{
|
||||
app::App,
|
||||
cli::{CliCommandHandler, Command},
|
||||
@@ -11,8 +13,6 @@ use crate::{
|
||||
network::{sonarr_network::SonarrEvent, NetworkTrait},
|
||||
};
|
||||
|
||||
use super::SonarrCommand;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "add_command_handler_tests.rs"]
|
||||
mod add_command_handler_tests;
|
||||
@@ -140,6 +140,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrAddCommand> for SonarrAddCommandHan
|
||||
series_type: series_type.to_string(),
|
||||
season_folder: !disable_season_folders,
|
||||
tags,
|
||||
tag_input_string: None,
|
||||
add_options: AddSeriesOptions {
|
||||
monitor: monitor.to_string(),
|
||||
search_for_cutoff_unmet_episodes: !no_search_for_series,
|
||||
@@ -148,14 +149,17 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrAddCommand> for SonarrAddCommandHan
|
||||
};
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::AddSeries(Some(body)).into())
|
||||
.handle_network_event(SonarrEvent::AddSeries(body).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrAddCommand::RootFolder { root_folder_path } => {
|
||||
let add_root_folder_body = AddRootFolderBody {
|
||||
path: root_folder_path,
|
||||
};
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::AddRootFolder(Some(root_folder_path)).into())
|
||||
.handle_network_event(SonarrEvent::AddRootFolder(add_root_folder_body).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -469,17 +469,21 @@ mod tests {
|
||||
use super::*;
|
||||
use mockall::predicate::eq;
|
||||
|
||||
use crate::models::servarr_models::AddRootFolderBody;
|
||||
use serde_json::json;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_add_root_folder_command() {
|
||||
let expected_root_folder_path = "/nfs/test".to_owned();
|
||||
let expected_add_root_folder_body = AddRootFolderBody {
|
||||
path: expected_root_folder_path.clone(),
|
||||
};
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::AddRootFolder(Some(expected_root_folder_path.clone())).into(),
|
||||
SonarrEvent::AddRootFolder(expected_add_root_folder_body.clone()).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -511,6 +515,7 @@ mod tests {
|
||||
series_type: "anime".to_owned(),
|
||||
monitored: false,
|
||||
tags: vec![1, 2],
|
||||
tag_input_string: None,
|
||||
season_folder: false,
|
||||
add_options: AddSeriesOptions {
|
||||
monitor: "future".to_owned(),
|
||||
@@ -522,7 +527,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::AddSeries(Some(expected_add_series_body)).into(),
|
||||
SonarrEvent::AddSeries(expected_add_series_body).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -94,35 +94,35 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrDeleteCommand> for SonarrDeleteComm
|
||||
SonarrDeleteCommand::BlocklistItem { blocklist_item_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::DeleteBlocklistItem(Some(blocklist_item_id)).into())
|
||||
.handle_network_event(SonarrEvent::DeleteBlocklistItem(blocklist_item_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrDeleteCommand::Download { download_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::DeleteDownload(Some(download_id)).into())
|
||||
.handle_network_event(SonarrEvent::DeleteDownload(download_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrDeleteCommand::EpisodeFile { episode_file_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::DeleteEpisodeFile(Some(episode_file_id)).into())
|
||||
.handle_network_event(SonarrEvent::DeleteEpisodeFile(episode_file_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrDeleteCommand::Indexer { indexer_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::DeleteIndexer(Some(indexer_id)).into())
|
||||
.handle_network_event(SonarrEvent::DeleteIndexer(indexer_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrDeleteCommand::RootFolder { root_folder_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::DeleteRootFolder(Some(root_folder_id)).into())
|
||||
.handle_network_event(SonarrEvent::DeleteRootFolder(root_folder_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
@@ -138,7 +138,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrDeleteCommand> for SonarrDeleteComm
|
||||
};
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::DeleteSeries(Some(delete_series_params)).into())
|
||||
.handle_network_event(SonarrEvent::DeleteSeries(delete_series_params).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
|
||||
SonarrEvent::DeleteBlocklistItem(expected_blocklist_item_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -332,7 +332,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::DeleteDownload(Some(expected_download_id)).into(),
|
||||
SonarrEvent::DeleteDownload(expected_download_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -351,6 +351,32 @@ mod tests {
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_episode_file_command() {
|
||||
let expected_episode_file_id = 1;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::DeleteEpisodeFile(expected_episode_file_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Sonarr(SonarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||
let delete_episode_file_command = SonarrDeleteCommand::EpisodeFile { episode_file_id: 1 };
|
||||
|
||||
let result =
|
||||
SonarrDeleteCommandHandler::with(&app_arc, delete_episode_file_command, &mut mock_network)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_indexer_command() {
|
||||
let expected_indexer_id = 1;
|
||||
@@ -358,7 +384,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::DeleteIndexer(Some(expected_indexer_id)).into(),
|
||||
SonarrEvent::DeleteIndexer(expected_indexer_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -384,7 +410,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::DeleteRootFolder(Some(expected_root_folder_id)).into(),
|
||||
SonarrEvent::DeleteRootFolder(expected_root_folder_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -414,7 +440,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::DeleteSeries(Some(expected_delete_series_params)).into(),
|
||||
SonarrEvent::DeleteSeries(expected_delete_series_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -274,7 +274,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrEditCommand> for SonarrEditCommandH
|
||||
};
|
||||
self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::EditAllIndexerSettings(Some(params)).into())
|
||||
.handle_network_event(SonarrEvent::EditAllIndexerSettings(params).into())
|
||||
.await?;
|
||||
"All indexer settings updated".to_owned()
|
||||
} else {
|
||||
@@ -312,13 +312,14 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrEditCommand> for SonarrEditCommandH
|
||||
api_key,
|
||||
seed_ratio,
|
||||
tags: tag,
|
||||
tag_input_string: None,
|
||||
priority,
|
||||
clear_tags,
|
||||
};
|
||||
|
||||
self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::EditIndexer(Some(edit_indexer_params)).into())
|
||||
.handle_network_event(SonarrEvent::EditIndexer(edit_indexer_params).into())
|
||||
.await?;
|
||||
"Indexer updated".to_owned()
|
||||
}
|
||||
@@ -347,12 +348,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrEditCommand> for SonarrEditCommandH
|
||||
language_profile_id,
|
||||
root_folder_path,
|
||||
tags: tag,
|
||||
tag_input_string: None,
|
||||
clear_tags,
|
||||
};
|
||||
|
||||
self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::EditSeries(Some(edit_series_params)).into())
|
||||
.handle_network_event(SonarrEvent::EditSeries(edit_series_params).into())
|
||||
.await?;
|
||||
"Series Updated".to_owned()
|
||||
}
|
||||
|
||||
@@ -650,7 +650,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
|
||||
SonarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -689,6 +689,7 @@ mod tests {
|
||||
api_key: Some("testKey".to_owned()),
|
||||
seed_ratio: Some("1.2".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
priority: Some(25),
|
||||
clear_tags: false,
|
||||
};
|
||||
@@ -696,7 +697,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::EditIndexer(Some(expected_edit_indexer_params)).into(),
|
||||
SonarrEvent::EditIndexer(expected_edit_indexer_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -741,13 +742,14 @@ mod tests {
|
||||
language_profile_id: Some(1),
|
||||
root_folder_path: Some("/nfs/test".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
clear_tags: false,
|
||||
};
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::EditSeries(Some(expected_edit_series_params)).into(),
|
||||
SonarrEvent::EditSeries(expected_edit_series_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -788,13 +790,14 @@ mod tests {
|
||||
language_profile_id: Some(1),
|
||||
root_folder_path: Some("/nfs/test".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
clear_tags: false,
|
||||
};
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::EditSeries(Some(expected_edit_series_params)).into(),
|
||||
SonarrEvent::EditSeries(expected_edit_series_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -835,13 +838,14 @@ mod tests {
|
||||
language_profile_id: Some(1),
|
||||
root_folder_path: Some("/nfs/test".to_owned()),
|
||||
tags: Some(vec![1, 2]),
|
||||
tag_input_string: None,
|
||||
clear_tags: false,
|
||||
};
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::EditSeries(Some(expected_edit_series_params)).into(),
|
||||
SonarrEvent::EditSeries(expected_edit_series_params).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -83,7 +83,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrGetCommand> for SonarrGetCommandHan
|
||||
SonarrGetCommand::EpisodeDetails { episode_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetEpisodeDetails(Some(episode_id)).into())
|
||||
.handle_network_event(SonarrEvent::GetEpisodeDetails(episode_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
@@ -104,7 +104,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrGetCommand> for SonarrGetCommandHan
|
||||
SonarrGetCommand::SeriesDetails { series_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetSeriesDetails(Some(series_id)).into())
|
||||
.handle_network_event(SonarrEvent::GetSeriesDetails(series_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetEpisodeDetails(Some(expected_episode_id)).into(),
|
||||
SonarrEvent::GetEpisodeDetails(expected_episode_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -232,7 +232,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetSeriesDetails(Some(expected_series_id)).into(),
|
||||
SonarrEvent::GetSeriesDetails(expected_series_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -33,6 +33,15 @@ pub enum SonarrListCommand {
|
||||
)]
|
||||
series_id: i64,
|
||||
},
|
||||
#[command(about = "List the episode files for the series with the given ID")]
|
||||
EpisodeFiles {
|
||||
#[arg(
|
||||
long,
|
||||
help = "The Sonarr ID of the series whose episode files you wish to fetch",
|
||||
required = true
|
||||
)]
|
||||
series_id: i64,
|
||||
},
|
||||
#[command(about = "Fetch all history events for the episode with the given ID")]
|
||||
EpisodeHistory {
|
||||
#[arg(
|
||||
@@ -67,6 +76,23 @@ pub enum SonarrListCommand {
|
||||
QueuedEvents,
|
||||
#[command(about = "List all root folders in Sonarr")]
|
||||
RootFolders,
|
||||
#[command(
|
||||
about = "Fetch all history events for the given season corresponding to the series with the given ID."
|
||||
)]
|
||||
SeasonHistory {
|
||||
#[arg(
|
||||
long,
|
||||
help = "The Sonarr ID of the series whose history you wish to fetch and list",
|
||||
required = true
|
||||
)]
|
||||
series_id: i64,
|
||||
#[arg(
|
||||
long,
|
||||
help = "The season number to fetch history events for",
|
||||
required = true
|
||||
)]
|
||||
season_number: i64,
|
||||
},
|
||||
#[command(about = "List all series in your Sonarr library")]
|
||||
Series,
|
||||
#[command(about = "Fetch all history events for the series with the given ID")]
|
||||
@@ -137,21 +163,28 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
|
||||
SonarrListCommand::Episodes { series_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetEpisodes(Some(series_id)).into())
|
||||
.handle_network_event(SonarrEvent::GetEpisodes(series_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrListCommand::EpisodeFiles { series_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetEpisodeFiles(series_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrListCommand::EpisodeHistory { episode_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetEpisodeHistory(Some(episode_id)).into())
|
||||
.handle_network_event(SonarrEvent::GetEpisodeHistory(episode_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrListCommand::History { events: items } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetHistory(Some(items)).into())
|
||||
.handle_network_event(SonarrEvent::GetHistory(items).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
@@ -175,7 +208,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
|
||||
} => {
|
||||
let logs = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetLogs(Some(events)).into())
|
||||
.handle_network_event(SonarrEvent::GetLogs(events).into())
|
||||
.await?;
|
||||
|
||||
if output_in_log_format {
|
||||
@@ -207,6 +240,16 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrListCommand::SeasonHistory {
|
||||
series_id,
|
||||
season_number,
|
||||
} => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetSeasonHistory((series_id, season_number)).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrListCommand::Series => {
|
||||
let resp = self
|
||||
.network
|
||||
@@ -217,7 +260,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
|
||||
SonarrListCommand::SeriesHistory { series_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetSeriesHistory(Some(series_id)).into())
|
||||
.handle_network_event(SonarrEvent::GetSeriesHistory(series_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -57,6 +57,18 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_episode_files_requires_series_id() {
|
||||
let result =
|
||||
Cli::command().try_get_matches_from(["managarr", "sonarr", "list", "episode-files"]);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_episode_history_requires_series_id() {
|
||||
let result =
|
||||
@@ -149,6 +161,79 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_episode_files_success() {
|
||||
let expected_args = SonarrListCommand::EpisodeFiles { series_id: 1 };
|
||||
let result = Cli::try_parse_from([
|
||||
"managarr",
|
||||
"sonarr",
|
||||
"list",
|
||||
"episode-files",
|
||||
"--series-id",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert!(result.is_ok());
|
||||
|
||||
if let Some(Command::Sonarr(SonarrCommand::List(episode_files_command))) =
|
||||
result.unwrap().command
|
||||
{
|
||||
assert_eq!(episode_files_command, expected_args);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_season_history_requires_series_id() {
|
||||
let result = Cli::command().try_get_matches_from([
|
||||
"managarr",
|
||||
"sonarr",
|
||||
"list",
|
||||
"season-history",
|
||||
"--season-number",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_season_history_requires_season_number() {
|
||||
let result = Cli::command().try_get_matches_from([
|
||||
"managarr",
|
||||
"sonarr",
|
||||
"list",
|
||||
"season-history",
|
||||
"--series-id",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_season_history_requirements_satisfied() {
|
||||
let result = Cli::command().try_get_matches_from([
|
||||
"managarr",
|
||||
"sonarr",
|
||||
"list",
|
||||
"season-history",
|
||||
"--series-id",
|
||||
"1",
|
||||
"--season-number",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_series_history_requires_series_id() {
|
||||
let result =
|
||||
@@ -244,7 +329,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetEpisodes(Some(expected_series_id)).into(),
|
||||
SonarrEvent::GetEpisodes(expected_series_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -263,6 +348,32 @@ mod tests {
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_list_episode_files_command() {
|
||||
let expected_series_id = 1;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetEpisodeFiles(expected_series_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Sonarr(SonarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||
let list_episode_files_command = SonarrListCommand::EpisodeFiles { series_id: 1 };
|
||||
|
||||
let result =
|
||||
SonarrListCommandHandler::with(&app_arc, list_episode_files_command, &mut mock_network)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_list_history_command() {
|
||||
let expected_events = 1000;
|
||||
@@ -270,7 +381,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetHistory(Some(expected_events)).into(),
|
||||
SonarrEvent::GetHistory(expected_events).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -296,7 +407,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetLogs(Some(expected_events)).into(),
|
||||
SonarrEvent::GetLogs(expected_events).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -324,7 +435,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetSeriesHistory(Some(expected_series_id)).into(),
|
||||
SonarrEvent::GetSeriesHistory(expected_series_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -350,7 +461,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetEpisodeHistory(Some(expected_episode_id)).into(),
|
||||
SonarrEvent::GetEpisodeHistory(expected_episode_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -368,5 +479,35 @@ mod tests {
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_season_history_command() {
|
||||
let expected_series_id = 1;
|
||||
let expected_season_number = 1;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetSeasonHistory((expected_series_id, expected_season_number)).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Sonarr(SonarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||
let list_season_history_command = SonarrListCommand::SeasonHistory {
|
||||
series_id: 1,
|
||||
season_number: 1,
|
||||
};
|
||||
|
||||
let result =
|
||||
SonarrListCommandHandler::with(&app_arc, list_season_history_command, &mut mock_network)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrManualSearchCommand>
|
||||
println!("Searching for episode releases. This may take a minute...");
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::GetEpisodeReleases(Some(episode_id)).into())
|
||||
.handle_network_event(SonarrEvent::GetEpisodeReleases(episode_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
@@ -86,9 +86,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrManualSearchCommand>
|
||||
println!("Searching for season releases. This may take a minute...");
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(
|
||||
SonarrEvent::GetSeasonReleases(Some((series_id, season_number))).into(),
|
||||
)
|
||||
.handle_network_event(SonarrEvent::GetSeasonReleases((series_id, season_number)).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetEpisodeReleases(Some(expected_episode_id)).into(),
|
||||
SonarrEvent::GetEpisodeReleases(expected_episode_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -160,7 +160,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetSeasonReleases(Some((expected_series_id, expected_season_number))).into(),
|
||||
SonarrEvent::GetSeasonReleases((expected_series_id, expected_season_number)).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -120,6 +120,32 @@ pub enum SonarrCommand {
|
||||
},
|
||||
#[command(about = "Test all Sonarr indexers")]
|
||||
TestAllIndexers,
|
||||
#[command(about = "Toggle monitoring for the specified episode")]
|
||||
ToggleEpisodeMonitoring {
|
||||
#[arg(
|
||||
long,
|
||||
help = "The Sonarr ID of the episode to toggle monitoring on",
|
||||
required = true
|
||||
)]
|
||||
episode_id: i64,
|
||||
},
|
||||
#[command(
|
||||
about = "Toggle monitoring for the specified season that corresponds to the specified series ID"
|
||||
)]
|
||||
ToggleSeasonMonitoring {
|
||||
#[arg(
|
||||
long,
|
||||
help = "The Sonarr ID of the series that the season belongs to",
|
||||
required = true
|
||||
)]
|
||||
series_id: i64,
|
||||
#[arg(
|
||||
long,
|
||||
help = "The season number to toggle monitoring for",
|
||||
required = true
|
||||
)]
|
||||
season_number: i64,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<SonarrCommand> for Command {
|
||||
@@ -219,21 +245,21 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrCommand> for SonarrCliHandler<'a, '
|
||||
SonarrCommand::SearchNewSeries { query } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::SearchNewSeries(Some(query)).into())
|
||||
.handle_network_event(SonarrEvent::SearchNewSeries(query).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrCommand::StartTask { task_name } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::StartTask(Some(task_name)).into())
|
||||
.handle_network_event(SonarrEvent::StartTask(task_name).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrCommand::TestIndexer { indexer_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::TestIndexer(Some(indexer_id)).into())
|
||||
.handle_network_event(SonarrEvent::TestIndexer(indexer_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
@@ -245,6 +271,25 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrCommand> for SonarrCliHandler<'a, '
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrCommand::ToggleEpisodeMonitoring { episode_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::ToggleEpisodeMonitoring(episode_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
SonarrCommand::ToggleSeasonMonitoring {
|
||||
series_id,
|
||||
season_number,
|
||||
} => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(
|
||||
SonarrEvent::ToggleSeasonMonitoring((series_id, season_number)).into(),
|
||||
)
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
|
||||
@@ -71,7 +71,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrRefreshCommand>
|
||||
SonarrRefreshCommand::Series { series_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::UpdateAndScanSeries(Some(series_id)).into())
|
||||
.handle_network_event(SonarrEvent::UpdateAndScanSeries(series_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::UpdateAndScanSeries(Some(expected_series_id)).into(),
|
||||
SonarrEvent::UpdateAndScanSeries(expected_series_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -142,6 +142,80 @@ mod tests {
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toggle_episode_monitoring_requires_episode_id() {
|
||||
let result =
|
||||
Cli::command().try_get_matches_from(["managarr", "sonarr", "toggle-episode-monitoring"]);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toggle_episode_monitoring_requirements_satisfied() {
|
||||
let result = Cli::command().try_get_matches_from([
|
||||
"managarr",
|
||||
"sonarr",
|
||||
"toggle-episode-monitoring",
|
||||
"--episode-id",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toggle_season_monitoring_requires_series_id() {
|
||||
let result = Cli::command().try_get_matches_from([
|
||||
"managarr",
|
||||
"sonarr",
|
||||
"toggle-season-monitoring",
|
||||
"--season-number",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toggle_season_monitoring_requires_season_number() {
|
||||
let result = Cli::command().try_get_matches_from([
|
||||
"managarr",
|
||||
"sonarr",
|
||||
"toggle-season-monitoring",
|
||||
"--series-id",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toggle_season_monitoring_requirements_satisfied() {
|
||||
let result = Cli::command().try_get_matches_from([
|
||||
"managarr",
|
||||
"sonarr",
|
||||
"toggle-season-monitoring",
|
||||
"--series-id",
|
||||
"1",
|
||||
"--season-number",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
mod handler {
|
||||
@@ -272,7 +346,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
|
||||
SonarrEvent::DeleteBlocklistItem(expected_blocklist_item_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -360,7 +434,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
|
||||
SonarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -396,7 +470,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::GetEpisodeReleases(Some(expected_episode_id)).into(),
|
||||
SonarrEvent::GetEpisodeReleases(expected_episode_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -424,7 +498,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::TriggerAutomaticEpisodeSearch(Some(expected_episode_id)).into(),
|
||||
SonarrEvent::TriggerAutomaticEpisodeSearch(expected_episode_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -497,7 +571,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::UpdateAndScanSeries(Some(expected_series_id)).into(),
|
||||
SonarrEvent::UpdateAndScanSeries(expected_series_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -523,7 +597,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::SearchNewSeries(Some(expected_search_query)).into(),
|
||||
SonarrEvent::SearchNewSeries(expected_search_query).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -550,7 +624,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::StartTask(Some(expected_task_name)).into(),
|
||||
SonarrEvent::StartTask(expected_task_name).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -577,7 +651,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::TestIndexer(Some(expected_indexer_id)).into(),
|
||||
SonarrEvent::TestIndexer(expected_indexer_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -616,5 +690,68 @@ mod tests {
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_toggle_episode_monitoring_command() {
|
||||
let expected_episode_id = 1;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::ToggleEpisodeMonitoring(expected_episode_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Sonarr(SonarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||
let toggle_episode_monitoring_command =
|
||||
SonarrCommand::ToggleEpisodeMonitoring { episode_id: 1 };
|
||||
|
||||
let result = SonarrCliHandler::with(
|
||||
&app_arc,
|
||||
toggle_episode_monitoring_command,
|
||||
&mut mock_network,
|
||||
)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_toggle_season_monitoring_command() {
|
||||
let expected_series_id = 1;
|
||||
let expected_season_number = 1;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::ToggleSeasonMonitoring((expected_series_id, expected_season_number)).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Sonarr(SonarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||
let toggle_season_monitoring_command = SonarrCommand::ToggleSeasonMonitoring {
|
||||
series_id: 1,
|
||||
season_number: 1,
|
||||
};
|
||||
|
||||
let result = SonarrCliHandler::with(
|
||||
&app_arc,
|
||||
toggle_season_monitoring_command,
|
||||
&mut mock_network,
|
||||
)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrTriggerAutomaticSearchCommand>
|
||||
SonarrTriggerAutomaticSearchCommand::Series { series_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::TriggerAutomaticSeriesSearch(Some(series_id)).into())
|
||||
.handle_network_event(SonarrEvent::TriggerAutomaticSeriesSearch(series_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
@@ -94,7 +94,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrTriggerAutomaticSearchCommand>
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(
|
||||
SonarrEvent::TriggerAutomaticSeasonSearch(Some((series_id, season_number))).into(),
|
||||
SonarrEvent::TriggerAutomaticSeasonSearch((series_id, season_number)).into(),
|
||||
)
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
@@ -102,7 +102,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrTriggerAutomaticSearchCommand>
|
||||
SonarrTriggerAutomaticSearchCommand::Episode { episode_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(SonarrEvent::TriggerAutomaticEpisodeSearch(Some(episode_id)).into())
|
||||
.handle_network_event(SonarrEvent::TriggerAutomaticEpisodeSearch(episode_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::TriggerAutomaticSeriesSearch(Some(expected_series_id)).into(),
|
||||
SonarrEvent::TriggerAutomaticSeriesSearch(expected_series_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
@@ -197,10 +197,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::TriggerAutomaticSeasonSearch(Some((
|
||||
expected_series_id,
|
||||
expected_season_number,
|
||||
)))
|
||||
SonarrEvent::TriggerAutomaticSeasonSearch((expected_series_id, expected_season_number))
|
||||
.into(),
|
||||
))
|
||||
.times(1)
|
||||
@@ -233,7 +230,7 @@ mod tests {
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
SonarrEvent::TriggerAutomaticEpisodeSearch(Some(expected_episode_id)).into(),
|
||||
SonarrEvent::TriggerAutomaticEpisodeSearch(expected_episode_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
|
||||
@@ -19,6 +19,7 @@ pub enum Key {
|
||||
Home,
|
||||
End,
|
||||
Tab,
|
||||
BackTab,
|
||||
Delete,
|
||||
Ctrl(char),
|
||||
Char(char),
|
||||
@@ -40,6 +41,7 @@ impl Display for Key {
|
||||
Key::Home => write!(f, "<home>"),
|
||||
Key::End => write!(f, "<end>"),
|
||||
Key::Tab => write!(f, "<tab>"),
|
||||
Key::BackTab => write!(f, "<shift-tab>"),
|
||||
Key::Delete => write!(f, "<del>"),
|
||||
_ => write!(f, "<{self:?}>"),
|
||||
}
|
||||
@@ -75,6 +77,11 @@ impl From<KeyEvent> for Key {
|
||||
KeyEvent {
|
||||
code: KeyCode::End, ..
|
||||
} => Key::End,
|
||||
KeyEvent {
|
||||
code: KeyCode::BackTab,
|
||||
modifiers: KeyModifiers::SHIFT,
|
||||
..
|
||||
} => Key::BackTab,
|
||||
KeyEvent {
|
||||
code: KeyCode::Tab, ..
|
||||
} => Key::Tab,
|
||||
|
||||
@@ -17,6 +17,7 @@ mod tests {
|
||||
#[case(Key::Home, "home")]
|
||||
#[case(Key::End, "end")]
|
||||
#[case(Key::Tab, "tab")]
|
||||
#[case(Key::BackTab, "shift-tab")]
|
||||
#[case(Key::Delete, "del")]
|
||||
#[case(Key::Char('q'), "q")]
|
||||
#[case(Key::Ctrl('q'), "ctrl-q")]
|
||||
@@ -67,6 +68,19 @@ mod tests {
|
||||
assert_eq!(Key::from(KeyEvent::from(KeyCode::Tab)), Key::Tab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_from_back_tab() {
|
||||
assert_eq!(
|
||||
Key::from(KeyEvent {
|
||||
code: KeyCode::BackTab,
|
||||
modifiers: KeyModifiers::SHIFT,
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::NONE
|
||||
}),
|
||||
Key::BackTab
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_from_delete() {
|
||||
assert_eq!(Key::from(KeyEvent::from(KeyCode::Delete)), Key::Delete);
|
||||
|
||||
@@ -99,86 +99,96 @@ mod test_utils {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_iterable_scroll {
|
||||
($func:ident, $handler:ident, $data_ref:ident, $block:expr, $context:expr) => {
|
||||
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $block:expr, $context:expr) => {
|
||||
#[rstest]
|
||||
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack($block.into());
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.$servarr_data
|
||||
.$data_ref
|
||||
.set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]);
|
||||
|
||||
$handler::with(&key, &mut app, &$block, &$context).handle();
|
||||
|
||||
assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 2");
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection(),
|
||||
"Test 2"
|
||||
);
|
||||
|
||||
$handler::with(&key, &mut app, &$block, &$context).handle();
|
||||
|
||||
assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 1");
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection(),
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
($func:ident, $handler:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => {
|
||||
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => {
|
||||
#[rstest]
|
||||
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack($block.into());
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.$servarr_data
|
||||
.$data_ref
|
||||
.set_items(simple_stateful_iterable_vec!($items));
|
||||
|
||||
$handler::with(&key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.$data_ref.current_selection().$field,
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection().$field,
|
||||
"Test 2"
|
||||
);
|
||||
|
||||
$handler::with(&key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.$data_ref.current_selection().$field,
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection().$field,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident) => {
|
||||
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident) => {
|
||||
#[rstest]
|
||||
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.$data_ref.set_items($items);
|
||||
app.push_navigation_stack($block.into());
|
||||
app.data.$servarr_data.$data_ref.set_items($items);
|
||||
|
||||
$handler::with(&key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.$data_ref.current_selection().$field,
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection().$field,
|
||||
"Test 2"
|
||||
);
|
||||
|
||||
$handler::with(&key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.$data_ref.current_selection().$field,
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection().$field,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => {
|
||||
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => {
|
||||
#[rstest]
|
||||
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.$data_ref.set_items($items);
|
||||
app.push_navigation_stack($block.into());
|
||||
app.data.$servarr_data.$data_ref.set_items($items);
|
||||
|
||||
$handler::with(&key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.$servarr_data
|
||||
.$data_ref
|
||||
.current_selection()
|
||||
.$field
|
||||
@@ -186,12 +196,12 @@ mod test_utils {
|
||||
"Test 2"
|
||||
);
|
||||
|
||||
$handler::with(&key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.$servarr_data
|
||||
.$data_ref
|
||||
.current_selection()
|
||||
.$field
|
||||
@@ -204,86 +214,96 @@ mod test_utils {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_iterable_home_and_end {
|
||||
($func:ident, $handler:ident, $data_ref:ident, $block:expr, $context:expr) => {
|
||||
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $block:expr, $context:expr) => {
|
||||
#[test]
|
||||
fn $func() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.$data_ref.set_items(vec![
|
||||
app.push_navigation_stack($block.into());
|
||||
app.data.$servarr_data.$data_ref.set_items(vec![
|
||||
"Test 1".to_owned(),
|
||||
"Test 2".to_owned(),
|
||||
"Test 3".to_owned(),
|
||||
]);
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(DEFAULT_KEYBINDINGS.end.key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 3");
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection(),
|
||||
"Test 3"
|
||||
);
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(DEFAULT_KEYBINDINGS.home.key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 1");
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection(),
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
($func:ident, $handler:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => {
|
||||
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => {
|
||||
#[test]
|
||||
fn $func() {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack($block.into());
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.$servarr_data
|
||||
.$data_ref
|
||||
.set_items(extended_stateful_iterable_vec!($items));
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(DEFAULT_KEYBINDINGS.end.key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.$data_ref.current_selection().$field,
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection().$field,
|
||||
"Test 3"
|
||||
);
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(DEFAULT_KEYBINDINGS.home.key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.$data_ref.current_selection().$field,
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection().$field,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident) => {
|
||||
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident) => {
|
||||
#[test]
|
||||
fn $func() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.$data_ref.set_items($items);
|
||||
app.push_navigation_stack($block.into());
|
||||
app.data.$servarr_data.$data_ref.set_items($items);
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(DEFAULT_KEYBINDINGS.end.key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.$data_ref.current_selection().$field,
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection().$field,
|
||||
"Test 3"
|
||||
);
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(DEFAULT_KEYBINDINGS.home.key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.$data_ref.current_selection().$field,
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app.data.$servarr_data.$data_ref.current_selection().$field,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => {
|
||||
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => {
|
||||
#[test]
|
||||
fn $func() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.$data_ref.set_items($items);
|
||||
app.push_navigation_stack($block.into());
|
||||
app.data.$servarr_data.$data_ref.set_items($items);
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(DEFAULT_KEYBINDINGS.end.key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.$servarr_data
|
||||
.$data_ref
|
||||
.current_selection()
|
||||
.$field
|
||||
@@ -291,12 +311,12 @@ mod test_utils {
|
||||
"Test 3"
|
||||
);
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle();
|
||||
$handler::with(DEFAULT_KEYBINDINGS.home.key, &mut app, $block, $context).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
pretty_assertions::assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.$servarr_data
|
||||
.$data_ref
|
||||
.current_selection()
|
||||
.$field
|
||||
@@ -311,18 +331,125 @@ mod test_utils {
|
||||
macro_rules! test_handler_delegation {
|
||||
($handler:ident, $base:expr, $active_block:expr) => {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack($base.clone().into());
|
||||
app.push_navigation_stack($active_block.clone().into());
|
||||
app.data.sonarr_data.history.set_items(vec![
|
||||
$crate::models::sonarr_models::SonarrHistoryItem::default(),
|
||||
]);
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.root_folders
|
||||
.set_items(vec![$crate::models::servarr_models::RootFolder::default()]);
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.indexers
|
||||
.set_items(vec![$crate::models::servarr_models::Indexer::default()]);
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.blocklist
|
||||
.set_items(vec![$crate::models::sonarr_models::BlocklistItem::default()]);
|
||||
app.data.sonarr_data.add_searched_series =
|
||||
Some($crate::models::stateful_table::StatefulTable::default());
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.movies
|
||||
.set_items(vec![$crate::models::radarr_models::Movie::default()]);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.set_items(vec![$crate::models::radarr_models::Collection::default()]);
|
||||
app.data.radarr_data.collection_movies.set_items(vec![
|
||||
$crate::models::radarr_models::CollectionMovie::default(),
|
||||
]);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexers
|
||||
.set_items(vec![$crate::models::servarr_models::Indexer::default()]);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.root_folders
|
||||
.set_items(vec![$crate::models::servarr_models::RootFolder::default()]);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.set_items(vec![$crate::models::radarr_models::BlocklistItem::default()]);
|
||||
app.data.radarr_data.add_searched_movies =
|
||||
Some($crate::models::stateful_table::StatefulTable::default());
|
||||
let mut movie_details_modal =
|
||||
$crate::models::servarr_data::radarr::modals::MovieDetailsModal::default();
|
||||
movie_details_modal.movie_history.set_items(vec![
|
||||
$crate::models::radarr_models::MovieHistoryItem::default(),
|
||||
]);
|
||||
movie_details_modal
|
||||
.movie_cast
|
||||
.set_items(vec![$crate::models::radarr_models::Credit::default()]);
|
||||
movie_details_modal
|
||||
.movie_crew
|
||||
.set_items(vec![$crate::models::radarr_models::Credit::default()]);
|
||||
movie_details_modal
|
||||
.movie_releases
|
||||
.set_items(vec![$crate::models::radarr_models::RadarrRelease::default()]);
|
||||
app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
|
||||
let mut season_details_modal =
|
||||
$crate::models::servarr_data::sonarr::modals::SeasonDetailsModal::default();
|
||||
season_details_modal.season_history.set_items(vec![
|
||||
$crate::models::sonarr_models::SonarrHistoryItem::default(),
|
||||
]);
|
||||
season_details_modal.episode_details_modal =
|
||||
Some($crate::models::servarr_data::sonarr::modals::EpisodeDetailsModal::default());
|
||||
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
|
||||
let mut series_history = $crate::models::stateful_table::StatefulTable::default();
|
||||
series_history.set_items(vec![
|
||||
$crate::models::sonarr_models::SonarrHistoryItem::default(),
|
||||
]);
|
||||
app.data.sonarr_data.series_history = Some(series_history);
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.series
|
||||
.set_items(vec![$crate::models::sonarr_models::Series::default()]);
|
||||
app.push_navigation_stack($base.into());
|
||||
app.push_navigation_stack($active_block.into());
|
||||
|
||||
$handler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&$active_block,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
$handler::with(DEFAULT_KEYBINDINGS.esc.key, &mut app, $active_block, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &$base.into());
|
||||
pretty_assertions::assert_eq!(app.get_current_route(), $base.into());
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_delete_prompt {
|
||||
($handler:ident, $block:expr, $expected_block:expr) => {
|
||||
let mut app = App::default();
|
||||
|
||||
$handler::with(DELETE_KEY, &mut app, $block, None).handle();
|
||||
|
||||
pretty_assertions::assert_eq!(app.get_current_route(), $expected_block.into());
|
||||
};
|
||||
|
||||
($handler:ident, $app:expr, $block:expr, $expected_block:expr) => {
|
||||
$handler::with(DELETE_KEY, &mut $app, $block, None).handle();
|
||||
|
||||
pretty_assertions::assert_eq!($app.get_current_route(), $expected_block.into());
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_refresh_key {
|
||||
($handler:ident, $block:expr) => {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack($block.into());
|
||||
|
||||
$handler::with(DEFAULT_KEYBINDINGS.refresh.key, &mut app, $block, None).handle();
|
||||
|
||||
pretty_assertions::assert_eq!(app.get_current_route(), $block.into());
|
||||
assert!(app.should_refresh);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::models::radarr_models::Movie;
|
||||
use crate::models::sonarr_models::Series;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::handle_events;
|
||||
use crate::handlers::{handle_clear_errors, handle_prompt_toggle};
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use crate::models::Route;
|
||||
|
||||
#[test]
|
||||
fn test_handle_clear_errors() {
|
||||
@@ -17,17 +27,90 @@ mod tests {
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_handle_prompt_toggle_left_right(#[values(Key::Left, Key::Right)] key: Key) {
|
||||
#[case(ActiveRadarrBlock::Movies.into(), ActiveRadarrBlock::SearchMovie.into())]
|
||||
#[case(ActiveSonarrBlock::Series.into(), ActiveSonarrBlock::SearchSeries.into())]
|
||||
fn test_handle_events(#[case] base_block: Route, #[case] top_block: Route) {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack(base_block);
|
||||
app.push_navigation_stack(top_block);
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.series
|
||||
.set_items(vec![Series::default()]);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.movies
|
||||
.set_items(vec![Movie::default()]);
|
||||
|
||||
handle_events(DEFAULT_KEYBINDINGS.esc.key, &mut app);
|
||||
|
||||
assert_eq!(app.get_current_route(), base_block);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(0, ActiveSonarrBlock::Series, ActiveSonarrBlock::Series)]
|
||||
#[case(1, ActiveRadarrBlock::Movies, ActiveRadarrBlock::Movies)]
|
||||
fn test_handle_change_tabs<T>(#[case] index: usize, #[case] left_block: T, #[case] right_block: T)
|
||||
where
|
||||
T: Into<Route> + Copy,
|
||||
{
|
||||
let mut app = App::default();
|
||||
app.error = "Test".into();
|
||||
app.server_tabs.set_index(index);
|
||||
|
||||
handle_events(DEFAULT_KEYBINDINGS.previous_servarr.key, &mut app);
|
||||
|
||||
assert_eq!(app.server_tabs.get_active_route(), left_block.into());
|
||||
assert_eq!(app.get_current_route(), left_block.into());
|
||||
assert!(app.is_first_render);
|
||||
assert_eq!(app.error, HorizontallyScrollableText::default());
|
||||
assert!(app.cancellation_token.is_cancelled());
|
||||
|
||||
app.server_tabs.set_index(index);
|
||||
app.is_first_render = false;
|
||||
app.error = "Test".into();
|
||||
app.cancellation_token = CancellationToken::new();
|
||||
|
||||
handle_events(DEFAULT_KEYBINDINGS.next_servarr.key, &mut app);
|
||||
|
||||
assert_eq!(app.server_tabs.get_active_route(), right_block.into());
|
||||
assert_eq!(app.get_current_route(), right_block.into());
|
||||
assert!(app.is_first_render);
|
||||
assert_eq!(app.error, HorizontallyScrollableText::default());
|
||||
assert!(app.cancellation_token.is_cancelled());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_handle_prompt_toggle_left_right_radarr(#[values(Key::Left, Key::Right)] key: Key) {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
|
||||
handle_prompt_toggle(&mut app, &key);
|
||||
handle_prompt_toggle(&mut app, key);
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
|
||||
handle_prompt_toggle(&mut app, &key);
|
||||
handle_prompt_toggle(&mut app, key);
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_handle_prompt_toggle_left_right_sonarr(#[values(Key::Left, Key::Right)] key: Key) {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
||||
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
|
||||
handle_prompt_toggle(&mut app, key);
|
||||
|
||||
assert!(app.data.sonarr_data.prompt_confirm);
|
||||
|
||||
handle_prompt_toggle(&mut app, key);
|
||||
|
||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use radarr_handlers::RadarrHandler;
|
||||
use sonarr_handlers::SonarrHandler;
|
||||
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
@@ -6,6 +7,7 @@ use crate::event::Key;
|
||||
use crate::models::{HorizontallyScrollableText, Route};
|
||||
|
||||
mod radarr_handlers;
|
||||
mod sonarr_handlers;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "handlers_tests.rs"]
|
||||
@@ -14,45 +16,46 @@ mod handlers_tests;
|
||||
#[cfg(test)]
|
||||
#[path = "handler_test_utils.rs"]
|
||||
pub mod handler_test_utils;
|
||||
mod table_handler;
|
||||
|
||||
pub trait KeyEventHandler<'a, 'b, T: Into<Route>> {
|
||||
pub trait KeyEventHandler<'a, 'b, T: Into<Route> + Copy> {
|
||||
fn handle_key_event(&mut self) {
|
||||
let key = self.get_key();
|
||||
match key {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.up.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.up.key => {
|
||||
if self.is_ready() {
|
||||
self.handle_scroll_up();
|
||||
}
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.down.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.down.key => {
|
||||
if self.is_ready() {
|
||||
self.handle_scroll_down();
|
||||
}
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.home.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.home.key => {
|
||||
if self.is_ready() {
|
||||
self.handle_home();
|
||||
}
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.end.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.end.key => {
|
||||
if self.is_ready() {
|
||||
self.handle_end();
|
||||
}
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.delete.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.delete.key => {
|
||||
if self.is_ready() {
|
||||
self.handle_delete();
|
||||
}
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.left.key || *key == DEFAULT_KEYBINDINGS.right.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.left.key || key == DEFAULT_KEYBINDINGS.right.key => {
|
||||
self.handle_left_right_action()
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.submit.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.submit.key => {
|
||||
if self.is_ready() {
|
||||
self.handle_submit();
|
||||
}
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.esc.key => self.handle_esc(),
|
||||
_ if key == DEFAULT_KEYBINDINGS.esc.key => self.handle_esc(),
|
||||
_ => {
|
||||
if self.is_ready() {
|
||||
self.handle_char_key_event();
|
||||
@@ -65,9 +68,9 @@ pub trait KeyEventHandler<'a, 'b, T: Into<Route>> {
|
||||
self.handle_key_event();
|
||||
}
|
||||
|
||||
fn accepts(active_block: &'a T) -> bool;
|
||||
fn with(key: &'a Key, app: &'a mut App<'b>, active_block: &'a T, context: &'a Option<T>) -> Self;
|
||||
fn get_key(&self) -> &Key;
|
||||
fn accepts(active_block: T) -> bool;
|
||||
fn with(key: Key, app: &'a mut App<'b>, active_block: T, context: Option<T>) -> Self;
|
||||
fn get_key(&self) -> Key;
|
||||
fn is_ready(&self) -> bool;
|
||||
fn handle_scroll_up(&mut self);
|
||||
fn handle_scroll_down(&mut self);
|
||||
@@ -81,8 +84,26 @@ pub trait KeyEventHandler<'a, 'b, T: Into<Route>> {
|
||||
}
|
||||
|
||||
pub fn handle_events(key: Key, app: &mut App<'_>) {
|
||||
if let Route::Radarr(active_radarr_block, context) = *app.get_current_route() {
|
||||
RadarrHandler::with(&key, app, &active_radarr_block, &context).handle()
|
||||
if key == DEFAULT_KEYBINDINGS.next_servarr.key {
|
||||
app.reset();
|
||||
app.server_tabs.next();
|
||||
app.pop_and_push_navigation_stack(app.server_tabs.get_active_route());
|
||||
app.cancellation_token.cancel();
|
||||
} else if key == DEFAULT_KEYBINDINGS.previous_servarr.key {
|
||||
app.reset();
|
||||
app.server_tabs.previous();
|
||||
app.pop_and_push_navigation_stack(app.server_tabs.get_active_route());
|
||||
app.cancellation_token.cancel();
|
||||
} else {
|
||||
match app.get_current_route() {
|
||||
Route::Radarr(active_radarr_block, context) => {
|
||||
RadarrHandler::with(key, app, active_radarr_block, context).handle()
|
||||
}
|
||||
Route::Sonarr(active_sonarr_block, context) => {
|
||||
SonarrHandler::with(key, app, active_sonarr_block, context).handle()
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,11 +113,17 @@ fn handle_clear_errors(app: &mut App<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_prompt_toggle(app: &mut App<'_>, key: &Key) {
|
||||
fn handle_prompt_toggle(app: &mut App<'_>, key: Key) {
|
||||
match key {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.left.key || *key == DEFAULT_KEYBINDINGS.right.key => {
|
||||
if let Route::Radarr(_, _) = *app.get_current_route() {
|
||||
app.data.radarr_data.prompt_confirm = !app.data.radarr_data.prompt_confirm;
|
||||
_ if key == DEFAULT_KEYBINDINGS.left.key || key == DEFAULT_KEYBINDINGS.right.key => {
|
||||
match app.get_current_route() {
|
||||
Route::Radarr(_, _) => {
|
||||
app.data.radarr_data.prompt_confirm = !app.data.radarr_data.prompt_confirm
|
||||
}
|
||||
Route::Sonarr(_, _) => {
|
||||
app.data.sonarr_data.prompt_confirm = !app.data.sonarr_data.prompt_confirm
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
@@ -107,10 +134,10 @@ fn handle_prompt_toggle(app: &mut App<'_>, key: &Key) {
|
||||
macro_rules! handle_text_box_left_right_keys {
|
||||
($self:expr, $key:expr, $input:expr) => {
|
||||
match $self.key {
|
||||
_ if *$key == DEFAULT_KEYBINDINGS.left.key => {
|
||||
_ if $key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.left.key => {
|
||||
$input.scroll_left();
|
||||
}
|
||||
_ if *$key == DEFAULT_KEYBINDINGS.right.key => {
|
||||
_ if $key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.right.key => {
|
||||
$input.scroll_right();
|
||||
}
|
||||
_ => (),
|
||||
@@ -122,13 +149,26 @@ macro_rules! handle_text_box_left_right_keys {
|
||||
macro_rules! handle_text_box_keys {
|
||||
($self:expr, $key:expr, $input:expr) => {
|
||||
match $self.key {
|
||||
_ if *$key == DEFAULT_KEYBINDINGS.backspace.key => {
|
||||
_ if $key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.backspace.key => {
|
||||
$input.pop();
|
||||
}
|
||||
Key::Char(character) => {
|
||||
$input.push(*character);
|
||||
$input.push(character);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! handle_prompt_left_right_keys {
|
||||
($self:expr, $confirm_prompt:expr, $data:ident) => {
|
||||
if $self.app.data.$data.selected_block.get_active_block() == $confirm_prompt {
|
||||
handle_prompt_toggle($self.app, $self.key);
|
||||
} else if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.left.key {
|
||||
$self.app.data.$data.selected_block.left();
|
||||
} else {
|
||||
$self.app.data.$data.selected_block.right();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,246 +14,6 @@ mod tests {
|
||||
use crate::models::radarr_models::{BlocklistItem, BlocklistItemMovie};
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS};
|
||||
use crate::models::servarr_models::{Language, Quality, QualityWrapper};
|
||||
use crate::models::stateful_table::SortOption;
|
||||
|
||||
mod test_handle_scroll_up_and_down {
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::models::radarr_models::BlocklistItem;
|
||||
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
|
||||
|
||||
use super::*;
|
||||
|
||||
test_iterable_scroll!(
|
||||
test_blocklist_scroll,
|
||||
BlocklistHandler,
|
||||
blocklist,
|
||||
simple_stateful_iterable_vec!(BlocklistItem, String, source_title),
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
source_title,
|
||||
to_string
|
||||
);
|
||||
|
||||
#[rstest]
|
||||
fn test_blocklist_scroll_no_op_when_not_ready(
|
||||
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
|
||||
) {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.set_items(simple_stateful_iterable_vec!(
|
||||
BlocklistItem,
|
||||
String,
|
||||
source_title
|
||||
));
|
||||
|
||||
BlocklistHandler::with(&key, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.current_selection()
|
||||
.source_title
|
||||
.to_string(),
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
BlocklistHandler::with(&key, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.current_selection()
|
||||
.source_title
|
||||
.to_string(),
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_blocklist_sort_scroll(
|
||||
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
|
||||
) {
|
||||
let blocklist_field_vec = sort_options();
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.blocklist.sorting(sort_options());
|
||||
|
||||
if key == Key::Up {
|
||||
for i in (0..blocklist_field_vec.len()).rev() {
|
||||
BlocklistHandler::with(
|
||||
&key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::BlocklistSortPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sort
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection(),
|
||||
&blocklist_field_vec[i]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for i in 0..blocklist_field_vec.len() {
|
||||
BlocklistHandler::with(
|
||||
&key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::BlocklistSortPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sort
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection(),
|
||||
&blocklist_field_vec[(i + 1) % blocklist_field_vec.len()]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_home_end {
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
|
||||
use crate::models::radarr_models::BlocklistItem;
|
||||
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
|
||||
|
||||
use super::*;
|
||||
|
||||
test_iterable_home_and_end!(
|
||||
test_blocklist_home_and_end,
|
||||
BlocklistHandler,
|
||||
blocklist,
|
||||
extended_stateful_iterable_vec!(BlocklistItem, String, source_title),
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
source_title,
|
||||
to_string
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn test_blocklist_home_and_end_no_op_when_not_ready() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.set_items(extended_stateful_iterable_vec!(
|
||||
BlocklistItem,
|
||||
String,
|
||||
source_title
|
||||
));
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.current_selection()
|
||||
.source_title
|
||||
.to_string(),
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.current_selection()
|
||||
.source_title
|
||||
.to_string(),
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blocklist_sort_home_end() {
|
||||
let blocklist_field_vec = sort_options();
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.blocklist.sorting(sort_options());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::BlocklistSortPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sort
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection(),
|
||||
&blocklist_field_vec[blocklist_field_vec.len() - 1]
|
||||
);
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::BlocklistSortPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sort
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection(),
|
||||
&blocklist_field_vec[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_delete {
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -267,11 +27,11 @@ mod tests {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
|
||||
BlocklistHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
|
||||
BlocklistHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::DeleteBlocklistItemPrompt.into()
|
||||
ActiveRadarrBlock::DeleteBlocklistItemPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -282,12 +42,9 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
|
||||
BlocklistHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
|
||||
BlocklistHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,21 +61,18 @@ mod tests {
|
||||
app.data.radarr_data.main_tabs.set_index(3);
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.left.key,
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
&ActiveRadarrBlock::Downloads.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Downloads.into()
|
||||
ActiveRadarrBlock::Downloads.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@@ -328,20 +82,20 @@ mod tests {
|
||||
app.data.radarr_data.main_tabs.set_index(3);
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.right.key,
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
&ActiveRadarrBlock::RootFolders.into()
|
||||
ActiveRadarrBlock::RootFolders.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::RootFolders.into()
|
||||
ActiveRadarrBlock::RootFolders.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -356,11 +110,11 @@ mod tests {
|
||||
) {
|
||||
let mut app = App::default();
|
||||
|
||||
BlocklistHandler::with(&key, &mut app, &active_radarr_block, &None).handle();
|
||||
BlocklistHandler::with(key, &mut app, active_radarr_block, None).handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
|
||||
BlocklistHandler::with(&key, &mut app, &active_radarr_block, &None).handle();
|
||||
BlocklistHandler::with(key, &mut app, active_radarr_block, None).handle();
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
@@ -382,11 +136,11 @@ mod tests {
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
|
||||
BlocklistHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
|
||||
BlocklistHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::BlocklistItemDetails.into()
|
||||
ActiveRadarrBlock::BlocklistItemDetails.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -397,19 +151,16 @@ mod tests {
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
|
||||
BlocklistHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
|
||||
BlocklistHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
ActiveRadarrBlock::DeleteBlocklistItemPrompt,
|
||||
RadarrEvent::DeleteBlocklistItem(None)
|
||||
RadarrEvent::DeleteBlocklistItem(3)
|
||||
)]
|
||||
#[case(
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
@@ -427,14 +178,14 @@ mod tests {
|
||||
app.push_navigation_stack(base_route.into());
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
|
||||
BlocklistHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle();
|
||||
BlocklistHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(expected_action)
|
||||
);
|
||||
assert_eq!(app.get_current_route(), &base_route.into());
|
||||
assert_eq!(app.get_current_route(), base_route.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@@ -450,42 +201,11 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
|
||||
BlocklistHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle();
|
||||
BlocklistHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blocklist_sort_prompt_submit() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.blocklist.sort_asc = true;
|
||||
app.data.radarr_data.blocklist.sorting(sort_options());
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::BlocklistSortPrompt.into());
|
||||
|
||||
let mut expected_vec = blocklist_vec();
|
||||
expected_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
expected_vec.reverse();
|
||||
|
||||
BlocklistHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::BlocklistSortPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_eq!(app.data.radarr_data.blocklist.items, expected_vec);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,9 +237,9 @@ mod tests {
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
|
||||
BlocklistHandler::with(&ESC_KEY, &mut app, &prompt_block, &None).handle();
|
||||
BlocklistHandler::with(ESC_KEY, &mut app, prompt_block, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &base_block.into());
|
||||
assert_eq!(app.get_current_route(), base_block.into());
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
@@ -530,37 +250,14 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::BlocklistItemDetails.into());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&ESC_KEY,
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::BlocklistItemDetails,
|
||||
&None,
|
||||
ActiveRadarrBlock::BlocklistItemDetails,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blocklist_sort_prompt_block_esc() {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::BlocklistSortPrompt.into());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::BlocklistSortPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@@ -571,12 +268,9 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
|
||||
DownloadsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
|
||||
DownloadsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
|
||||
assert!(app.error.text.is_empty());
|
||||
}
|
||||
}
|
||||
@@ -596,17 +290,14 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.refresh.key,
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
|
||||
assert!(app.should_refresh);
|
||||
}
|
||||
|
||||
@@ -618,17 +309,14 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.refresh.key,
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
|
||||
@@ -638,16 +326,16 @@ mod tests {
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.clear.key,
|
||||
DEFAULT_KEYBINDINGS.clear.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::BlocklistClearAllItemsPrompt.into()
|
||||
ActiveRadarrBlock::BlocklistClearAllItemsPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -659,71 +347,21 @@ mod tests {
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.clear.key,
|
||||
DEFAULT_KEYBINDINGS.clear.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_key() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.sort.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::BlocklistSortPrompt.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.blocklist.sort.as_ref().unwrap().items,
|
||||
blocklist_sorting_options()
|
||||
);
|
||||
assert!(!app.data.radarr_data.blocklist.sort_asc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_key_no_op_when_not_ready() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.sort.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert!(app.data.radarr_data.blocklist.sort.is_none());
|
||||
assert!(!app.data.radarr_data.blocklist.sort_asc);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
ActiveRadarrBlock::DeleteBlocklistItemPrompt,
|
||||
RadarrEvent::DeleteBlocklistItem(None)
|
||||
RadarrEvent::DeleteBlocklistItem(3)
|
||||
)]
|
||||
#[case(
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
@@ -741,10 +379,10 @@ mod tests {
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
|
||||
BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.confirm.key,
|
||||
DEFAULT_KEYBINDINGS.confirm.key,
|
||||
&mut app,
|
||||
&prompt_block,
|
||||
&None,
|
||||
prompt_block,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -753,7 +391,7 @@ mod tests {
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(expected_action)
|
||||
);
|
||||
assert_eq!(app.get_current_route(), &base_route.into());
|
||||
assert_eq!(app.get_current_route(), base_route.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -896,23 +534,39 @@ mod tests {
|
||||
fn test_blocklist_handler_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if BLOCKLIST_BLOCKS.contains(&active_radarr_block) {
|
||||
assert!(BlocklistHandler::accepts(&active_radarr_block));
|
||||
assert!(BlocklistHandler::accepts(active_radarr_block));
|
||||
} else {
|
||||
assert!(!BlocklistHandler::accepts(&active_radarr_block));
|
||||
assert!(!BlocklistHandler::accepts(active_radarr_block));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_blocklist_item_id() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.blocklist.set_items(blocklist_vec());
|
||||
|
||||
let blocklist_item_id = BlocklistHandler::with(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
)
|
||||
.extract_blocklist_item_id();
|
||||
|
||||
assert_eq!(blocklist_item_id, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blocklist_handler_not_ready_when_loading() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -924,10 +578,10 @@ mod tests {
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -944,10 +598,10 @@ mod tests {
|
||||
.set_items(vec![BlocklistItem::default()]);
|
||||
|
||||
let handler = BlocklistHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Blocklist,
|
||||
&None,
|
||||
ActiveRadarrBlock::Blocklist,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
@@ -1029,15 +683,4 @@ mod tests {
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn sort_options() -> Vec<SortOption<BlocklistItem>> {
|
||||
vec![SortOption {
|
||||
name: "Test 1",
|
||||
cmp_fn: Some(|a, b| {
|
||||
b.source_title
|
||||
.to_lowercase()
|
||||
.cmp(&a.source_title.to_lowercase())
|
||||
}),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handle_table_events;
|
||||
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
|
||||
use crate::handlers::table_handler::TableHandlingConfig;
|
||||
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
|
||||
use crate::models::radarr_models::BlocklistItem;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS};
|
||||
use crate::models::stateful_table::SortOption;
|
||||
use crate::models::Scrollable;
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -14,22 +15,47 @@ use crate::network::radarr_network::RadarrEvent;
|
||||
mod blocklist_handler_tests;
|
||||
|
||||
pub(super) struct BlocklistHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> BlocklistHandler<'a, 'b> {
|
||||
handle_table_events!(
|
||||
self,
|
||||
blocklist,
|
||||
self.app.data.radarr_data.blocklist,
|
||||
BlocklistItem
|
||||
);
|
||||
|
||||
fn extract_blocklist_item_id(&self) -> i64 {
|
||||
self.app.data.radarr_data.blocklist.current_selection().id
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a, 'b> {
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
BLOCKLIST_BLOCKS.contains(active_block)
|
||||
fn handle(&mut self) {
|
||||
let blocklist_table_handling_config =
|
||||
TableHandlingConfig::new(ActiveRadarrBlock::Blocklist.into())
|
||||
.sorting_block(ActiveRadarrBlock::BlocklistSortPrompt.into())
|
||||
.sort_by_fn(|a: &BlocklistItem, b: &BlocklistItem| a.id.cmp(&b.id))
|
||||
.sort_options(blocklist_sorting_options());
|
||||
|
||||
if !self.handle_blocklist_table_events(blocklist_table_handling_config) {
|
||||
self.handle_key_event();
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
BLOCKLIST_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
) -> Self {
|
||||
BlocklistHandler {
|
||||
key,
|
||||
@@ -39,7 +65,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -47,72 +73,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
|
||||
!self.app.is_loading && !self.app.data.radarr_data.blocklist.is_empty()
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Blocklist => self.app.data.radarr_data.blocklist.scroll_up(),
|
||||
ActiveRadarrBlock::BlocklistSortPrompt => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sort
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_up(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn handle_scroll_up(&mut self) {}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Blocklist => self.app.data.radarr_data.blocklist.scroll_down(),
|
||||
ActiveRadarrBlock::BlocklistSortPrompt => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sort
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_down(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn handle_scroll_down(&mut self) {}
|
||||
|
||||
fn handle_home(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Blocklist => self.app.data.radarr_data.blocklist.scroll_to_top(),
|
||||
ActiveRadarrBlock::BlocklistSortPrompt => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sort
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_to_top(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn handle_home(&mut self) {}
|
||||
|
||||
fn handle_end(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Blocklist => self.app.data.radarr_data.blocklist.scroll_to_bottom(),
|
||||
ActiveRadarrBlock::BlocklistSortPrompt => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sort
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_to_bottom(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn handle_end(&mut self) {}
|
||||
|
||||
fn handle_delete(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Blocklist {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::Blocklist {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::DeleteBlocklistItemPrompt.into());
|
||||
@@ -132,8 +102,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::DeleteBlocklistItemPrompt => {
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::DeleteBlocklistItem(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteBlocklistItem(
|
||||
self.extract_blocklist_item_id(),
|
||||
));
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
@@ -145,18 +116,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
ActiveRadarrBlock::BlocklistSortPrompt => {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.items
|
||||
.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
self.app.data.radarr_data.blocklist.apply_sorting();
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
ActiveRadarrBlock::Blocklist => {
|
||||
self
|
||||
.app
|
||||
@@ -173,7 +132,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.radarr_data.prompt_confirm = false;
|
||||
}
|
||||
ActiveRadarrBlock::BlocklistItemDetails | ActiveRadarrBlock::BlocklistSortPrompt => {
|
||||
ActiveRadarrBlock::BlocklistItemDetails => {
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
_ => handle_clear_errors(self.app),
|
||||
@@ -184,38 +143,28 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
|
||||
let key = self.key;
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Blocklist => match self.key {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.clear.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.clear.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::BlocklistClearAllItemsPrompt.into());
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.sorting(blocklist_sorting_options());
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::BlocklistSortPrompt.into());
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveRadarrBlock::DeleteBlocklistItemPrompt => {
|
||||
if *key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
if key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::DeleteBlocklistItem(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteBlocklistItem(
|
||||
self.extract_blocklist_item_id(),
|
||||
));
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
}
|
||||
ActiveRadarrBlock::BlocklistClearAllItemsPrompt => {
|
||||
if *key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
if key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::ClearBlocklist);
|
||||
|
||||
|
||||
@@ -1,35 +1,56 @@
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handle_table_events;
|
||||
use crate::handlers::table_handler::TableHandlingConfig;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::models::radarr_models::CollectionMovie;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
ActiveRadarrBlock, ADD_MOVIE_SELECTION_BLOCKS, COLLECTION_DETAILS_BLOCKS,
|
||||
EDIT_COLLECTION_SELECTION_BLOCKS,
|
||||
};
|
||||
use crate::models::stateful_table::StatefulTable;
|
||||
use crate::models::{BlockSelectionState, Scrollable};
|
||||
use crate::models::BlockSelectionState;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "collection_details_handler_tests.rs"]
|
||||
mod collection_details_handler_tests;
|
||||
|
||||
pub(super) struct CollectionDetailsHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> CollectionDetailsHandler<'a, 'b> {
|
||||
handle_table_events!(
|
||||
self,
|
||||
collection_movies,
|
||||
self.app.data.radarr_data.collection_movies,
|
||||
CollectionMovie
|
||||
);
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHandler<'a, 'b> {
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
COLLECTION_DETAILS_BLOCKS.contains(active_block)
|
||||
fn handle(&mut self) {
|
||||
let collection_movies_table_handling_config =
|
||||
TableHandlingConfig::new(ActiveRadarrBlock::CollectionDetails.into());
|
||||
|
||||
if !self.handle_collection_movies_table_events(collection_movies_table_handling_config) {
|
||||
self.handle_key_event();
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
COLLECTION_DETAILS_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
) -> CollectionDetailsHandler<'a, 'b> {
|
||||
CollectionDetailsHandler {
|
||||
key,
|
||||
@@ -39,7 +60,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -47,41 +68,20 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan
|
||||
!self.app.is_loading && !self.app.data.radarr_data.collection_movies.is_empty()
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
|
||||
self.app.data.radarr_data.collection_movies.scroll_up()
|
||||
}
|
||||
}
|
||||
fn handle_scroll_up(&mut self) {}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
|
||||
self.app.data.radarr_data.collection_movies.scroll_down()
|
||||
}
|
||||
}
|
||||
fn handle_scroll_down(&mut self) {}
|
||||
|
||||
fn handle_home(&mut self) {
|
||||
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
|
||||
self.app.data.radarr_data.collection_movies.scroll_to_top();
|
||||
}
|
||||
}
|
||||
fn handle_home(&mut self) {}
|
||||
|
||||
fn handle_end(&mut self) {
|
||||
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collection_movies
|
||||
.scroll_to_bottom();
|
||||
}
|
||||
}
|
||||
fn handle_end(&mut self) {}
|
||||
|
||||
fn handle_delete(&mut self) {}
|
||||
|
||||
fn handle_left_right_action(&mut self) {}
|
||||
|
||||
fn handle_submit(&mut self) {
|
||||
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
|
||||
if ActiveRadarrBlock::CollectionDetails == self.active_radarr_block {
|
||||
let tmdb_id = self
|
||||
.app
|
||||
.data
|
||||
@@ -111,7 +111,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan
|
||||
.into(),
|
||||
);
|
||||
self.app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
|
||||
self.app.data.radarr_data.add_movie_modal = Some((&self.app.data.radarr_data).into());
|
||||
}
|
||||
}
|
||||
@@ -129,19 +129,19 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan
|
||||
}
|
||||
|
||||
fn handle_char_key_event(&mut self) {
|
||||
if *self.active_radarr_block == ActiveRadarrBlock::CollectionDetails
|
||||
&& *self.key == DEFAULT_KEYBINDINGS.edit.key
|
||||
if self.active_radarr_block == ActiveRadarrBlock::CollectionDetails
|
||||
&& self.key == DEFAULT_KEYBINDINGS.edit.key
|
||||
{
|
||||
self.app.push_navigation_stack(
|
||||
(
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
Some(*self.active_radarr_block),
|
||||
Some(self.active_radarr_block),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
self.app.data.radarr_data.edit_collection_modal = Some((&self.app.data.radarr_data).into());
|
||||
self.app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,142 +12,6 @@ mod tests {
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
ActiveRadarrBlock, COLLECTION_DETAILS_BLOCKS,
|
||||
};
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
|
||||
mod test_handle_scroll_up_and_down {
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
|
||||
|
||||
use super::*;
|
||||
|
||||
test_iterable_scroll!(
|
||||
test_collection_details_scroll,
|
||||
CollectionDetailsHandler,
|
||||
collection_movies,
|
||||
simple_stateful_iterable_vec!(CollectionMovie, HorizontallyScrollableText),
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
title,
|
||||
to_string
|
||||
);
|
||||
|
||||
#[rstest]
|
||||
fn test_collection_details_scroll_no_op_when_not_ready(
|
||||
#[values(
|
||||
DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key
|
||||
)]
|
||||
key: Key,
|
||||
) {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.collection_movies
|
||||
.set_items(simple_stateful_iterable_vec!(
|
||||
CollectionMovie,
|
||||
HorizontallyScrollableText
|
||||
));
|
||||
|
||||
CollectionDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::CollectionDetails, &None)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.collection_movies
|
||||
.current_selection()
|
||||
.title
|
||||
.to_string(),
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
CollectionDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::CollectionDetails, &None)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.collection_movies
|
||||
.current_selection()
|
||||
.title
|
||||
.to_string(),
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_home_end {
|
||||
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
|
||||
|
||||
use super::*;
|
||||
|
||||
test_iterable_home_and_end!(
|
||||
test_collection_details_home_end,
|
||||
CollectionDetailsHandler,
|
||||
collection_movies,
|
||||
extended_stateful_iterable_vec!(CollectionMovie, HorizontallyScrollableText),
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
title,
|
||||
to_string
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn test_collection_details_home_end_no_op_when_not_ready() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.collection_movies
|
||||
.set_items(extended_stateful_iterable_vec!(
|
||||
CollectionMovie,
|
||||
HorizontallyScrollableText
|
||||
));
|
||||
|
||||
CollectionDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.collection_movies
|
||||
.current_selection()
|
||||
.title
|
||||
.to_string(),
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
CollectionDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.collection_movies
|
||||
.current_selection()
|
||||
.title
|
||||
.to_string(),
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_submit {
|
||||
use bimap::BiMap;
|
||||
@@ -171,24 +35,24 @@ mod tests {
|
||||
.set_items(vec![CollectionMovie::default()]);
|
||||
app.data.radarr_data.quality_profile_map =
|
||||
BiMap::from_iter([(1, "B - Test 2".to_owned()), (0, "A - Test 1".to_owned())]);
|
||||
app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block = BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
|
||||
.set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
|
||||
|
||||
CollectionDetailsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&(
|
||||
(
|
||||
ActiveRadarrBlock::AddMoviePrompt,
|
||||
Some(ActiveRadarrBlock::CollectionDetails)
|
||||
)
|
||||
@@ -205,7 +69,7 @@ mod tests {
|
||||
.is_empty());
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::AddMovieSelectRootFolder
|
||||
ActiveRadarrBlock::AddMovieSelectRootFolder
|
||||
);
|
||||
assert!(!app
|
||||
.data
|
||||
@@ -250,16 +114,16 @@ mod tests {
|
||||
.set_items(vec![CollectionMovie::default()]);
|
||||
|
||||
CollectionDetailsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::CollectionDetails.into()
|
||||
ActiveRadarrBlock::CollectionDetails.into()
|
||||
);
|
||||
assert!(app.data.radarr_data.add_movie_modal.is_none());
|
||||
}
|
||||
@@ -279,16 +143,16 @@ mod tests {
|
||||
.set_items(vec![Movie::default()]);
|
||||
|
||||
CollectionDetailsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::ViewMovieOverview.into()
|
||||
ActiveRadarrBlock::ViewMovieOverview.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -313,16 +177,16 @@ mod tests {
|
||||
.set_items(vec![CollectionMovie::default()]);
|
||||
|
||||
CollectionDetailsHandler::with(
|
||||
&ESC_KEY,
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Collections.into()
|
||||
ActiveRadarrBlock::Collections.into()
|
||||
);
|
||||
assert!(app.data.radarr_data.collection_movies.items.is_empty());
|
||||
}
|
||||
@@ -334,16 +198,16 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::ViewMovieOverview.into());
|
||||
|
||||
CollectionDetailsHandler::with(
|
||||
&ESC_KEY,
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::ViewMovieOverview,
|
||||
&None,
|
||||
ActiveRadarrBlock::ViewMovieOverview,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::CollectionDetails.into()
|
||||
ActiveRadarrBlock::CollectionDetails.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -367,7 +231,7 @@ mod tests {
|
||||
test_edit_collection_key!(
|
||||
CollectionDetailsHandler,
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
ActiveRadarrBlock::CollectionDetails
|
||||
Some(ActiveRadarrBlock::CollectionDetails)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -388,16 +252,16 @@ mod tests {
|
||||
app.data.radarr_data = radarr_data;
|
||||
|
||||
CollectionDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.edit.key,
|
||||
DEFAULT_KEYBINDINGS.edit.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::CollectionDetails.into()
|
||||
ActiveRadarrBlock::CollectionDetails.into()
|
||||
);
|
||||
assert!(app.data.radarr_data.edit_collection_modal.is_none());
|
||||
}
|
||||
@@ -407,9 +271,9 @@ mod tests {
|
||||
fn test_collection_details_handler_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if COLLECTION_DETAILS_BLOCKS.contains(&active_radarr_block) {
|
||||
assert!(CollectionDetailsHandler::accepts(&active_radarr_block));
|
||||
assert!(CollectionDetailsHandler::accepts(active_radarr_block));
|
||||
} else {
|
||||
assert!(!CollectionDetailsHandler::accepts(&active_radarr_block));
|
||||
assert!(!CollectionDetailsHandler::accepts(active_radarr_block));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -420,10 +284,10 @@ mod tests {
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = CollectionDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -435,10 +299,10 @@ mod tests {
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = CollectionDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -455,10 +319,10 @@ mod tests {
|
||||
.set_items(vec![CollectionMovie::default()]);
|
||||
|
||||
let handler = CollectionDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::CollectionDetails,
|
||||
&None,
|
||||
ActiveRadarrBlock::CollectionDetails,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
|
||||
@@ -2,6 +2,8 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
|
||||
use crate::models::radarr_models::EditCollectionParams;
|
||||
use crate::models::servarr_data::radarr::modals::EditCollectionModal;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_COLLECTION_BLOCKS};
|
||||
use crate::models::Scrollable;
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
@@ -12,22 +14,67 @@ use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
|
||||
mod edit_collection_handler_tests;
|
||||
|
||||
pub(super) struct EditCollectionHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> EditCollectionHandler<'a, 'b> {
|
||||
fn build_edit_collection_params(&mut self) -> EditCollectionParams {
|
||||
let collection_id = self.app.data.radarr_data.collections.current_selection().id;
|
||||
let EditCollectionModal {
|
||||
path,
|
||||
search_on_add,
|
||||
minimum_availability_list,
|
||||
monitored,
|
||||
quality_profile_list,
|
||||
} = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.edit_collection_modal
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let quality_profile = quality_profile_list.current_selection();
|
||||
let quality_profile_id = *self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.quality_profile_map
|
||||
.iter()
|
||||
.filter(|(_, value)| *value == quality_profile)
|
||||
.map(|(key, _)| key)
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
let root_folder_path: String = path.text.clone();
|
||||
let monitored = monitored.unwrap_or_default();
|
||||
let search_on_add = search_on_add.unwrap_or_default();
|
||||
let minimum_availability = *minimum_availability_list.current_selection();
|
||||
self.app.data.radarr_data.edit_collection_modal = None;
|
||||
|
||||
EditCollectionParams {
|
||||
collection_id,
|
||||
monitored: Some(monitored),
|
||||
minimum_availability: Some(minimum_availability),
|
||||
quality_profile_id: Some(quality_profile_id),
|
||||
root_folder_path: Some(root_folder_path),
|
||||
search_on_add: Some(search_on_add),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandler<'a, 'b> {
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
EDIT_COLLECTION_BLOCKS.contains(active_block)
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
EDIT_COLLECTION_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
) -> EditCollectionHandler<'a, 'b> {
|
||||
EditCollectionHandler {
|
||||
key,
|
||||
@@ -37,7 +84,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -65,9 +112,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
|
||||
.unwrap()
|
||||
.quality_profile_list
|
||||
.scroll_up(),
|
||||
ActiveRadarrBlock::EditCollectionPrompt => {
|
||||
self.app.data.radarr_data.selected_block.previous()
|
||||
}
|
||||
ActiveRadarrBlock::EditCollectionPrompt => self.app.data.radarr_data.selected_block.up(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -92,7 +137,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
|
||||
.unwrap()
|
||||
.quality_profile_list
|
||||
.scroll_down(),
|
||||
ActiveRadarrBlock::EditCollectionPrompt => self.app.data.radarr_data.selected_block.next(),
|
||||
ActiveRadarrBlock::EditCollectionPrompt => self.app.data.radarr_data.selected_block.down(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -192,8 +237,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
|
||||
match self.app.data.radarr_data.selected_block.get_active_block() {
|
||||
ActiveRadarrBlock::EditCollectionConfirmPrompt => {
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::EditCollection(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection(
|
||||
self.build_edit_collection_params(),
|
||||
));
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
|
||||
@@ -203,8 +249,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
|
||||
| ActiveRadarrBlock::EditCollectionSelectQualityProfile => {
|
||||
self.app.push_navigation_stack(
|
||||
(
|
||||
*self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
*self.context,
|
||||
self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
self.context,
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
@@ -212,8 +258,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
|
||||
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
|
||||
self.app.push_navigation_stack(
|
||||
(
|
||||
*self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
*self.context,
|
||||
self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
self.context,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
@@ -308,11 +354,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
|
||||
}
|
||||
ActiveRadarrBlock::EditCollectionPrompt => {
|
||||
if self.app.data.radarr_data.selected_block.get_active_block()
|
||||
== &ActiveRadarrBlock::EditCollectionConfirmPrompt
|
||||
&& *key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
== ActiveRadarrBlock::EditCollectionConfirmPrompt
|
||||
&& key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
{
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection(
|
||||
self.build_edit_collection_params(),
|
||||
));
|
||||
self.app.should_refresh = true;
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bimap::BiMap;
|
||||
use pretty_assertions::assert_str_eq;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
@@ -7,8 +8,9 @@ mod tests {
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::radarr_handlers::collections::edit_collection_handler::EditCollectionHandler;
|
||||
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::collection;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::models::radarr_models::MinimumAvailability;
|
||||
use crate::models::radarr_models::{Collection, EditCollectionParams, MinimumAvailability};
|
||||
use crate::models::servarr_data::radarr::modals::EditCollectionModal;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
ActiveRadarrBlock, EDIT_COLLECTION_BLOCKS,
|
||||
@@ -44,10 +46,10 @@ mod tests {
|
||||
if key == Key::Up {
|
||||
for i in (0..minimum_availability_vec.len()).rev() {
|
||||
EditCollectionHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -66,10 +68,10 @@ mod tests {
|
||||
} else {
|
||||
for i in 0..minimum_availability_vec.len() {
|
||||
EditCollectionHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -104,10 +106,10 @@ mod tests {
|
||||
.set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionSelectQualityProfile,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -124,10 +126,10 @@ mod tests {
|
||||
);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionSelectQualityProfile,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -149,26 +151,21 @@ mod tests {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.next();
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.down();
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
)
|
||||
EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
|
||||
.handle();
|
||||
|
||||
if key == Key::Up {
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::EditCollectionToggleMonitored
|
||||
ActiveRadarrBlock::EditCollectionToggleMonitored
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::EditCollectionSelectQualityProfile
|
||||
ActiveRadarrBlock::EditCollectionSelectQualityProfile
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -181,20 +178,15 @@ mod tests {
|
||||
app.is_loading = true;
|
||||
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.next();
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.down();
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
)
|
||||
EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
|
||||
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -224,10 +216,10 @@ mod tests {
|
||||
.set_items(minimum_availability_vec.clone());
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -244,10 +236,10 @@ mod tests {
|
||||
);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -282,10 +274,10 @@ mod tests {
|
||||
]);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionSelectQualityProfile,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -302,10 +294,10 @@ mod tests {
|
||||
);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionSelectQualityProfile,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -331,10 +323,10 @@ mod tests {
|
||||
});
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -352,10 +344,10 @@ mod tests {
|
||||
);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -386,22 +378,12 @@ mod tests {
|
||||
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
|
||||
let mut app = App::default();
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
)
|
||||
EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
|
||||
.handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
)
|
||||
EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
|
||||
.handle();
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
@@ -416,10 +398,10 @@ mod tests {
|
||||
});
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.left.key,
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -437,10 +419,10 @@ mod tests {
|
||||
);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.right.key,
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -484,10 +466,10 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into());
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -503,7 +485,7 @@ mod tests {
|
||||
.is_empty());
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::EditCollectionPrompt.into()
|
||||
ActiveRadarrBlock::EditCollectionPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -514,24 +496,24 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
|
||||
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Collections.into()
|
||||
ActiveRadarrBlock::Collections.into()
|
||||
);
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
}
|
||||
@@ -539,35 +521,63 @@ mod tests {
|
||||
#[test]
|
||||
fn test_edit_collection_confirm_prompt_prompt_confirmation_submit() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
|
||||
let mut edit_collection_modal = EditCollectionModal {
|
||||
path: "/nfs/Test Path".into(),
|
||||
monitored: Some(false),
|
||||
search_on_add: Some(false),
|
||||
..EditCollectionModal::default()
|
||||
};
|
||||
edit_collection_modal
|
||||
.quality_profile_list
|
||||
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
|
||||
edit_collection_modal
|
||||
.minimum_availability_list
|
||||
.set_items(Vec::from_iter(MinimumAvailability::iter()));
|
||||
app.data.radarr_data.edit_collection_modal = Some(edit_collection_modal);
|
||||
app.data.radarr_data.collections.set_items(vec![Collection {
|
||||
monitored: false,
|
||||
search_on_add: false,
|
||||
..collection()
|
||||
}]);
|
||||
app.data.radarr_data.quality_profile_map =
|
||||
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
|
||||
let expected_edit_collection_params = EditCollectionParams {
|
||||
collection_id: 123,
|
||||
monitored: Some(false),
|
||||
minimum_availability: Some(MinimumAvailability::Announced),
|
||||
quality_profile_id: Some(1111),
|
||||
root_folder_path: Some("/nfs/Test Path".to_owned()),
|
||||
search_on_add: Some(false),
|
||||
};
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
|
||||
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Collections.into()
|
||||
ActiveRadarrBlock::Collections.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(RadarrEvent::EditCollection(None))
|
||||
Some(RadarrEvent::EditCollection(expected_edit_collection_params))
|
||||
);
|
||||
assert!(app.should_refresh);
|
||||
assert!(app.data.radarr_data.edit_collection_modal.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -579,24 +589,24 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
|
||||
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::EditCollectionPrompt.into()
|
||||
ActiveRadarrBlock::EditCollectionPrompt.into()
|
||||
);
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
assert!(!app.should_refresh);
|
||||
@@ -611,18 +621,18 @@ mod tests {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app.push_navigation_stack(current_route);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&Some(ActiveRadarrBlock::Collections),
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
Some(ActiveRadarrBlock::Collections),
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ¤t_route);
|
||||
assert_eq!(app.get_current_route(), current_route);
|
||||
assert_eq!(
|
||||
app
|
||||
.data
|
||||
@@ -635,14 +645,14 @@ mod tests {
|
||||
);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&Some(ActiveRadarrBlock::Collections),
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
Some(ActiveRadarrBlock::Collections),
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ¤t_route);
|
||||
assert_eq!(app.get_current_route(), current_route);
|
||||
assert_eq!(
|
||||
app
|
||||
.data
|
||||
@@ -664,23 +674,23 @@ mod tests {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 2);
|
||||
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 2);
|
||||
app.push_navigation_stack(current_route);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&Some(ActiveRadarrBlock::Collections),
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
Some(ActiveRadarrBlock::Collections),
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ¤t_route);
|
||||
assert_eq!(app.get_current_route(), current_route);
|
||||
assert_eq!(
|
||||
app
|
||||
.data
|
||||
@@ -693,14 +703,14 @@ mod tests {
|
||||
);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&Some(ActiveRadarrBlock::Collections),
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
Some(ActiveRadarrBlock::Collections),
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ¤t_route);
|
||||
assert_eq!(app.get_current_route(), current_route);
|
||||
assert_eq!(
|
||||
app
|
||||
.data
|
||||
@@ -731,20 +741,20 @@ mod tests {
|
||||
.into(),
|
||||
);
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(index);
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(0, index);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&Some(ActiveRadarrBlock::Collections),
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
Some(ActiveRadarrBlock::Collections),
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&(selected_block, Some(ActiveRadarrBlock::Collections)).into()
|
||||
(selected_block, Some(ActiveRadarrBlock::Collections)).into()
|
||||
);
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
|
||||
@@ -768,16 +778,16 @@ mod tests {
|
||||
app.push_navigation_stack(active_radarr_block.into());
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&active_radarr_block,
|
||||
&Some(ActiveRadarrBlock::Collections),
|
||||
active_radarr_block,
|
||||
Some(ActiveRadarrBlock::Collections),
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::EditCollectionPrompt.into()
|
||||
ActiveRadarrBlock::EditCollectionPrompt.into()
|
||||
);
|
||||
|
||||
if active_radarr_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput {
|
||||
@@ -806,17 +816,17 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into());
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&ESC_KEY,
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(!app.should_ignore_quit_key);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::EditCollectionPrompt.into()
|
||||
ActiveRadarrBlock::EditCollectionPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -828,16 +838,16 @@ mod tests {
|
||||
app.data.radarr_data = create_test_radarr_data();
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&ESC_KEY,
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Collections.into()
|
||||
ActiveRadarrBlock::Collections.into()
|
||||
);
|
||||
let radarr_data = &app.data.radarr_data;
|
||||
|
||||
@@ -860,11 +870,11 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
|
||||
app.push_navigation_stack(active_radarr_block.into());
|
||||
|
||||
EditCollectionHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle();
|
||||
EditCollectionHandler::with(ESC_KEY, &mut app, active_radarr_block, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Collections.into()
|
||||
ActiveRadarrBlock::Collections.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -890,10 +900,10 @@ mod tests {
|
||||
});
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.backspace.key,
|
||||
DEFAULT_KEYBINDINGS.backspace.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -916,10 +926,10 @@ mod tests {
|
||||
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&Key::Char('h'),
|
||||
Key::Char('h'),
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -939,34 +949,62 @@ mod tests {
|
||||
#[test]
|
||||
fn test_edit_collection_confirm_prompt_prompt_confirmation_confirm() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
|
||||
let mut edit_collection_modal = EditCollectionModal {
|
||||
path: "/nfs/Test Path".into(),
|
||||
monitored: Some(false),
|
||||
search_on_add: Some(false),
|
||||
..EditCollectionModal::default()
|
||||
};
|
||||
edit_collection_modal
|
||||
.quality_profile_list
|
||||
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
|
||||
edit_collection_modal
|
||||
.minimum_availability_list
|
||||
.set_items(Vec::from_iter(MinimumAvailability::iter()));
|
||||
app.data.radarr_data.edit_collection_modal = Some(edit_collection_modal);
|
||||
app.data.radarr_data.collections.set_items(vec![Collection {
|
||||
monitored: false,
|
||||
search_on_add: false,
|
||||
..collection()
|
||||
}]);
|
||||
app.data.radarr_data.quality_profile_map =
|
||||
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
|
||||
let expected_edit_collection_params = EditCollectionParams {
|
||||
collection_id: 123,
|
||||
monitored: Some(false),
|
||||
minimum_availability: Some(MinimumAvailability::Announced),
|
||||
quality_profile_id: Some(1111),
|
||||
root_folder_path: Some("/nfs/Test Path".to_owned()),
|
||||
search_on_add: Some(false),
|
||||
};
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
|
||||
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
|
||||
|
||||
EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.confirm.key,
|
||||
DEFAULT_KEYBINDINGS.confirm.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Collections.into()
|
||||
ActiveRadarrBlock::Collections.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(RadarrEvent::EditCollection(None))
|
||||
Some(RadarrEvent::EditCollection(expected_edit_collection_params))
|
||||
);
|
||||
assert!(app.should_refresh);
|
||||
assert!(app.data.radarr_data.edit_collection_modal.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -974,23 +1012,67 @@ mod tests {
|
||||
fn test_edit_collection_handler_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if EDIT_COLLECTION_BLOCKS.contains(&active_radarr_block) {
|
||||
assert!(EditCollectionHandler::accepts(&active_radarr_block));
|
||||
assert!(EditCollectionHandler::accepts(active_radarr_block));
|
||||
} else {
|
||||
assert!(!EditCollectionHandler::accepts(&active_radarr_block));
|
||||
assert!(!EditCollectionHandler::accepts(active_radarr_block));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_edit_collection_params() {
|
||||
let mut app = App::default();
|
||||
let mut edit_collection_modal = EditCollectionModal {
|
||||
path: "/nfs/Test Path".into(),
|
||||
monitored: Some(false),
|
||||
search_on_add: Some(false),
|
||||
..EditCollectionModal::default()
|
||||
};
|
||||
edit_collection_modal
|
||||
.quality_profile_list
|
||||
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
|
||||
edit_collection_modal
|
||||
.minimum_availability_list
|
||||
.set_items(Vec::from_iter(MinimumAvailability::iter()));
|
||||
app.data.radarr_data.edit_collection_modal = Some(edit_collection_modal);
|
||||
app.data.radarr_data.collections.set_items(vec![Collection {
|
||||
monitored: false,
|
||||
search_on_add: false,
|
||||
..collection()
|
||||
}]);
|
||||
app.data.radarr_data.quality_profile_map =
|
||||
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
|
||||
let expected_edit_collection_params = EditCollectionParams {
|
||||
collection_id: 123,
|
||||
monitored: Some(false),
|
||||
minimum_availability: Some(MinimumAvailability::Announced),
|
||||
quality_profile_id: Some(1111),
|
||||
root_folder_path: Some("/nfs/Test Path".to_owned()),
|
||||
search_on_add: Some(false),
|
||||
};
|
||||
|
||||
let edit_collection_params = EditCollectionHandler::with(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
None,
|
||||
)
|
||||
.build_edit_collection_params();
|
||||
|
||||
assert_eq!(edit_collection_params, expected_edit_collection_params);
|
||||
assert!(app.data.radarr_data.edit_collection_modal.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edit_collection_handler_is_not_ready_when_loading() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -1002,10 +1084,10 @@ mod tests {
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -1018,10 +1100,10 @@ mod tests {
|
||||
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
|
||||
|
||||
let handler = EditCollectionHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::EditCollectionPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handle_table_events;
|
||||
use crate::handlers::radarr_handlers::collections::collection_details_handler::CollectionDetailsHandler;
|
||||
use crate::handlers::radarr_handlers::collections::edit_collection_handler::EditCollectionHandler;
|
||||
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
|
||||
use crate::handlers::table_handler::TableHandlingConfig;
|
||||
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
|
||||
use crate::models::radarr_models::Collection;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
ActiveRadarrBlock, COLLECTIONS_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS,
|
||||
};
|
||||
use crate::models::stateful_table::SortOption;
|
||||
use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable};
|
||||
use crate::models::BlockSelectionState;
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
|
||||
|
||||
mod collection_details_handler;
|
||||
mod edit_collection_handler;
|
||||
@@ -22,17 +23,44 @@ mod edit_collection_handler;
|
||||
mod collections_handler_tests;
|
||||
|
||||
pub(super) struct CollectionsHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> CollectionsHandler<'a, 'b> {
|
||||
handle_table_events!(
|
||||
self,
|
||||
collections,
|
||||
self.app.data.radarr_data.collections,
|
||||
Collection
|
||||
);
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'a, 'b> {
|
||||
fn handle(&mut self) {
|
||||
let collections_table_handling_config =
|
||||
TableHandlingConfig::new(ActiveRadarrBlock::Collections.into())
|
||||
.sorting_block(ActiveRadarrBlock::CollectionsSortPrompt.into())
|
||||
.sort_by_fn(|a: &Collection, b: &Collection| a.id.cmp(&b.id))
|
||||
.sort_options(collections_sorting_options())
|
||||
.searching_block(ActiveRadarrBlock::SearchCollection.into())
|
||||
.search_error_block(ActiveRadarrBlock::SearchCollectionError.into())
|
||||
.search_field_fn(|collection| &collection.title.text)
|
||||
.filtering_block(ActiveRadarrBlock::FilterCollections.into())
|
||||
.filter_error_block(ActiveRadarrBlock::FilterCollectionsError.into())
|
||||
.filter_field_fn(|collection| &collection.title.text);
|
||||
|
||||
if !self.handle_collections_table_events(collections_table_handling_config) {
|
||||
match self.active_radarr_block {
|
||||
_ if CollectionDetailsHandler::accepts(self.active_radarr_block) => {
|
||||
CollectionDetailsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
|
||||
CollectionDetailsHandler::with(
|
||||
self.key,
|
||||
self.app,
|
||||
self.active_radarr_block,
|
||||
self.context,
|
||||
)
|
||||
.handle();
|
||||
}
|
||||
_ if EditCollectionHandler::accepts(self.active_radarr_block) => {
|
||||
@@ -42,18 +70,19 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
|
||||
_ => self.handle_key_event(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
CollectionDetailsHandler::accepts(active_block)
|
||||
|| EditCollectionHandler::accepts(active_block)
|
||||
|| COLLECTIONS_BLOCKS.contains(active_block)
|
||||
|| COLLECTIONS_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
) -> CollectionsHandler<'a, 'b> {
|
||||
CollectionsHandler {
|
||||
key,
|
||||
@@ -63,7 +92,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -71,105 +100,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
|
||||
!self.app.is_loading && !self.app.data.radarr_data.collections.is_empty()
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Collections => self.app.data.radarr_data.collections.scroll_up(),
|
||||
ActiveRadarrBlock::CollectionsSortPrompt => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.sort
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_up(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn handle_scroll_up(&mut self) {}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Collections => self.app.data.radarr_data.collections.scroll_down(),
|
||||
ActiveRadarrBlock::CollectionsSortPrompt => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.sort
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_down(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn handle_scroll_down(&mut self) {}
|
||||
|
||||
fn handle_home(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Collections => self.app.data.radarr_data.collections.scroll_to_top(),
|
||||
ActiveRadarrBlock::SearchCollection => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.search
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_home(),
|
||||
ActiveRadarrBlock::FilterCollections => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.filter
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_home(),
|
||||
ActiveRadarrBlock::CollectionsSortPrompt => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.sort
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_to_top(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn handle_home(&mut self) {}
|
||||
|
||||
fn handle_end(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Collections => self.app.data.radarr_data.collections.scroll_to_bottom(),
|
||||
ActiveRadarrBlock::SearchCollection => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.search
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.reset_offset(),
|
||||
ActiveRadarrBlock::FilterCollections => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.filter
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.reset_offset(),
|
||||
ActiveRadarrBlock::CollectionsSortPrompt => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.sort
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_to_bottom(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
fn handle_end(&mut self) {}
|
||||
|
||||
fn handle_delete(&mut self) {}
|
||||
|
||||
@@ -177,34 +114,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Collections => handle_change_tab_left_right_keys(self.app, self.key),
|
||||
ActiveRadarrBlock::UpdateAllCollectionsPrompt => handle_prompt_toggle(self.app, self.key),
|
||||
ActiveRadarrBlock::SearchCollection => {
|
||||
handle_text_box_left_right_keys!(
|
||||
self,
|
||||
self.key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.search
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
ActiveRadarrBlock::FilterCollections => {
|
||||
handle_text_box_left_right_keys!(
|
||||
self,
|
||||
self.key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.filter
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -214,44 +123,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
|
||||
ActiveRadarrBlock::Collections => self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()),
|
||||
ActiveRadarrBlock::SearchCollection => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.should_ignore_quit_key = false;
|
||||
|
||||
if self.app.data.radarr_data.collections.search.is_some() {
|
||||
let has_match = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.apply_search(|collection| &collection.title.text);
|
||||
|
||||
if !has_match {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::SearchCollectionError.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
ActiveRadarrBlock::FilterCollections => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.should_ignore_quit_key = false;
|
||||
|
||||
if self.app.data.radarr_data.collections.filter.is_some() {
|
||||
let has_matches = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.apply_filter(|collection| &collection.title.text);
|
||||
|
||||
if !has_matches {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::FilterCollectionsError.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
ActiveRadarrBlock::UpdateAllCollectionsPrompt => {
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateCollections);
|
||||
@@ -259,44 +130,17 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
ActiveRadarrBlock::CollectionsSortPrompt => {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.items
|
||||
.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
self.app.data.radarr_data.collections.apply_sorting();
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_esc(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::FilterCollections | ActiveRadarrBlock::FilterCollectionsError => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.radarr_data.collections.reset_filter();
|
||||
self.app.should_ignore_quit_key = false;
|
||||
}
|
||||
ActiveRadarrBlock::SearchCollection | ActiveRadarrBlock::SearchCollectionError => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.radarr_data.collections.reset_search();
|
||||
self.app.should_ignore_quit_key = false;
|
||||
}
|
||||
ActiveRadarrBlock::UpdateAllCollectionsPrompt => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.radarr_data.prompt_confirm = false;
|
||||
}
|
||||
ActiveRadarrBlock::CollectionsSortPrompt => {
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
_ => {
|
||||
self.app.data.radarr_data.collections.reset_search();
|
||||
self.app.data.radarr_data.collections.reset_filter();
|
||||
handle_clear_errors(self.app);
|
||||
}
|
||||
}
|
||||
@@ -306,87 +150,27 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
|
||||
let key = self.key;
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Collections => match self.key {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.search.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.edit.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::SearchCollection.into());
|
||||
self.app.data.radarr_data.collections.search =
|
||||
Some(HorizontallyScrollableText::default());
|
||||
self.app.should_ignore_quit_key = true;
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.filter.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::FilterCollections.into());
|
||||
self.app.data.radarr_data.collections.reset_filter();
|
||||
self.app.data.radarr_data.collections.filter =
|
||||
Some(HorizontallyScrollableText::default());
|
||||
self.app.should_ignore_quit_key = true;
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.edit.key => {
|
||||
self.app.push_navigation_stack(
|
||||
(
|
||||
ActiveRadarrBlock::EditCollectionPrompt,
|
||||
Some(ActiveRadarrBlock::Collections),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
|
||||
self.app.data.radarr_data.edit_collection_modal =
|
||||
Some((&self.app.data.radarr_data).into());
|
||||
self.app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.update.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into());
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.sorting(collections_sorting_options());
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::CollectionsSortPrompt.into());
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveRadarrBlock::SearchCollection => {
|
||||
handle_text_box_keys!(
|
||||
self,
|
||||
key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.search
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
ActiveRadarrBlock::FilterCollections => {
|
||||
handle_text_box_keys!(
|
||||
self,
|
||||
key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.filter
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
ActiveRadarrBlock::UpdateAllCollectionsPrompt => {
|
||||
if *key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
if key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateCollections);
|
||||
|
||||
|
||||
@@ -1,119 +1,17 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::assert_str_eq;
|
||||
use pretty_assertions::assert_eq;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::radarr_handlers::downloads::DownloadsHandler;
|
||||
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::download_record;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::models::radarr_models::DownloadRecord;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLOADS_BLOCKS};
|
||||
|
||||
mod test_handle_scroll_up_and_down {
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::models::radarr_models::DownloadRecord;
|
||||
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
|
||||
|
||||
use super::*;
|
||||
|
||||
test_iterable_scroll!(
|
||||
test_downloads_scroll,
|
||||
DownloadsHandler,
|
||||
downloads,
|
||||
DownloadRecord,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
title
|
||||
);
|
||||
|
||||
#[rstest]
|
||||
fn test_downloads_scroll_no_op_when_not_ready(
|
||||
#[values(
|
||||
DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key
|
||||
)]
|
||||
key: Key,
|
||||
) {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.downloads
|
||||
.set_items(simple_stateful_iterable_vec!(DownloadRecord));
|
||||
|
||||
DownloadsHandler::with(&key, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.downloads.current_selection().title,
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
DownloadsHandler::with(&key, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.downloads.current_selection().title,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_home_end {
|
||||
use crate::models::radarr_models::DownloadRecord;
|
||||
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
|
||||
|
||||
use super::*;
|
||||
|
||||
test_iterable_home_and_end!(
|
||||
test_downloads_home_end,
|
||||
DownloadsHandler,
|
||||
downloads,
|
||||
DownloadRecord,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
title
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn test_downloads_home_end_no_op_when_not_ready() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.downloads
|
||||
.set_items(extended_stateful_iterable_vec!(DownloadRecord));
|
||||
|
||||
DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.downloads.current_selection().title,
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.downloads.current_selection().title,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_delete {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@@ -130,11 +28,11 @@ mod tests {
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
|
||||
DownloadsHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Downloads, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::DeleteDownloadPrompt.into()
|
||||
ActiveRadarrBlock::DeleteDownloadPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -149,12 +47,9 @@ mod tests {
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
|
||||
DownloadsHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Downloads, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Downloads.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,20 +66,20 @@ mod tests {
|
||||
app.data.radarr_data.main_tabs.set_index(2);
|
||||
|
||||
DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.left.key,
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
&ActiveRadarrBlock::Collections.into()
|
||||
ActiveRadarrBlock::Collections.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Collections.into()
|
||||
ActiveRadarrBlock::Collections.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -195,21 +90,18 @@ mod tests {
|
||||
app.data.radarr_data.main_tabs.set_index(2);
|
||||
|
||||
DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.right.key,
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Blocklist.into()
|
||||
ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@@ -223,11 +115,11 @@ mod tests {
|
||||
) {
|
||||
let mut app = App::default();
|
||||
|
||||
DownloadsHandler::with(&key, &mut app, &active_radarr_block, &None).handle();
|
||||
DownloadsHandler::with(key, &mut app, active_radarr_block, None).handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
|
||||
DownloadsHandler::with(&key, &mut app, &active_radarr_block, &None).handle();
|
||||
DownloadsHandler::with(key, &mut app, active_radarr_block, None).handle();
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
@@ -247,7 +139,7 @@ mod tests {
|
||||
#[case(
|
||||
ActiveRadarrBlock::Downloads,
|
||||
ActiveRadarrBlock::DeleteDownloadPrompt,
|
||||
RadarrEvent::DeleteDownload(None)
|
||||
RadarrEvent::DeleteDownload(1)
|
||||
)]
|
||||
#[case(
|
||||
ActiveRadarrBlock::Downloads,
|
||||
@@ -264,19 +156,19 @@ mod tests {
|
||||
.data
|
||||
.radarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
.set_items(vec![download_record()]);
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
app.push_navigation_stack(base_route.into());
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
|
||||
DownloadsHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle();
|
||||
DownloadsHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(expected_action)
|
||||
);
|
||||
assert_eq!(app.get_current_route(), &base_route.into());
|
||||
assert_eq!(app.get_current_route(), base_route.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@@ -295,11 +187,11 @@ mod tests {
|
||||
app.push_navigation_stack(base_route.into());
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
|
||||
DownloadsHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle();
|
||||
DownloadsHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
assert_eq!(app.get_current_route(), &base_route.into());
|
||||
assert_eq!(app.get_current_route(), base_route.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,9 +215,9 @@ mod tests {
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
|
||||
DownloadsHandler::with(&ESC_KEY, &mut app, &prompt_block, &None).handle();
|
||||
DownloadsHandler::with(ESC_KEY, &mut app, prompt_block, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &base_block.into());
|
||||
assert_eq!(app.get_current_route(), base_block.into());
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
@@ -337,12 +229,9 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
|
||||
|
||||
DownloadsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
|
||||
DownloadsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::Downloads, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Downloads.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
|
||||
assert!(app.error.text.is_empty());
|
||||
}
|
||||
}
|
||||
@@ -365,16 +254,16 @@ mod tests {
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.update.key,
|
||||
DEFAULT_KEYBINDINGS.update.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::UpdateDownloadsPrompt.into()
|
||||
ActiveRadarrBlock::UpdateDownloadsPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -390,17 +279,14 @@ mod tests {
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.update.key,
|
||||
DEFAULT_KEYBINDINGS.update.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Downloads.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -414,17 +300,14 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
|
||||
|
||||
DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.refresh.key,
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Downloads.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
|
||||
assert!(app.should_refresh);
|
||||
}
|
||||
|
||||
@@ -440,17 +323,14 @@ mod tests {
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.refresh.key,
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::Downloads.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
|
||||
@@ -458,7 +338,7 @@ mod tests {
|
||||
#[case(
|
||||
ActiveRadarrBlock::Downloads,
|
||||
ActiveRadarrBlock::DeleteDownloadPrompt,
|
||||
RadarrEvent::DeleteDownload(None)
|
||||
RadarrEvent::DeleteDownload(1)
|
||||
)]
|
||||
#[case(
|
||||
ActiveRadarrBlock::Downloads,
|
||||
@@ -475,15 +355,15 @@ mod tests {
|
||||
.data
|
||||
.radarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
.set_items(vec![download_record()]);
|
||||
app.push_navigation_stack(base_route.into());
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
|
||||
DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.confirm.key,
|
||||
DEFAULT_KEYBINDINGS.confirm.key,
|
||||
&mut app,
|
||||
&prompt_block,
|
||||
&None,
|
||||
prompt_block,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -492,7 +372,7 @@ mod tests {
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(expected_action)
|
||||
);
|
||||
assert_eq!(app.get_current_route(), &base_route.into());
|
||||
assert_eq!(app.get_current_route(), base_route.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,23 +380,43 @@ mod tests {
|
||||
fn test_downloads_handler_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if DOWNLOADS_BLOCKS.contains(&active_radarr_block) {
|
||||
assert!(DownloadsHandler::accepts(&active_radarr_block));
|
||||
assert!(DownloadsHandler::accepts(active_radarr_block));
|
||||
} else {
|
||||
assert!(!DownloadsHandler::accepts(&active_radarr_block));
|
||||
assert!(!DownloadsHandler::accepts(active_radarr_block));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_download_id() {
|
||||
let mut app = App::default();
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.downloads
|
||||
.set_items(vec![download_record()]);
|
||||
|
||||
let download_id = DownloadsHandler::with(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.extract_download_id();
|
||||
|
||||
assert_eq!(download_id, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downloads_handler_not_ready_when_loading() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -528,10 +428,10 @@ mod tests {
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -548,10 +448,10 @@ mod tests {
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
let handler = DownloadsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Downloads,
|
||||
&None,
|
||||
ActiveRadarrBlock::Downloads,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handle_table_events;
|
||||
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
|
||||
use crate::handlers::table_handler::TableHandlingConfig;
|
||||
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
|
||||
use crate::models::radarr_models::DownloadRecord;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLOADS_BLOCKS};
|
||||
use crate::models::Scrollable;
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -12,22 +14,44 @@ use crate::network::radarr_network::RadarrEvent;
|
||||
mod downloads_handler_tests;
|
||||
|
||||
pub(super) struct DownloadsHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> DownloadsHandler<'a, 'b> {
|
||||
handle_table_events!(
|
||||
self,
|
||||
downloads,
|
||||
self.app.data.radarr_data.downloads,
|
||||
DownloadRecord
|
||||
);
|
||||
|
||||
fn extract_download_id(&self) -> i64 {
|
||||
self.app.data.radarr_data.downloads.current_selection().id
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a, 'b> {
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
DOWNLOADS_BLOCKS.contains(active_block)
|
||||
fn handle(&mut self) {
|
||||
let downloads_table_handling_config =
|
||||
TableHandlingConfig::new(ActiveRadarrBlock::Downloads.into());
|
||||
|
||||
if !self.handle_downloads_table_events(downloads_table_handling_config) {
|
||||
self.handle_key_event();
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
DOWNLOADS_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
) -> DownloadsHandler<'a, 'b> {
|
||||
DownloadsHandler {
|
||||
key,
|
||||
@@ -37,7 +61,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -45,32 +69,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
|
||||
!self.app.is_loading && !self.app.data.radarr_data.downloads.is_empty()
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
|
||||
self.app.data.radarr_data.downloads.scroll_up()
|
||||
}
|
||||
}
|
||||
fn handle_scroll_up(&mut self) {}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
|
||||
self.app.data.radarr_data.downloads.scroll_down()
|
||||
}
|
||||
}
|
||||
fn handle_scroll_down(&mut self) {}
|
||||
|
||||
fn handle_home(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
|
||||
self.app.data.radarr_data.downloads.scroll_to_top()
|
||||
}
|
||||
}
|
||||
fn handle_home(&mut self) {}
|
||||
|
||||
fn handle_end(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
|
||||
self.app.data.radarr_data.downloads.scroll_to_bottom()
|
||||
}
|
||||
}
|
||||
fn handle_end(&mut self) {}
|
||||
|
||||
fn handle_delete(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::Downloads {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::DeleteDownloadPrompt.into())
|
||||
@@ -91,7 +99,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::DeleteDownloadPrompt => {
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::DeleteDownload(self.extract_download_id()));
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
@@ -121,26 +130,27 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
|
||||
let key = self.key;
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Downloads => match self.key {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.update.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::UpdateDownloadsPrompt.into());
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveRadarrBlock::DeleteDownloadPrompt => {
|
||||
if *key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
if key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::DeleteDownload(self.extract_download_id()));
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
}
|
||||
ActiveRadarrBlock::UpdateDownloadsPrompt => {
|
||||
if *key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
if key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateDownloads);
|
||||
|
||||
|
||||
@@ -2,31 +2,88 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
|
||||
use crate::models::servarr_data::modals::EditIndexerModal;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
|
||||
use crate::models::servarr_models::EditIndexerParams;
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
|
||||
use crate::{handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "edit_indexer_handler_tests.rs"]
|
||||
mod edit_indexer_handler_tests;
|
||||
|
||||
pub(super) struct EditIndexerHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> EditIndexerHandler<'a, 'b> {
|
||||
fn build_edit_indexer_params(&mut self) -> EditIndexerParams {
|
||||
let indexer_id = self.app.data.radarr_data.indexers.current_selection().id;
|
||||
let tags = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.edit_indexer_modal
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.tags
|
||||
.text
|
||||
.clone();
|
||||
|
||||
let params = {
|
||||
let EditIndexerModal {
|
||||
name,
|
||||
enable_rss,
|
||||
enable_automatic_search,
|
||||
enable_interactive_search,
|
||||
url,
|
||||
api_key,
|
||||
seed_ratio,
|
||||
priority,
|
||||
..
|
||||
} = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.edit_indexer_modal
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
EditIndexerParams {
|
||||
indexer_id,
|
||||
name: Some(name.text.clone()),
|
||||
enable_rss: Some(enable_rss.unwrap_or_default()),
|
||||
enable_automatic_search: Some(enable_automatic_search.unwrap_or_default()),
|
||||
enable_interactive_search: Some(enable_interactive_search.unwrap_or_default()),
|
||||
url: Some(url.text.clone()),
|
||||
api_key: Some(api_key.text.clone()),
|
||||
seed_ratio: Some(seed_ratio.text.clone()),
|
||||
tags: None,
|
||||
tag_input_string: Some(tags),
|
||||
priority: Some(*priority),
|
||||
clear_tags: false,
|
||||
}
|
||||
};
|
||||
|
||||
self.app.data.radarr_data.edit_indexer_modal = None;
|
||||
|
||||
params
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'a, 'b> {
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
EDIT_INDEXER_BLOCKS.contains(active_block)
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
EDIT_INDEXER_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
) -> EditIndexerHandler<'a, 'b> {
|
||||
EditIndexerHandler {
|
||||
key,
|
||||
@@ -36,7 +93,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -45,14 +102,42 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::EditIndexerPrompt {
|
||||
self.app.data.radarr_data.selected_block.previous();
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::EditIndexerPrompt => {
|
||||
self.app.data.radarr_data.selected_block.up();
|
||||
}
|
||||
ActiveRadarrBlock::EditIndexerPriorityInput => {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.edit_indexer_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.priority += 1;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::EditIndexerPrompt {
|
||||
self.app.data.radarr_data.selected_block.next();
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::EditIndexerPrompt => {
|
||||
self.app.data.radarr_data.selected_block.down();
|
||||
}
|
||||
ActiveRadarrBlock::EditIndexerPriorityInput => {
|
||||
let edit_indexer_modal = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.edit_indexer_modal
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
if edit_indexer_modal.priority > 0 {
|
||||
edit_indexer_modal.priority -= 1;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,15 +268,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
|
||||
fn handle_left_right_action(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::EditIndexerPrompt => {
|
||||
if self.app.data.radarr_data.selected_block.get_active_block()
|
||||
== &ActiveRadarrBlock::EditIndexerConfirmPrompt
|
||||
{
|
||||
handle_prompt_toggle(self.app, self.key);
|
||||
} else {
|
||||
let len = self.app.data.radarr_data.selected_block.blocks.len();
|
||||
let idx = self.app.data.radarr_data.selected_block.index;
|
||||
self.app.data.radarr_data.selected_block.index = (idx + 5) % len;
|
||||
}
|
||||
handle_prompt_left_right_keys!(
|
||||
self,
|
||||
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||
radarr_data
|
||||
);
|
||||
}
|
||||
ActiveRadarrBlock::EditIndexerNameInput => {
|
||||
handle_text_box_left_right_keys!(
|
||||
@@ -270,15 +351,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
|
||||
fn handle_submit(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::EditIndexerPrompt => {
|
||||
let selected_block = *self.app.data.radarr_data.selected_block.get_active_block();
|
||||
let selected_block = self.app.data.radarr_data.selected_block.get_active_block();
|
||||
match selected_block {
|
||||
ActiveRadarrBlock::EditIndexerConfirmPrompt => {
|
||||
let radarr_data = &mut self.app.data.radarr_data;
|
||||
if radarr_data.prompt_confirm {
|
||||
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditIndexer(None));
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::EditIndexer(self.build_edit_indexer_params()));
|
||||
self.app.should_refresh = true;
|
||||
} else {
|
||||
radarr_data.edit_indexer_modal = None;
|
||||
self.app.data.radarr_data.edit_indexer_modal = None;
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
@@ -291,6 +372,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
|
||||
self.app.push_navigation_stack(selected_block.into());
|
||||
self.app.should_ignore_quit_key = true;
|
||||
}
|
||||
ActiveRadarrBlock::EditIndexerPriorityInput => self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::EditIndexerPriorityInput.into()),
|
||||
ActiveRadarrBlock::EditIndexerToggleEnableRss => {
|
||||
let indexer = self
|
||||
.app
|
||||
@@ -334,6 +418,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.should_ignore_quit_key = false;
|
||||
}
|
||||
ActiveRadarrBlock::EditIndexerPriorityInput => self.app.pop_navigation_stack(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -349,6 +434,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
|
||||
| ActiveRadarrBlock::EditIndexerUrlInput
|
||||
| ActiveRadarrBlock::EditIndexerApiKeyInput
|
||||
| ActiveRadarrBlock::EditIndexerSeedRatioInput
|
||||
| ActiveRadarrBlock::EditIndexerPriorityInput
|
||||
| ActiveRadarrBlock::EditIndexerTagsInput => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.should_ignore_quit_key = false;
|
||||
@@ -431,11 +517,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
|
||||
}
|
||||
ActiveRadarrBlock::EditIndexerPrompt => {
|
||||
if self.app.data.radarr_data.selected_block.get_active_block()
|
||||
== &ActiveRadarrBlock::EditIndexerConfirmPrompt
|
||||
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
== ActiveRadarrBlock::EditIndexerConfirmPrompt
|
||||
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
{
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditIndexer(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::EditIndexer(self.build_edit_indexer_params()));
|
||||
self.app.should_refresh = true;
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
|
||||
@@ -2,33 +2,42 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
|
||||
use crate::models::radarr_models::IndexerSettings;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS,
|
||||
};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
|
||||
use crate::{handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "edit_indexer_settings_handler_tests.rs"]
|
||||
mod edit_indexer_settings_handler_tests;
|
||||
|
||||
pub(super) struct IndexerSettingsHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> IndexerSettingsHandler<'a, 'b> {
|
||||
fn build_edit_indexer_settings_body(&mut self) -> IndexerSettings {
|
||||
let indexer_settings = self.app.data.radarr_data.indexer_settings.clone().unwrap();
|
||||
self.app.data.radarr_data.indexer_settings = None;
|
||||
indexer_settings
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandler<'a, 'b> {
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
INDEXER_SETTINGS_BLOCKS.contains(active_block)
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
INDEXER_SETTINGS_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
) -> IndexerSettingsHandler<'a, 'b> {
|
||||
IndexerSettingsHandler {
|
||||
key,
|
||||
@@ -38,7 +47,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -50,7 +59,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
||||
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||
self.app.data.radarr_data.selected_block.previous();
|
||||
self.app.data.radarr_data.selected_block.up();
|
||||
}
|
||||
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
|
||||
indexer_settings.minimum_age += 1;
|
||||
@@ -75,7 +84,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
||||
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||
self.app.data.radarr_data.selected_block.next()
|
||||
self.app.data.radarr_data.selected_block.down()
|
||||
}
|
||||
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
|
||||
if indexer_settings.minimum_age > 0 {
|
||||
@@ -105,7 +114,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
||||
}
|
||||
|
||||
fn handle_home(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
@@ -119,7 +128,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
||||
}
|
||||
|
||||
fn handle_end(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
@@ -137,15 +146,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
||||
fn handle_left_right_action(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||
if self.app.data.radarr_data.selected_block.get_active_block()
|
||||
== &ActiveRadarrBlock::IndexerSettingsConfirmPrompt
|
||||
{
|
||||
handle_prompt_toggle(self.app, self.key);
|
||||
} else {
|
||||
let len = self.app.data.radarr_data.selected_block.blocks.len();
|
||||
let idx = self.app.data.radarr_data.selected_block.index;
|
||||
self.app.data.radarr_data.selected_block.index = (idx + 5) % len;
|
||||
}
|
||||
handle_prompt_left_right_keys!(
|
||||
self,
|
||||
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
|
||||
radarr_data
|
||||
);
|
||||
}
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput => {
|
||||
handle_text_box_left_right_keys!(
|
||||
@@ -170,12 +175,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||
match self.app.data.radarr_data.selected_block.get_active_block() {
|
||||
ActiveRadarrBlock::IndexerSettingsConfirmPrompt => {
|
||||
let radarr_data = &mut self.app.data.radarr_data;
|
||||
if radarr_data.prompt_confirm {
|
||||
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditAllIndexerSettings(None));
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(
|
||||
RadarrEvent::EditAllIndexerSettings(self.build_edit_indexer_settings_body()),
|
||||
);
|
||||
self.app.should_refresh = true;
|
||||
} else {
|
||||
radarr_data.indexer_settings = None;
|
||||
self.app.data.radarr_data.indexer_settings = None;
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
@@ -187,7 +193,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
||||
| ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput => {
|
||||
self.app.push_navigation_stack(
|
||||
(
|
||||
*self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
@@ -258,12 +264,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
||||
}
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||
if self.app.data.radarr_data.selected_block.get_active_block()
|
||||
== &ActiveRadarrBlock::IndexerSettingsConfirmPrompt
|
||||
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
== ActiveRadarrBlock::IndexerSettingsConfirmPrompt
|
||||
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
{
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::EditAllIndexerSettings(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(
|
||||
RadarrEvent::EditAllIndexerSettings(self.build_edit_indexer_settings_body()),
|
||||
);
|
||||
self.app.should_refresh = true;
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::radarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
|
||||
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::indexer_settings;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::models::radarr_models::IndexerSettings;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
@@ -27,7 +29,7 @@ mod tests {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
|
||||
IndexerSettingsHandler::with(&$key, &mut app, &$block, &None).handle();
|
||||
IndexerSettingsHandler::with($key, &mut app, $block, None).handle();
|
||||
|
||||
if $key == Key::Up {
|
||||
assert_eq!(
|
||||
@@ -64,7 +66,7 @@ mod tests {
|
||||
0
|
||||
);
|
||||
|
||||
IndexerSettingsHandler::with(&Key::Up, &mut app, &$block, &None).handle();
|
||||
IndexerSettingsHandler::with(Key::Up, &mut app, $block, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app
|
||||
@@ -77,7 +79,7 @@ mod tests {
|
||||
1
|
||||
);
|
||||
|
||||
IndexerSettingsHandler::with(&$key, &mut app, &$block, &None).handle();
|
||||
IndexerSettingsHandler::with($key, &mut app, $block, None).handle();
|
||||
assert_eq!(
|
||||
app
|
||||
.data
|
||||
@@ -98,26 +100,26 @@ mod tests {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.next();
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.down();
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
if key == Key::Up {
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::IndexerSettingsMinimumAgeInput
|
||||
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::IndexerSettingsMaximumSizeInput
|
||||
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -130,20 +132,20 @@ mod tests {
|
||||
app.is_loading = true;
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.next();
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.down();
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::IndexerSettingsRetentionInput
|
||||
ActiveRadarrBlock::IndexerSettingsRetentionInput
|
||||
);
|
||||
}
|
||||
|
||||
@@ -218,10 +220,10 @@ mod tests {
|
||||
});
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -239,10 +241,10 @@ mod tests {
|
||||
);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -276,24 +278,24 @@ mod tests {
|
||||
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.index = INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1;
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.y = INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1;
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -323,44 +325,44 @@ mod tests {
|
||||
)]
|
||||
fn test_left_right_block_toggle(
|
||||
#[values(Key::Left, Key::Right)] key: Key,
|
||||
#[case] starting_index: usize,
|
||||
#[case] starting_y_index: usize,
|
||||
#[case] left_block: ActiveRadarrBlock,
|
||||
#[case] right_block: ActiveRadarrBlock,
|
||||
) {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.index = starting_index;
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.y = starting_y_index;
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&left_block
|
||||
left_block
|
||||
);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&right_block
|
||||
right_block
|
||||
);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&key,
|
||||
key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&left_block
|
||||
left_block
|
||||
);
|
||||
}
|
||||
|
||||
@@ -373,10 +375,10 @@ mod tests {
|
||||
});
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.left.key,
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -394,10 +396,10 @@ mod tests {
|
||||
);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.right.key,
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -438,23 +440,23 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
|
||||
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
assert!(!app.should_refresh);
|
||||
assert_eq!(app.data.radarr_data.indexer_settings, None);
|
||||
@@ -466,29 +468,29 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
|
||||
app.data.radarr_data.indexer_settings = Some(indexer_settings());
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(RadarrEvent::EditAllIndexerSettings(None))
|
||||
Some(RadarrEvent::EditAllIndexerSettings(indexer_settings()))
|
||||
);
|
||||
assert!(app.data.radarr_data.indexer_settings.is_some());
|
||||
assert!(app.data.radarr_data.indexer_settings.is_none());
|
||||
assert!(app.should_refresh);
|
||||
}
|
||||
|
||||
@@ -502,71 +504,80 @@ mod tests {
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
);
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsMinimumAgeInput, 0)]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsRetentionInput, 1)]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsMaximumSizeInput, 2)]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput, 5)]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput, 6)]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsMinimumAgeInput, 0, 0)]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsRetentionInput, 1, 0)]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsMaximumSizeInput, 2, 0)]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput, 0, 1)]
|
||||
#[case(ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput, 1, 1)]
|
||||
fn test_edit_indexer_settings_prompt_submit_selected_block(
|
||||
#[case] selected_block: ActiveRadarrBlock,
|
||||
#[case] index: usize,
|
||||
#[case] y_index: usize,
|
||||
#[case] x_index: usize,
|
||||
) {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(index);
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(x_index, y_index);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &selected_block.into());
|
||||
assert_eq!(app.get_current_route(), selected_block.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_edit_indexer_settings_prompt_submit_selected_block_no_op_when_not_ready(
|
||||
#[values(0, 1, 2, 5, 6)] index: usize,
|
||||
#[values((0, 0), (1, 0), (2, 0), (0, 1), (1, 1))] index: (usize, usize),
|
||||
) {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(index);
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(index.1, index.0);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -576,20 +587,20 @@ mod tests {
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(7);
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(1, 2);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput.into()
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput.into()
|
||||
);
|
||||
assert!(app.should_ignore_quit_key);
|
||||
}
|
||||
@@ -599,21 +610,21 @@ mod tests {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(3);
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(0, 3);
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
);
|
||||
assert!(
|
||||
app
|
||||
@@ -626,16 +637,16 @@ mod tests {
|
||||
);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
);
|
||||
assert!(
|
||||
!app
|
||||
@@ -653,21 +664,21 @@ mod tests {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(8);
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.set_index(1, 3);
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
);
|
||||
assert!(
|
||||
app
|
||||
@@ -680,16 +691,16 @@ mod tests {
|
||||
);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
);
|
||||
assert!(
|
||||
!app
|
||||
@@ -716,10 +727,10 @@ mod tests {
|
||||
);
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -735,7 +746,7 @@ mod tests {
|
||||
.is_empty());
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -755,11 +766,11 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
app.push_navigation_stack(active_radarr_block.into());
|
||||
|
||||
IndexerSettingsHandler::with(&SUBMIT_KEY, &mut app, &active_radarr_block, &None).handle();
|
||||
IndexerSettingsHandler::with(SUBMIT_KEY, &mut app, active_radarr_block, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -783,14 +794,14 @@ mod tests {
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&ESC_KEY,
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.data.radarr_data.indexer_settings, None);
|
||||
}
|
||||
@@ -806,14 +817,14 @@ mod tests {
|
||||
app.should_ignore_quit_key = true;
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&ESC_KEY,
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert!(!app.should_ignore_quit_key);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.indexer_settings,
|
||||
@@ -838,9 +849,9 @@ mod tests {
|
||||
app.push_navigation_stack(active_radarr_block.into());
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
|
||||
IndexerSettingsHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle();
|
||||
IndexerSettingsHandler::with(ESC_KEY, &mut app, active_radarr_block, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(
|
||||
app.data.radarr_data.indexer_settings,
|
||||
Some(IndexerSettings::default())
|
||||
@@ -849,7 +860,7 @@ mod tests {
|
||||
}
|
||||
|
||||
mod test_handle_key_char {
|
||||
use pretty_assertions::assert_str_eq;
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
|
||||
use crate::{
|
||||
models::{
|
||||
@@ -870,10 +881,10 @@ mod tests {
|
||||
});
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.backspace.key,
|
||||
DEFAULT_KEYBINDINGS.backspace.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -896,10 +907,10 @@ mod tests {
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&Key::Char('h'),
|
||||
Key::Char('h'),
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
&None,
|
||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
@@ -922,28 +933,28 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
|
||||
app.data.radarr_data.indexer_settings = Some(indexer_settings());
|
||||
|
||||
IndexerSettingsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.confirm.key,
|
||||
DEFAULT_KEYBINDINGS.confirm.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(RadarrEvent::EditAllIndexerSettings(None))
|
||||
Some(RadarrEvent::EditAllIndexerSettings(indexer_settings()))
|
||||
);
|
||||
assert!(app.data.radarr_data.indexer_settings.is_some());
|
||||
assert!(app.data.radarr_data.indexer_settings.is_none());
|
||||
assert!(app.should_refresh);
|
||||
}
|
||||
}
|
||||
@@ -952,23 +963,40 @@ mod tests {
|
||||
fn test_indexer_settings_handler_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if INDEXER_SETTINGS_BLOCKS.contains(&active_radarr_block) {
|
||||
assert!(IndexerSettingsHandler::accepts(&active_radarr_block));
|
||||
assert!(IndexerSettingsHandler::accepts(active_radarr_block));
|
||||
} else {
|
||||
assert!(!IndexerSettingsHandler::accepts(&active_radarr_block));
|
||||
assert!(!IndexerSettingsHandler::accepts(active_radarr_block));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_edit_indexer_settings_body() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.indexer_settings = Some(indexer_settings());
|
||||
|
||||
let body = IndexerSettingsHandler::with(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
)
|
||||
.build_edit_indexer_settings_body();
|
||||
|
||||
assert_eq!(body, indexer_settings());
|
||||
assert!(app.data.radarr_data.indexer_settings.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edit_indexer_settings_handler_not_ready_when_loading() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = IndexerSettingsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -980,10 +1008,10 @@ mod tests {
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = IndexerSettingsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -996,10 +1024,10 @@ mod tests {
|
||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||
|
||||
let handler = IndexerSettingsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
@@ -8,6 +8,7 @@ mod tests {
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::radarr_handlers::indexers::IndexersHandler;
|
||||
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::indexer;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
ActiveRadarrBlock, EDIT_INDEXER_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
||||
@@ -15,107 +16,6 @@ mod tests {
|
||||
use crate::models::servarr_models::Indexer;
|
||||
use crate::test_handler_delegation;
|
||||
|
||||
mod test_handle_scroll_up_and_down {
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
|
||||
|
||||
use super::*;
|
||||
|
||||
test_iterable_scroll!(
|
||||
test_indexers_scroll,
|
||||
IndexersHandler,
|
||||
indexers,
|
||||
simple_stateful_iterable_vec!(Indexer, String, protocol),
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
protocol
|
||||
);
|
||||
|
||||
#[rstest]
|
||||
fn test_indexers_scroll_no_op_when_not_ready(
|
||||
#[values(
|
||||
DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key
|
||||
)]
|
||||
key: Key,
|
||||
) {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexers
|
||||
.set_items(simple_stateful_iterable_vec!(Indexer, String, protocol));
|
||||
|
||||
IndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.indexers.current_selection().protocol,
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
IndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.indexers.current_selection().protocol,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_home_end {
|
||||
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
|
||||
|
||||
use super::*;
|
||||
|
||||
test_iterable_home_and_end!(
|
||||
test_indexers_home_end,
|
||||
IndexersHandler,
|
||||
indexers,
|
||||
extended_stateful_iterable_vec!(Indexer, String, protocol),
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
protocol
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn test_indexers_home_end_no_op_when_not_ready() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexers
|
||||
.set_items(extended_stateful_iterable_vec!(Indexer, String, protocol));
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.indexers.current_selection().protocol,
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app.data.radarr_data.indexers.current_selection().protocol,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_delete {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@@ -132,11 +32,11 @@ mod tests {
|
||||
.indexers
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
|
||||
IndexersHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Indexers, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::DeleteIndexerPrompt.into()
|
||||
ActiveRadarrBlock::DeleteIndexerPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -151,9 +51,9 @@ mod tests {
|
||||
.indexers
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
|
||||
IndexersHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Indexers, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,20 +70,20 @@ mod tests {
|
||||
app.data.radarr_data.main_tabs.set_index(5);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.left.key,
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
&ActiveRadarrBlock::RootFolders.into()
|
||||
ActiveRadarrBlock::RootFolders.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::RootFolders.into()
|
||||
ActiveRadarrBlock::RootFolders.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -194,18 +94,18 @@ mod tests {
|
||||
app.data.radarr_data.main_tabs.set_index(5);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.right.key,
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
&ActiveRadarrBlock::System.into()
|
||||
ActiveRadarrBlock::System.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@@ -214,40 +114,28 @@ mod tests {
|
||||
) {
|
||||
let mut app = App::default();
|
||||
|
||||
IndexersHandler::with(
|
||||
&key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
IndexersHandler::with(key, &mut app, ActiveRadarrBlock::DeleteIndexerPrompt, None).handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
|
||||
IndexersHandler::with(
|
||||
&key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
IndexersHandler::with(key, &mut app, ActiveRadarrBlock::DeleteIndexerPrompt, None).handle();
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_submit {
|
||||
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::indexer;
|
||||
use crate::models::servarr_data::modals::EditIndexerModal;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
RadarrData, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
|
||||
};
|
||||
use crate::models::servarr_models::{Indexer, IndexerField};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use bimap::BiMap;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::{Number, Value};
|
||||
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
|
||||
use super::*;
|
||||
|
||||
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
||||
@@ -306,11 +194,11 @@ mod tests {
|
||||
radarr_data.indexers.set_items(vec![indexer]);
|
||||
app.data.radarr_data = radarr_data;
|
||||
|
||||
IndexersHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
|
||||
IndexersHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::Indexers, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::EditIndexerPrompt.into()
|
||||
ActiveRadarrBlock::EditIndexerPrompt.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.edit_indexer_modal,
|
||||
@@ -323,12 +211,12 @@ mod tests {
|
||||
if torrent_protocol {
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.blocks,
|
||||
&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS
|
||||
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.blocks,
|
||||
&EDIT_INDEXER_NZB_SELECTION_BLOCKS
|
||||
EDIT_INDEXER_NZB_SELECTION_BLOCKS
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -344,38 +232,34 @@ mod tests {
|
||||
.indexers
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
|
||||
IndexersHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::Indexers, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.data.radarr_data.edit_indexer_modal, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_indexer_prompt_confirm_submit() {
|
||||
let mut app = App::default();
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexers
|
||||
.set_items(vec![Indexer::default()]);
|
||||
app.data.radarr_data.indexers.set_items(vec![indexer()]);
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
|
||||
|
||||
IndexersHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(RadarrEvent::DeleteIndexer(None))
|
||||
Some(RadarrEvent::DeleteIndexer(1))
|
||||
);
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -390,16 +274,16 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
|
||||
|
||||
IndexersHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,14 +303,14 @@ mod tests {
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
|
||||
IndexersHandler::with(
|
||||
&ESC_KEY,
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
@@ -434,14 +318,14 @@ mod tests {
|
||||
fn test_test_indexer_esc(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::default();
|
||||
app.is_loading = is_ready;
|
||||
app.data.radarr_data.indexer_test_error = Some("test result".to_owned());
|
||||
app.data.radarr_data.indexer_test_errors = Some("test result".to_owned());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::TestIndexer.into());
|
||||
|
||||
IndexersHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::TestIndexer, &None).handle();
|
||||
IndexersHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::TestIndexer, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.data.radarr_data.indexer_test_error, None);
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.data.radarr_data.indexer_test_errors, None);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@@ -452,9 +336,9 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
|
||||
IndexersHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
|
||||
IndexersHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::Indexers, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert!(app.error.text.is_empty());
|
||||
}
|
||||
}
|
||||
@@ -462,58 +346,13 @@ mod tests {
|
||||
mod test_handle_key_char {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::indexer;
|
||||
use crate::{
|
||||
models::servarr_data::radarr::radarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS,
|
||||
network::radarr_network::RadarrEvent,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_indexer_add() {
|
||||
let mut app = App::default();
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexers
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.add.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AddIndexer.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_indexer_add_no_op_when_not_ready() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexers
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.add.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_refresh_indexers_key() {
|
||||
let mut app = App::default();
|
||||
@@ -525,14 +364,14 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.refresh.key,
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert!(app.should_refresh);
|
||||
}
|
||||
|
||||
@@ -548,14 +387,14 @@ mod tests {
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.refresh.key,
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
|
||||
@@ -569,20 +408,20 @@ mod tests {
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.settings.key,
|
||||
DEFAULT_KEYBINDINGS.settings.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.blocks,
|
||||
&INDEXER_SETTINGS_SELECTION_BLOCKS
|
||||
INDEXER_SETTINGS_SELECTION_BLOCKS
|
||||
);
|
||||
}
|
||||
|
||||
@@ -598,14 +437,14 @@ mod tests {
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.settings.key,
|
||||
DEFAULT_KEYBINDINGS.settings.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -618,16 +457,16 @@ mod tests {
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.test.key,
|
||||
DEFAULT_KEYBINDINGS.test.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::TestIndexer.into()
|
||||
ActiveRadarrBlock::TestIndexer.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -643,14 +482,14 @@ mod tests {
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.test.key,
|
||||
DEFAULT_KEYBINDINGS.test.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -663,16 +502,16 @@ mod tests {
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.test_all.key,
|
||||
DEFAULT_KEYBINDINGS.test_all.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::TestAllIndexers.into()
|
||||
ActiveRadarrBlock::TestAllIndexers.into()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -688,41 +527,37 @@ mod tests {
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.test_all.key,
|
||||
DEFAULT_KEYBINDINGS.test_all.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_indexer_prompt_confirm() {
|
||||
let mut app = App::default();
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexers
|
||||
.set_items(vec![Indexer::default()]);
|
||||
app.data.radarr_data.indexers.set_items(vec![indexer()]);
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
|
||||
|
||||
IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.confirm.key,
|
||||
DEFAULT_KEYBINDINGS.confirm.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(RadarrEvent::DeleteIndexer(None))
|
||||
Some(RadarrEvent::DeleteIndexer(1))
|
||||
);
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,23 +626,39 @@ mod tests {
|
||||
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if indexers_blocks.contains(&active_radarr_block) {
|
||||
assert!(IndexersHandler::accepts(&active_radarr_block));
|
||||
assert!(IndexersHandler::accepts(active_radarr_block));
|
||||
} else {
|
||||
assert!(!IndexersHandler::accepts(&active_radarr_block));
|
||||
assert!(!IndexersHandler::accepts(active_radarr_block));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_indexer_id() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.indexers.set_items(vec![indexer()]);
|
||||
|
||||
let indexer_id = IndexersHandler::with(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
)
|
||||
.extract_indexer_id();
|
||||
|
||||
assert_eq!(indexer_id, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_indexers_handler_not_ready_when_loading() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -819,10 +670,10 @@ mod tests {
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -839,10 +690,10 @@ mod tests {
|
||||
.set_items(vec![Indexer::default()]);
|
||||
|
||||
let handler = IndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::Indexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::Indexers,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handle_table_events;
|
||||
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
|
||||
use crate::handlers::radarr_handlers::indexers::edit_indexer_handler::EditIndexerHandler;
|
||||
use crate::handlers::radarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
|
||||
use crate::handlers::radarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler;
|
||||
use crate::handlers::table_handler::TableHandlingConfig;
|
||||
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
ActiveRadarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
|
||||
INDEXERS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS,
|
||||
};
|
||||
use crate::models::servarr_models::Indexer;
|
||||
use crate::models::BlockSelectionState;
|
||||
use crate::models::Scrollable;
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
|
||||
mod edit_indexer_handler;
|
||||
@@ -23,14 +25,26 @@ mod test_all_indexers_handler;
|
||||
mod indexers_handler_tests;
|
||||
|
||||
pub(super) struct IndexersHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> IndexersHandler<'a, 'b> {
|
||||
handle_table_events!(self, indexers, self.app.data.radarr_data.indexers, Indexer);
|
||||
|
||||
fn extract_indexer_id(&self) -> i64 {
|
||||
self.app.data.radarr_data.indexers.current_selection().id
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, 'b> {
|
||||
fn handle(&mut self) {
|
||||
let indexer_table_handling_config =
|
||||
TableHandlingConfig::new(ActiveRadarrBlock::Indexers.into());
|
||||
|
||||
if !self.handle_indexers_table_events(indexer_table_handling_config) {
|
||||
match self.active_radarr_block {
|
||||
_ if EditIndexerHandler::accepts(self.active_radarr_block) => {
|
||||
EditIndexerHandler::with(self.key, self.app, self.active_radarr_block, self.context)
|
||||
@@ -47,19 +61,20 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
||||
_ => self.handle_key_event(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
EditIndexerHandler::accepts(active_block)
|
||||
|| IndexerSettingsHandler::accepts(active_block)
|
||||
|| TestAllIndexersHandler::accepts(active_block)
|
||||
|| INDEXERS_BLOCKS.contains(active_block)
|
||||
|| INDEXERS_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
) -> IndexersHandler<'a, 'b> {
|
||||
IndexersHandler {
|
||||
key,
|
||||
@@ -69,7 +84,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -77,32 +92,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
||||
!self.app.is_loading && !self.app.data.radarr_data.indexers.is_empty()
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
|
||||
self.app.data.radarr_data.indexers.scroll_up();
|
||||
}
|
||||
}
|
||||
fn handle_scroll_up(&mut self) {}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
|
||||
self.app.data.radarr_data.indexers.scroll_down();
|
||||
}
|
||||
}
|
||||
fn handle_scroll_down(&mut self) {}
|
||||
|
||||
fn handle_home(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
|
||||
self.app.data.radarr_data.indexers.scroll_to_top();
|
||||
}
|
||||
}
|
||||
fn handle_home(&mut self) {}
|
||||
|
||||
fn handle_end(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
|
||||
self.app.data.radarr_data.indexers.scroll_to_bottom();
|
||||
}
|
||||
}
|
||||
fn handle_end(&mut self) {}
|
||||
|
||||
fn handle_delete(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::Indexers {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
|
||||
@@ -120,9 +119,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
||||
fn handle_submit(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::DeleteIndexerPrompt => {
|
||||
let indexer_id = self.extract_indexer_id();
|
||||
let radarr_data = &mut self.app.data.radarr_data;
|
||||
if radarr_data.prompt_confirm {
|
||||
radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(None));
|
||||
radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(indexer_id));
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
@@ -141,10 +141,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
||||
.protocol;
|
||||
if protocol == "torrent" {
|
||||
self.app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_INDEXER_TORRENT_SELECTION_BLOCKS);
|
||||
} else {
|
||||
self.app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&EDIT_INDEXER_NZB_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(EDIT_INDEXER_NZB_SELECTION_BLOCKS);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
@@ -159,7 +159,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
||||
}
|
||||
ActiveRadarrBlock::TestIndexer => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.radarr_data.indexer_test_error = None;
|
||||
self.app.data.radarr_data.indexer_test_errors = None;
|
||||
}
|
||||
_ => handle_clear_errors(self.app),
|
||||
}
|
||||
@@ -169,37 +169,33 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
||||
let key = self.key;
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::Indexers => match self.key {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.add.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::AddIndexer.into());
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.test.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.test.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::TestIndexer.into());
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.test_all.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.test_all.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::TestAllIndexers.into());
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.settings.key => {
|
||||
_ if key == DEFAULT_KEYBINDINGS.settings.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||
self.app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveRadarrBlock::DeleteIndexerPrompt => {
|
||||
if *key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
if key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::DeleteIndexer(self.extract_indexer_id()));
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
|
||||
@@ -1,30 +1,58 @@
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handle_table_events;
|
||||
use crate::handlers::table_handler::TableHandlingConfig;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::models::Scrollable;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "test_all_indexers_handler_tests.rs"]
|
||||
mod test_all_indexers_handler_tests;
|
||||
|
||||
pub(super) struct TestAllIndexersHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> TestAllIndexersHandler<'a, 'b> {
|
||||
handle_table_events!(
|
||||
self,
|
||||
indexer_test_all_results,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_mut()
|
||||
.unwrap(),
|
||||
IndexerTestResultModalItem
|
||||
);
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandler<'a, 'b> {
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
active_block == &ActiveRadarrBlock::TestAllIndexers
|
||||
fn handle(&mut self) {
|
||||
let test_all_indexers_test_results_table_handler_config =
|
||||
TableHandlingConfig::new(ActiveRadarrBlock::TestAllIndexers.into());
|
||||
|
||||
if !self.handle_indexer_test_all_results_table_events(
|
||||
test_all_indexers_test_results_table_handler_config,
|
||||
) {
|
||||
self.handle_key_event();
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
active_block == ActiveRadarrBlock::TestAllIndexers
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
) -> TestAllIndexersHandler<'a, 'b> {
|
||||
TestAllIndexersHandler {
|
||||
key,
|
||||
@@ -34,7 +62,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -48,57 +76,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
|
||||
!self.app.is_loading && table_is_ready
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_up()
|
||||
}
|
||||
}
|
||||
fn handle_scroll_up(&mut self) {}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_down()
|
||||
}
|
||||
}
|
||||
fn handle_scroll_down(&mut self) {}
|
||||
|
||||
fn handle_home(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_to_top()
|
||||
}
|
||||
}
|
||||
fn handle_home(&mut self) {}
|
||||
|
||||
fn handle_end(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_to_bottom()
|
||||
}
|
||||
}
|
||||
fn handle_end(&mut self) {}
|
||||
|
||||
fn handle_delete(&mut self) {}
|
||||
|
||||
@@ -107,7 +91,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
|
||||
fn handle_submit(&mut self) {}
|
||||
|
||||
fn handle_esc(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::TestAllIndexers {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.radarr_data.indexer_test_all_results = None;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
mod tests {
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::radarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
|
||||
@@ -10,220 +9,6 @@ mod tests {
|
||||
use crate::models::stateful_table::StatefulTable;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
mod test_handle_scroll_up_and_down {
|
||||
use pretty_assertions::assert_str_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
|
||||
use crate::models::stateful_table::StatefulTable;
|
||||
use crate::simple_stateful_iterable_vec;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[rstest]
|
||||
fn test_test_all_indexers_results_scroll(
|
||||
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
|
||||
) {
|
||||
let mut app = App::default();
|
||||
let mut indexer_test_results = StatefulTable::default();
|
||||
indexer_test_results.set_items(simple_stateful_iterable_vec!(
|
||||
IndexerTestResultModalItem,
|
||||
String,
|
||||
name
|
||||
));
|
||||
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
|
||||
|
||||
TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.name,
|
||||
"Test 2"
|
||||
);
|
||||
|
||||
TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.name,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_test_all_indexers_results_scroll_no_op_when_not_ready(
|
||||
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
|
||||
) {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
let mut indexer_test_results = StatefulTable::default();
|
||||
indexer_test_results.set_items(simple_stateful_iterable_vec!(
|
||||
IndexerTestResultModalItem,
|
||||
String,
|
||||
name
|
||||
));
|
||||
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
|
||||
|
||||
TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.name,
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.name,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_home_end {
|
||||
use crate::extended_stateful_iterable_vec;
|
||||
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
|
||||
use crate::models::stateful_table::StatefulTable;
|
||||
use pretty_assertions::assert_str_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_test_all_indexers_results_home_end() {
|
||||
let mut app = App::default();
|
||||
let mut indexer_test_results = StatefulTable::default();
|
||||
indexer_test_results.set_items(extended_stateful_iterable_vec!(
|
||||
IndexerTestResultModalItem,
|
||||
String,
|
||||
name
|
||||
));
|
||||
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
|
||||
|
||||
TestAllIndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::TestAllIndexers,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.name,
|
||||
"Test 3"
|
||||
);
|
||||
|
||||
TestAllIndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::TestAllIndexers,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.name,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_test_all_indexers_results_home_end_no_op_when_not_ready() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
let mut indexer_test_results = StatefulTable::default();
|
||||
indexer_test_results.set_items(extended_stateful_iterable_vec!(
|
||||
IndexerTestResultModalItem,
|
||||
String,
|
||||
name
|
||||
));
|
||||
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
|
||||
|
||||
TestAllIndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::TestAllIndexers,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.name,
|
||||
"Test 1"
|
||||
);
|
||||
|
||||
TestAllIndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::TestAllIndexers,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexer_test_all_results
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.name,
|
||||
"Test 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_esc {
|
||||
use super::*;
|
||||
use crate::models::stateful_table::StatefulTable;
|
||||
@@ -239,14 +24,14 @@ mod tests {
|
||||
app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default());
|
||||
|
||||
TestAllIndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::TestAllIndexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::TestAllIndexers,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert!(app.data.radarr_data.indexer_test_all_results.is_none());
|
||||
}
|
||||
@@ -256,9 +41,9 @@ mod tests {
|
||||
fn test_test_all_indexers_handler_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if active_radarr_block == ActiveRadarrBlock::TestAllIndexers {
|
||||
assert!(TestAllIndexersHandler::accepts(&active_radarr_block));
|
||||
assert!(TestAllIndexersHandler::accepts(active_radarr_block));
|
||||
} else {
|
||||
assert!(!TestAllIndexersHandler::accepts(&active_radarr_block));
|
||||
assert!(!TestAllIndexersHandler::accepts(active_radarr_block));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -269,10 +54,10 @@ mod tests {
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = TestAllIndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::TestAllIndexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::TestAllIndexers,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -284,10 +69,10 @@ mod tests {
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = TestAllIndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::TestAllIndexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::TestAllIndexers,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -300,10 +85,10 @@ mod tests {
|
||||
app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default());
|
||||
|
||||
let handler = TestAllIndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::TestAllIndexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::TestAllIndexers,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -318,10 +103,10 @@ mod tests {
|
||||
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
|
||||
|
||||
let handler = TestAllIndexersHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::TestAllIndexers,
|
||||
&None,
|
||||
ActiveRadarrBlock::TestAllIndexers,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
|
||||
@@ -1,33 +1,149 @@
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::handlers::table_handler::TableHandlingConfig;
|
||||
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
|
||||
use crate::models::radarr_models::{
|
||||
AddMovieBody, AddMovieOptions, AddMovieSearchResult, CollectionMovie,
|
||||
};
|
||||
use crate::models::servarr_data::radarr::modals::AddMovieModal;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{
|
||||
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS,
|
||||
};
|
||||
use crate::models::stateful_table::StatefulTable;
|
||||
use crate::models::{BlockSelectionState, Scrollable};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, App, Key};
|
||||
use crate::{handle_table_events, handle_text_box_keys, handle_text_box_left_right_keys, App, Key};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "add_movie_handler_tests.rs"]
|
||||
mod add_movie_handler_tests;
|
||||
|
||||
pub(super) struct AddMovieHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> AddMovieHandler<'a, 'b> {
|
||||
handle_table_events!(
|
||||
self,
|
||||
add_movie_search_results,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.add_searched_movies
|
||||
.as_mut()
|
||||
.unwrap_or(&mut StatefulTable::default()),
|
||||
AddMovieSearchResult
|
||||
);
|
||||
|
||||
fn build_add_movie_body(&mut self) -> AddMovieBody {
|
||||
let tags = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.add_movie_modal
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.tags
|
||||
.text
|
||||
.clone();
|
||||
let AddMovieModal {
|
||||
root_folder_list,
|
||||
monitor_list,
|
||||
minimum_availability_list,
|
||||
quality_profile_list,
|
||||
..
|
||||
} = self.app.data.radarr_data.add_movie_modal.as_ref().unwrap();
|
||||
let (tmdb_id, title) = if let Some(context) = self.context {
|
||||
if context == ActiveRadarrBlock::CollectionDetails {
|
||||
let CollectionMovie { tmdb_id, title, .. } = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.collection_movies
|
||||
.current_selection()
|
||||
.clone();
|
||||
(tmdb_id, title.text)
|
||||
} else {
|
||||
let AddMovieSearchResult { tmdb_id, title, .. } = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.add_searched_movies
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.clone();
|
||||
(tmdb_id, title.text)
|
||||
}
|
||||
} else {
|
||||
let AddMovieSearchResult { tmdb_id, title, .. } = self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.add_searched_movies
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.current_selection()
|
||||
.clone();
|
||||
(tmdb_id, title.text)
|
||||
};
|
||||
let quality_profile = quality_profile_list.current_selection();
|
||||
let quality_profile_id = *self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.quality_profile_map
|
||||
.iter()
|
||||
.filter(|(_, value)| *value == quality_profile)
|
||||
.map(|(key, _)| key)
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
let path = root_folder_list.current_selection().path.clone();
|
||||
let monitor = monitor_list.current_selection().to_string();
|
||||
let minimum_availability = minimum_availability_list.current_selection().to_string();
|
||||
|
||||
self.app.data.radarr_data.add_movie_modal = None;
|
||||
|
||||
AddMovieBody {
|
||||
tmdb_id,
|
||||
title,
|
||||
root_folder_path: path,
|
||||
minimum_availability,
|
||||
monitored: true,
|
||||
quality_profile_id,
|
||||
tags: Vec::new(),
|
||||
tag_input_string: Some(tags),
|
||||
add_options: AddMovieOptions {
|
||||
monitor,
|
||||
search_for_movie: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, 'b> {
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
ADD_MOVIE_BLOCKS.contains(active_block)
|
||||
fn handle(&mut self) {
|
||||
let add_movie_table_handling_config =
|
||||
TableHandlingConfig::new(ActiveRadarrBlock::AddMovieSearchResults.into());
|
||||
|
||||
if !self.handle_add_movie_search_results_table_events(add_movie_table_handling_config) {
|
||||
self.handle_key_event();
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
ADD_MOVIE_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
) -> AddMovieHandler<'a, 'b> {
|
||||
AddMovieHandler {
|
||||
key,
|
||||
@@ -37,7 +153,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -47,14 +163,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::AddMovieSearchResults => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.add_searched_movies
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_up(),
|
||||
ActiveRadarrBlock::AddMovieSelectMonitor => self
|
||||
.app
|
||||
.data
|
||||
@@ -91,21 +199,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
.unwrap()
|
||||
.root_folder_list
|
||||
.scroll_up(),
|
||||
ActiveRadarrBlock::AddMoviePrompt => self.app.data.radarr_data.selected_block.previous(),
|
||||
ActiveRadarrBlock::AddMoviePrompt => self.app.data.radarr_data.selected_block.up(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::AddMovieSearchResults => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.add_searched_movies
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_down(),
|
||||
ActiveRadarrBlock::AddMovieSelectMonitor => self
|
||||
.app
|
||||
.data
|
||||
@@ -142,21 +242,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
.unwrap()
|
||||
.root_folder_list
|
||||
.scroll_down(),
|
||||
ActiveRadarrBlock::AddMoviePrompt => self.app.data.radarr_data.selected_block.next(),
|
||||
ActiveRadarrBlock::AddMoviePrompt => self.app.data.radarr_data.selected_block.down(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_home(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::AddMovieSearchResults => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.add_searched_movies
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_to_top(),
|
||||
ActiveRadarrBlock::AddMovieSelectMonitor => self
|
||||
.app
|
||||
.data
|
||||
@@ -216,14 +308,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
|
||||
fn handle_end(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::AddMovieSearchResults => self
|
||||
.app
|
||||
.data
|
||||
.radarr_data
|
||||
.add_searched_movies
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scroll_to_bottom(),
|
||||
ActiveRadarrBlock::AddMovieSelectMonitor => self
|
||||
.app
|
||||
.data
|
||||
@@ -313,7 +397,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
|
||||
fn handle_submit(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
_ if *self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchInput
|
||||
_ if self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchInput
|
||||
&& !self
|
||||
.app
|
||||
.data
|
||||
@@ -329,7 +413,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchResults.into());
|
||||
self.app.should_ignore_quit_key = false;
|
||||
}
|
||||
_ if *self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchResults
|
||||
_ if self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchResults
|
||||
&& self.app.data.radarr_data.add_searched_movies.is_some() =>
|
||||
{
|
||||
let tmdb_id = self
|
||||
@@ -360,14 +444,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
|
||||
self.app.data.radarr_data.add_movie_modal = Some((&self.app.data.radarr_data).into());
|
||||
self.app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
|
||||
BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
|
||||
}
|
||||
}
|
||||
ActiveRadarrBlock::AddMoviePrompt => {
|
||||
match self.app.data.radarr_data.selected_block.get_active_block() {
|
||||
ActiveRadarrBlock::AddMovieConfirmPrompt => {
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::AddMovie(self.build_add_movie_body()));
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
@@ -377,16 +462,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
| ActiveRadarrBlock::AddMovieSelectQualityProfile
|
||||
| ActiveRadarrBlock::AddMovieSelectRootFolder => self.app.push_navigation_stack(
|
||||
(
|
||||
*self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
*self.context,
|
||||
self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
self.context,
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
ActiveRadarrBlock::AddMovieTagsInput => {
|
||||
self.app.push_navigation_stack(
|
||||
(
|
||||
*self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
*self.context,
|
||||
self.app.data.radarr_data.selected_block.get_active_block(),
|
||||
self.context,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
@@ -463,11 +548,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
||||
}
|
||||
ActiveRadarrBlock::AddMoviePrompt => {
|
||||
if self.app.data.radarr_data.selected_block.get_active_block()
|
||||
== &ActiveRadarrBlock::AddMovieConfirmPrompt
|
||||
&& *key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
== ActiveRadarrBlock::AddMovieConfirmPrompt
|
||||
&& key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
{
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::AddMovie(self.build_add_movie_body()));
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
|
||||
use crate::models::radarr_models::DeleteMovieParams;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DELETE_MOVIE_BLOCKS};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
|
||||
@@ -10,22 +11,37 @@ use crate::network::radarr_network::RadarrEvent;
|
||||
mod delete_movie_handler_tests;
|
||||
|
||||
pub(super) struct DeleteMovieHandler<'a, 'b> {
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> DeleteMovieHandler<'a, 'b> {
|
||||
fn build_delete_movie_params(&mut self) -> DeleteMovieParams {
|
||||
let id = self.app.data.radarr_data.movies.current_selection().id;
|
||||
let delete_movie_files = self.app.data.radarr_data.delete_movie_files;
|
||||
let add_list_exclusion = self.app.data.radarr_data.add_list_exclusion;
|
||||
self.app.data.radarr_data.reset_delete_movie_preferences();
|
||||
|
||||
DeleteMovieParams {
|
||||
id,
|
||||
delete_movie_files,
|
||||
add_list_exclusion,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'a, 'b> {
|
||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||
DELETE_MOVIE_BLOCKS.contains(active_block)
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
DELETE_MOVIE_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn with(
|
||||
key: &'a Key,
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: &'a ActiveRadarrBlock,
|
||||
_context: &'a Option<ActiveRadarrBlock>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
) -> Self {
|
||||
DeleteMovieHandler {
|
||||
key,
|
||||
@@ -35,7 +51,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Key {
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
@@ -44,14 +60,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
self.app.data.radarr_data.selected_block.previous();
|
||||
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
self.app.data.radarr_data.selected_block.up();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
self.app.data.radarr_data.selected_block.next();
|
||||
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
self.app.data.radarr_data.selected_block.down();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,17 +78,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
|
||||
fn handle_delete(&mut self) {}
|
||||
|
||||
fn handle_left_right_action(&mut self) {
|
||||
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
handle_prompt_toggle(self.app, self.key);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_submit(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
match self.app.data.radarr_data.selected_block.get_active_block() {
|
||||
ActiveRadarrBlock::DeleteMovieConfirmPrompt => {
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteMovie(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::DeleteMovie(self.build_delete_movie_params()));
|
||||
self.app.should_refresh = true;
|
||||
} else {
|
||||
self.app.data.radarr_data.reset_delete_movie_preferences();
|
||||
@@ -94,7 +111,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
|
||||
}
|
||||
|
||||
fn handle_esc(&mut self) {
|
||||
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.radarr_data.reset_delete_movie_preferences();
|
||||
self.app.data.radarr_data.prompt_confirm = false;
|
||||
@@ -102,13 +119,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
|
||||
}
|
||||
|
||||
fn handle_char_key_event(&mut self) {
|
||||
if self.active_radarr_block == &ActiveRadarrBlock::DeleteMoviePrompt
|
||||
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt
|
||||
&& self.app.data.radarr_data.selected_block.get_active_block()
|
||||
== &ActiveRadarrBlock::DeleteMovieConfirmPrompt
|
||||
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
== ActiveRadarrBlock::DeleteMovieConfirmPrompt
|
||||
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
|
||||
{
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteMovie(None));
|
||||
self.app.data.radarr_data.prompt_confirm_action =
|
||||
Some(RadarrEvent::DeleteMovie(self.build_delete_movie_params()));
|
||||
self.app.should_refresh = true;
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::radarr_handlers::library::delete_movie_handler::DeleteMovieHandler;
|
||||
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::movie;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::models::radarr_models::DeleteMovieParams;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DELETE_MOVIE_BLOCKS};
|
||||
|
||||
mod test_handle_scroll_up_and_down {
|
||||
@@ -21,22 +24,20 @@ mod tests {
|
||||
#[rstest]
|
||||
fn test_delete_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.next();
|
||||
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.down();
|
||||
|
||||
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None)
|
||||
.handle();
|
||||
DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
|
||||
|
||||
if key == Key::Up {
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::DeleteMovieToggleDeleteFile
|
||||
ActiveRadarrBlock::DeleteMovieToggleDeleteFile
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::DeleteMovieConfirmPrompt
|
||||
ActiveRadarrBlock::DeleteMovieConfirmPrompt
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -47,16 +48,14 @@ mod tests {
|
||||
) {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.next();
|
||||
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block.down();
|
||||
|
||||
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None)
|
||||
.handle();
|
||||
DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.selected_block.get_active_block(),
|
||||
&ActiveRadarrBlock::DeleteMovieToggleAddListExclusion
|
||||
ActiveRadarrBlock::DeleteMovieToggleAddListExclusion
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -70,13 +69,11 @@ mod tests {
|
||||
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
|
||||
let mut app = App::default();
|
||||
|
||||
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None)
|
||||
.handle();
|
||||
DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
|
||||
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None)
|
||||
.handle();
|
||||
DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
|
||||
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
@@ -98,25 +95,24 @@ mod tests {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
|
||||
.set_index(0, DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
|
||||
app.data.radarr_data.delete_movie_files = true;
|
||||
app.data.radarr_data.add_list_exclusion = true;
|
||||
|
||||
DeleteMovieHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert!(!app.data.radarr_data.delete_movie_files);
|
||||
@@ -126,36 +122,41 @@ mod tests {
|
||||
#[test]
|
||||
fn test_delete_movie_confirm_prompt_prompt_confirmation_submit() {
|
||||
let mut app = App::default();
|
||||
let expected_delete_movie_params = DeleteMovieParams {
|
||||
id: 1,
|
||||
delete_movie_files: true,
|
||||
add_list_exclusion: true,
|
||||
};
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
|
||||
app.data.radarr_data.movies.set_items(vec![movie()]);
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
app.data.radarr_data.delete_movie_files = true;
|
||||
app.data.radarr_data.add_list_exclusion = true;
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
|
||||
.set_index(0, DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
|
||||
|
||||
DeleteMovieHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(RadarrEvent::DeleteMovie(None))
|
||||
Some(RadarrEvent::DeleteMovie(expected_delete_movie_params))
|
||||
);
|
||||
assert!(app.should_refresh);
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
assert!(app.data.radarr_data.delete_movie_files);
|
||||
assert!(app.data.radarr_data.add_list_exclusion);
|
||||
assert!(!app.data.radarr_data.delete_movie_files);
|
||||
assert!(!app.data.radarr_data.add_list_exclusion);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -169,16 +170,16 @@ mod tests {
|
||||
app.data.radarr_data.add_list_exclusion = true;
|
||||
|
||||
DeleteMovieHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt.into()
|
||||
ActiveRadarrBlock::DeleteMoviePrompt.into()
|
||||
);
|
||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||
assert!(!app.should_refresh);
|
||||
@@ -191,36 +192,36 @@ mod tests {
|
||||
fn test_delete_movie_toggle_delete_files_submit() {
|
||||
let current_route = ActiveRadarrBlock::DeleteMoviePrompt.into();
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
|
||||
|
||||
DeleteMovieHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ¤t_route);
|
||||
assert_eq!(app.get_current_route(), current_route);
|
||||
assert_eq!(app.data.radarr_data.delete_movie_files, true);
|
||||
|
||||
DeleteMovieHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ¤t_route);
|
||||
assert_eq!(app.get_current_route(), current_route);
|
||||
assert_eq!(app.data.radarr_data.delete_movie_files, false);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_esc {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
|
||||
@@ -236,14 +237,14 @@ mod tests {
|
||||
app.data.radarr_data.add_list_exclusion = true;
|
||||
|
||||
DeleteMovieHandler::with(
|
||||
&ESC_KEY,
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert!(!app.data.radarr_data.delete_movie_files);
|
||||
assert!(!app.data.radarr_data.add_list_exclusion);
|
||||
@@ -257,41 +258,47 @@ mod tests {
|
||||
},
|
||||
network::radarr_network::RadarrEvent,
|
||||
};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_delete_movie_confirm_prompt_prompt_confirm() {
|
||||
let mut app = App::default();
|
||||
let expected_delete_movie_params = DeleteMovieParams {
|
||||
id: 1,
|
||||
delete_movie_files: true,
|
||||
add_list_exclusion: true,
|
||||
};
|
||||
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
|
||||
app.data.radarr_data.movies.set_items(vec![movie()]);
|
||||
app.data.radarr_data.delete_movie_files = true;
|
||||
app.data.radarr_data.add_list_exclusion = true;
|
||||
app.data.radarr_data.selected_block =
|
||||
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.selected_block
|
||||
.set_index(DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
|
||||
.set_index(0, DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
|
||||
|
||||
DeleteMovieHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.confirm.key,
|
||||
DEFAULT_KEYBINDINGS.confirm.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(RadarrEvent::DeleteMovie(None))
|
||||
Some(RadarrEvent::DeleteMovie(expected_delete_movie_params))
|
||||
);
|
||||
assert!(app.should_refresh);
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
assert!(app.data.radarr_data.delete_movie_files);
|
||||
assert!(app.data.radarr_data.add_list_exclusion);
|
||||
assert!(!app.data.radarr_data.delete_movie_files);
|
||||
assert!(!app.data.radarr_data.add_list_exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,23 +306,48 @@ mod tests {
|
||||
fn test_delete_movie_handler_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if DELETE_MOVIE_BLOCKS.contains(&active_radarr_block) {
|
||||
assert!(DeleteMovieHandler::accepts(&active_radarr_block));
|
||||
assert!(DeleteMovieHandler::accepts(active_radarr_block));
|
||||
} else {
|
||||
assert!(!DeleteMovieHandler::accepts(&active_radarr_block));
|
||||
assert!(!DeleteMovieHandler::accepts(active_radarr_block));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_delete_movie_params() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.movies.set_items(vec![movie()]);
|
||||
app.data.radarr_data.delete_movie_files = true;
|
||||
app.data.radarr_data.add_list_exclusion = true;
|
||||
let expected_delete_movie_params = DeleteMovieParams {
|
||||
id: 1,
|
||||
delete_movie_files: true,
|
||||
add_list_exclusion: true,
|
||||
};
|
||||
|
||||
let delete_movie_params = DeleteMovieHandler::with(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
)
|
||||
.build_delete_movie_params();
|
||||
|
||||
assert_eq!(delete_movie_params, expected_delete_movie_params);
|
||||
assert!(!app.data.radarr_data.delete_movie_files);
|
||||
assert!(!app.data.radarr_data.add_list_exclusion);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_movie_handler_not_ready_when_loading() {
|
||||
let mut app = App::default();
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = DeleteMovieHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
@@ -327,10 +359,10 @@ mod tests {
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = DeleteMovieHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.esc.key,
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
&None,
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
|
||||