Compare commits

...

108 Commits

Author SHA1 Message Date
90170cb3d5 ci: Fixed a typo in the github-release job [skip ci] 2024-12-14 00:14:52 -07:00
4dcb141f3a ci: Fixed a typo in the docker release [skip ci] 2024-12-13 23:41:01 -07:00
133721917f ci: Attempting a different artifact job version to see if it corrects the error [skip ci] 2024-12-13 23:19:07 -07:00
766e23d265 ci: Use the same version of upload/download-artifact action [skip ci] 2024-12-13 23:06:36 -07:00
77d8e84e14 ci: Correct the artifact paths for the release [skip ci] 2024-12-13 22:42:42 -07:00
00c1cca412 ci: Fix the artifacts directory creation for Windows binaries [skip ci] 2024-12-13 22:24:03 -07:00
4968833d05 ci: Attempting a different way of creating the artifacts directory [skip ci] 2024-12-13 22:05:22 -07:00
d172fa17f6 ci: Attempting to fix the artifacts directory [skip ci] 2024-12-13 21:58:30 -07:00
3c99b38db7 ci: Support for arm64 docker builds 2024-12-13 21:40:53 -07:00
Alex Clarke
1128937cac Merge pull request #21 from Dark-Alex-17/sonarr-tui-support
Sonarr TUI support
2024-12-13 21:09:53 -07:00
6fc1228173 docs: Updated the README to include OS-specific steps for running Managarr on different platforms 2024-12-13 21:04:10 -07:00
b48a2efb7d fix(blocklist_handler): Fixed a breaking change between Sonarr v3 and v4 2024-12-13 20:48:10 -07:00
412cb2408e fix(style): Addressed linter complaints on formatting 2024-12-13 19:48:22 -07:00
682bc91855 fix: Implemented a handful of fixes that are breaking changes between Sonarr v3 and v4 2024-12-13 19:44:10 -07:00
f03120e5a1 ci: Updated CI to cross compile for a handful of additional architectures to increase availability 2024-12-13 17:58:59 -07:00
8dd63b30e8 feat(docs): Updated the README with new screeshots for the Sonarr release 2024-12-13 16:28:42 -07:00
54006c378f feat(handler): Support for toggling the monitoring status of a specified episode in the Sonarr UI 2024-12-13 16:18:02 -07:00
9269b66aa8 feat(handlers): Support for toggling the monitoring status of a season in the Sonarr UI 2024-12-13 16:10:06 -07:00
cfac433861 feat(keybindings): Added a new keybinding for toggling the monitoring of a highlighted table item 2024-12-13 14:53:39 -07:00
a28f8c3dd2 feat(cli): Support for toggling monitoring on a specific episode in Sonarr 2024-12-13 14:49:00 -07:00
4001dee1bd refactor(network): Changed the toggle episode monitoring handler to simply return empty since the response is always empty from Sonarr 2024-12-13 14:45:06 -07:00
d1ffd0d77f feat(network): Support for toggling the monitoring status of an episode in Sonarr 2024-12-13 14:40:11 -07:00
91ad50350d feat(cli): Support for toggling monitoring for a specific season in Sonarr 2024-12-13 14:09:11 -07:00
a88d43807e feat(network): Support for toggling monitoring/unmonitoring a season 2024-12-13 13:59:02 -07:00
98619664cf refactor(ui): Tweaked some of the color schemes in the series table 2024-12-13 13:10:57 -07:00
39f8ad2106 refactor: Fixed a couple of typos in some test function names 2024-12-13 11:51:23 -07:00
82ce38d7b5 feat(handlers): Support for the episode details popup 2024-12-12 18:52:27 -07:00
12eb453fc7 feat(ui): Support for the episode details UI 2024-12-12 16:25:02 -07:00
a84324d3bc feat(handler): Full handler support for the Season details UI in Sonarr 2024-12-11 23:18:37 -07:00
ed2211586e refactor(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 2024-12-11 17:03:52 -07:00
c09950d0af refactor(ui): Simplified the popup delegation so all future UI is easier to implement 2024-12-11 15:08:52 -07:00
e9a30382a3 feat(ui): Sonarr support for viewing season details 2024-12-10 18:23:09 -07:00
7bf3311102 feat(cli): Sonarr support for fetching a list of all episode files for a given series ID 2024-12-10 16:32:35 -07:00
cbad40245f feat(app): Dispatch support for Season Details to fetch both the current downloads as well as the episode files to match qualities to them 2024-12-10 16:23:30 -07:00
75c4fcbb9e feat(network): Support for fetching all episode files for a given series 2024-12-10 16:22:02 -07:00
f3b7f155b7 feat(app): Model and modal support for the season and episode details popups 2024-12-09 15:15:09 -07:00
6427a80bd1 feat(cli): Sonarr support for fetching season history events 2024-12-09 14:30:07 -07:00
5b65e87225 feat(network): Sonarr support for fetching season history 2024-12-09 14:15:47 -07:00
1b8b19fde5 refactor(indexers_handler): Use the new handle_table_events macro 2024-12-08 14:42:18 -07:00
03d7aed258 refactor(root_folders_handler): Use the new handle_table_events macro 2024-12-08 14:38:26 -07:00
23d149093f refactor(blocklist_handler): Use the new handle_table_events macro 2024-12-08 14:34:47 -07:00
27f12716d9 refactor(downloads_handler): Use the new handle_table_events macro 2024-12-08 14:28:12 -07:00
048877bbb6 refactor(collection_details_handler): use the new handle_table_events macro 2024-12-08 14:22:59 -07:00
87a652d911 refactor(collections_handler): Use the new handle_table_events macro 2024-12-08 14:14:24 -07:00
d6863dc1fd refactor(movie_details_handler): Use the new handle_table_events macro 2024-12-08 14:04:34 -07:00
f1d934b0a6 refactor(library_handler): Radarr use the new handle_table_events macro 2024-12-08 13:43:01 -07:00
5850f7a621 refactor(indexers_handler): Use the new handle_table_events macro 2024-12-08 13:26:59 -07:00
dd23e84ccf refactor(indexers_handler): Use the new handle_table_events macro 2024-12-08 13:24:18 -07:00
b060518778 refactor(root_folder_handler): Use the new handle_table_events macro 2024-12-08 13:15:59 -07:00
de95f13feb fix(handler_tests): Fixed all delegation tests to have initial conditions set properly 2024-12-08 13:10:17 -07:00
0205f13e53 refactor(history_handler): Use the new handle_table_event macro 2024-12-08 13:08:43 -07:00
b4de97dfe2 refactor(blocklist_handler): Use the new handle_table_events macro 2024-12-08 12:39:07 -07:00
35bc6cf31c refactor(downloads_handler): Use the new handle_table_events macro 2024-12-08 12:35:12 -07:00
c58e8b1a00 refactor(series_details_handler): Use the new handle_table_events macro 2024-12-08 12:29:59 -07:00
accdf99503 fix(ui): Fixed a bug that requires a minimum height for all popups so all error messages and other simple popups appear 2024-12-07 19:36:43 -07:00
47b609369b refactor(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 2024-12-07 19:20:13 -07:00
23b1ca4371 feat(ui): Sonarr support for the series details popup 2024-12-06 20:30:26 -07:00
73d666d1f5 feat(ui): Sonarr support for editing a series from within the series details popup 2024-12-05 19:11:54 -07:00
b27c13cf74 fix(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 2024-12-05 19:08:11 -07:00
bd1a4f0939 feat(ui): Sonarr Series details UI is now available 2024-12-05 19:07:45 -07:00
5abed23cf2 refactor(ui): all table search and filter functionality is now available directly through the ManagarrTable widget to make life easier moving forward 2024-12-05 19:07:03 -07:00
9d0948e124 refactor(keys): Created a auto search key instead of reusing the existing search key to make things easier 2024-12-05 12:29:09 -07:00
678bc77a23 fix(ui): Fix the System Details Tasks popup to be navigable in both Sonarr and Radarr 2024-12-05 11:45:46 -07:00
00cdeee5c6 feat(ui): Full Sonarr system tab support
Signed-off-by: Alex Clarke <alex.j.tusa@gmail.com>
2024-12-04 17:41:30 -07:00
2d251554ad feat(handler): System handler support for Sonarr 2024-12-04 17:04:36 -07:00
1b5d70ae2d perf: Improved performance by optimizing API calls to only refresh when the tick prompts a refresh. All UI is now significantly faster 2024-12-04 16:46:06 -07:00
2d2901f6dc feat(ui): Full Sonarr support for the indexer tab 2024-12-04 16:39:37 -07:00
a0b27ec105 feat(ui): Support for modifying the indexer priority in Radarr 2024-12-03 18:12:23 -07:00
093ef136e7 feat(handler): Full indexer tab handler support 2024-12-03 17:46:37 -07:00
8660de530d feat(ui): Root folder tab support 2024-12-03 16:24:23 -07:00
bda6f253e0 feat(handlers): Support for root folder actions 2024-12-03 16:18:39 -07:00
4eb974567f feat(ui): History tab support 2024-12-02 18:47:50 -07:00
4f5bad5874 feat(handler): History tab support 2024-12-02 18:03:59 -07:00
1c6e798632 feat(ui): Blocklist UI support 2024-12-02 16:54:27 -07:00
3186fb42e7 feat(handler): Wired in the blocklist handler to the main handlers 2024-12-02 16:39:40 -07:00
4b7185fbb0 feat(handler): Blocklist handler support 2024-12-02 16:37:46 -07:00
f0d8555a8a feat(ui): Downloads tab support 2024-12-02 15:57:48 -07:00
f338dfcb12 feat(handler): Download tab support 2024-12-02 15:40:11 -07:00
188d781b0d feat(ui): Edit series support 2024-12-02 15:31:12 -07:00
adb1f07fd0 feat(handler): Edit series support 2024-12-02 14:58:51 -07:00
82e51be096 feat(ui): Add series support Sonarr 2024-12-02 13:53:28 -07:00
d7f6d12f59 feat(handler): Add series support for Sonarr 2024-12-02 12:43:17 -07:00
0db57fbff1 feat(ui): Delete a series 2024-12-02 11:45:13 -07:00
b1bdc19afb feat(handler): Support for deleting a series in Sonarr 2024-12-02 11:30:34 -07:00
b75a95a708 feat(ui): Support for the Series table 2024-12-01 14:08:06 -07:00
c3fb5dcd5f feat(handlers): Sonarr key support for the Series table 2024-12-01 13:48:48 -07:00
21911f93d1 feat(models): Added the necessary contextual help and tabs for the Sonarr UI 2024-12-01 12:05:20 -07:00
f7c96d81e9 refactor(BlockSelectionState): Refactored so selection of blocks in 2x2 grids is more intuitive and added left() and right() methods to aid this effort. 2024-11-30 12:22:46 -07:00
9b2040059d fix(ui): Fixed a potential rare bug in the UI where the application would panic if the height of the downloads window is 0. 2024-11-29 16:31:51 -07:00
08f190fc6e feat(ui): Initial UI support for switching to Sonarr tabs 2024-11-29 15:58:19 -07:00
4d1b0fe301 docs(context): Updated the Servarr context clues to say how to switch Servarr tabs via TAB and SHIFT+TAB 2024-11-27 17:14:40 -07:00
f139db07d9 feat(app): Dispatch support for all relevant Sonarr blocks 2024-11-27 17:06:20 -07:00
Alex Clarke
73a4129000 Update environment variables table so it appears better in Crates.io [skip ci] 2024-11-25 20:25:04 -07:00
1ddf797e28 ci: Updated the release so all GitHub release names are correctly simply the version: v0.1.2 [skip ci] 2024-11-25 20:11:15 -07:00
github-actions[bot]
18280f0478 chore: Bump the version in Cargo.lock 2024-11-26 03:04:19 +00:00
github-actions[bot]
4348705a0a bump: version 0.3.6 → 0.3.7 [skip ci] 2024-11-26 03:04:17 +00:00
5a1b92547d fix(ci): Forgot to also pull in the most recent changes [skip ci] 2024-11-25 20:02:37 -07:00
github-actions[bot]
9e44713985 chore: Bump the version in Cargo.lock 2024-11-26 02:59:13 +00:00
github-actions[bot]
e93837fef7 bump: version 0.3.5 → 0.3.6 [skip ci] 2024-11-26 02:59:11 +00:00
6006c9d0e8 fix(ci): Ensure the Release Crate job fetches the most recent commit before publishing the crate [skip ci] 2024-11-25 19:58:34 -07:00
github-actions[bot]
c93543186a chore: Bump the version in Cargo.lock 2024-11-26 02:49:11 +00:00
github-actions[bot]
e1b74d7a36 bump: version 0.3.3 → 0.3.4 [skip ci] 2024-11-26 02:49:07 +00:00
6375bc3413 docs(README): Updated the README to not use GitHub markdown symbols and to directly use the unicode symbols so it can be displayed correctly in Crates.io [skip ci] 2024-11-25 19:47:57 -07:00
github-actions[bot]
9c99e0d2ef chore: Bump the version in Cargo.lock 2024-11-26 02:43:36 +00:00
github-actions[bot]
518ccaadc8 bump: version 0.3.2 → 0.3.3 [skip ci] 2024-11-26 02:43:34 +00:00
a766b395c1 fix(ci): Properly prefix version tags with 'v' [skip ci] 2024-11-25 19:41:11 -07:00
1746869b45 fix(ci): Bump the version in the Cargo.lock file and commit it as well when releasing [skip ci] 2024-11-25 19:39:36 -07:00
github-actions[bot]
533366b90b bump: version 0.3.1 → 0.3.2 [skip ci] 2024-11-26 02:33:19 +00:00
226 changed files with 36001 additions and 10152 deletions
+11 -2
View File
@@ -11,8 +11,6 @@ name: Check
env: env:
CARGO_TERM_COLOR: always 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: concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true cancel-in-progress: true
@@ -24,14 +22,18 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Rust stable - name: Install Rust stable
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
components: rustfmt components: rustfmt
- name: Run cargo fmt - name: Run cargo fmt
run: cargo fmt -- --check run: cargo fmt -- --check
- name: Cache Cargo dependencies - name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
clippy: clippy:
name: ${{ matrix.toolchain }} / clippy name: ${{ matrix.toolchain }} / clippy
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -45,12 +47,15 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Rust stable - name: Install Rust stable
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
components: clippy components: clippy
- name: Run clippy action - name: Run clippy action
uses: clechasseur/rs-clippy-check@v3 uses: clechasseur/rs-clippy-check@v3
- name: Cache Cargo dependencies - name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
doc: doc:
@@ -61,8 +66,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Rust nightly - name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly uses: dtolnay/rust-toolchain@nightly
- name: Run cargo doc - name: Run cargo doc
run: cargo doc --no-deps --all-features run: cargo doc --no-deps --all-features
env: env:
@@ -73,9 +80,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install 1.82.0 - name: Install 1.82.0
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
toolchain: 1.82.0 toolchain: 1.82.0
- name: cargo +1.82.0 check - name: cargo +1.82.0 check
run: cargo check run: cargo check
+237 -6
View File
@@ -18,7 +18,139 @@ on:
- major - major
jobs: jobs:
bump: build-release-artifacts:
name: build-release
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 }}
run: |
echo "You are not authorized to run this workflow."
exit 1
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: actions/cache@v3
name: Cache Cargo registry
with:
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 }}
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 SHA to artifacts
run: |
mkdir -p 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 runs-on: ubuntu-latest
steps: steps:
- name: Configure SSH for Git - name: Configure SSH for Git
@@ -34,6 +166,12 @@ jobs:
ssh-key: ${{ secrets.RELEASE_BOT_SSH_KEY }} ssh-key: ${{ secrets.RELEASE_BOT_SSH_KEY }}
fetch-depth: 0 fetch-depth: 0
- name: Download all artifacts
uses: actions/download-artifact@v3
with:
name: artifacts
path: artifacts
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
@@ -58,12 +196,22 @@ jobs:
run: | run: |
git commit --amend --no-edit -m "$(git log -1 --pretty=%B) [skip ci]" git commit --amend --no-edit -m "$(git log -1 --pretty=%B) [skip ci]"
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Update the Cargo.lock
run: |
cargo update
git add Cargo.lock
git commit -m "chore: Bump the version in Cargo.lock"
- name: Get the new version tag - name: Get the new version tag
id: version id: version
run: | run: |
NEW_TAG=$(cz version --project) NEW_TAG=$(cz version --project)
echo "New version: $NEW_TAG" echo "New version: $NEW_TAG"
echo "version=$NEW_TAG" >> $GITHUB_ENV echo "version=$NEW_TAG" >> $GITHUB_ENV
echo "$NEW_TAG" > artifacts/release-version
- name: Get the previous version tag - name: Get the previous version tag
id: prev_version id: prev_version
@@ -84,8 +232,33 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ env.version }} files: |
name: "Release ${{ env.version }}" 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.version }}
name: "v${{ env.version }}"
body: ${{ env.changelog_body }} body: ${{ env.changelog_body }}
draft: false draft: false
prerelease: false prerelease: false
@@ -96,9 +269,49 @@ jobs:
run: | run: |
git push origin --follow-tags git push origin --follow-tags
release-crate: - name: Upload artifacts
needs: bump uses: actions/upload-artifact@v3
name: Release Crate with:
name: artifacts
path: artifacts
publish-docker-image:
needs: [build-release-artifacts]
name: Publishing Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Get release artifacts
uses: actions/download-artifact@v3
with:
name: artifacts
path: artifacts
- name: Validate release environment variables
run: |
echo "Release version: ${{ env.version }}"
- 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:
tags: darkalex17/managarr:latest, darkalex17/managarr:x86_64, darkalex17/managarr:x86_64-${{ env.version }}
push: true
- name: Push to Docker Hub
uses: docker/build-push-action@v5
with:
file: Dockerfile.arm64
tags: darkalex17/managarr:arm64, darkalex17/managarr:arm64-${{ env.version }}
push: true
publish-crate:
needs: publish-github-release
name: Publish Crate
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check if actor is repository owner - name: Check if actor is repository owner
@@ -111,8 +324,26 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 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 - name: Install Rust stable
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- uses: katyo/publish-crates@v2 - uses: katyo/publish-crates@v2
with: with:
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
+23
View File
@@ -34,16 +34,20 @@ jobs:
toolchain: [stable, beta] toolchain: [stable, beta]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install ${{ matrix.toolchain }} - name: Install ${{ matrix.toolchain }}
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.toolchain }}
# enable this ci template to run regardless of whether the lockfile is checked in or not # enable this ci template to run regardless of whether the lockfile is checked in or not
- name: cargo generate-lockfile - name: cargo generate-lockfile
if: hashFiles('Cargo.lock') == '' if: hashFiles('Cargo.lock') == ''
run: cargo generate-lockfile run: cargo generate-lockfile
- name: cargo test --locked - name: cargo test --locked
run: cargo test --locked --all-features --all-targets run: cargo test --locked --all-features --all-targets
minimal-versions: minimal-versions:
# This action chooses the oldest version of the dependencies permitted by Cargo.toml to ensure # 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 # 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 name: ubuntu / stable / minimal-versions
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Rust stable - name: Install Rust stable
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- name: Install nightly for -Zdirect-minimal-versions - name: Install nightly for -Zdirect-minimal-versions
uses: dtolnay/rust-toolchain@nightly uses: dtolnay/rust-toolchain@nightly
- name: rustup default stable - name: rustup default stable
run: rustup default stable run: rustup default stable
- name: cargo update -Zdirect-minimal-versions - name: cargo update -Zdirect-minimal-versions
run: cargo +nightly update -Zdirect-minimal-versions run: cargo +nightly update -Zdirect-minimal-versions
- name: cargo test - name: cargo test
run: cargo test --locked --all-features --all-targets run: cargo test --locked --all-features --all-targets
- name: Cache Cargo dependencies - name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
os-check: os-check:
# run cargo test on mac and windows # run cargo test on mac and windows
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -100,15 +111,20 @@ jobs:
# if: runner.os == 'Windows' # if: runner.os == 'Windows'
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Rust stable - name: Install Rust stable
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- name: cargo generate-lockfile - name: cargo generate-lockfile
if: hashFiles('Cargo.lock') == '' if: hashFiles('Cargo.lock') == ''
run: cargo generate-lockfile run: cargo generate-lockfile
- name: cargo test - name: cargo test
run: cargo test --locked --all-features --all-targets run: cargo test --locked --all-features --all-targets
- name: Cache Cargo dependencies - name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
coverage: coverage:
# use llvm-cov to build and collect coverage and outputs in a format that # use llvm-cov to build and collect coverage and outputs in a format that
# is compatible with codecov.io # is compatible with codecov.io
@@ -136,21 +152,28 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Rust stable - name: Install Rust stable
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
components: llvm-tools-preview components: llvm-tools-preview
- name: cargo install cargo-llvm-cov - name: cargo install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov
- name: cargo generate-lockfile - name: cargo generate-lockfile
if: hashFiles('Cargo.lock') == '' if: hashFiles('Cargo.lock') == ''
run: cargo generate-lockfile run: cargo generate-lockfile
- name: cargo llvm-cov - name: cargo llvm-cov
run: cargo llvm-cov --locked --all-features --lcov --output-path lcov.info run: cargo llvm-cov --locked --all-features --lcov --output-path lcov.info
- name: Record Rust version - name: Record Rust version
run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV" run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV"
- name: Cache Cargo dependencies - name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
- name: Upload to codecov.io - name: Upload to codecov.io
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v4
with: with:
+29
View File
@@ -5,6 +5,35 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 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
- **ci**: Properly prefix version tags with 'v' [skip ci]
- **ci**: Bump the version in the Cargo.lock file and commit it as well when releasing [skip ci]
## v0.3.2 (2024-11-26)
### Fix
- **ci**: Updated the Cargo.lock file [skip ci]
- **ci**: Use a different GitHub action to release the crate to Crates.io [skip ci]
- **ci**: Don't manually push the tags and let Commitizen do it [skip ci]
## v0.3.1 (2024-11-26) ## v0.3.1 (2024-11-26)
### Fix ### Fix
Generated
+168 -96
View File
@@ -28,9 +28,9 @@ dependencies = [
[[package]] [[package]]
name = "allocator-api2" name = "allocator-api2"
version = "0.2.20" version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]] [[package]]
name = "android-tzdata" name = "android-tzdata"
@@ -142,7 +142,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -195,9 +195,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.11.0" version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8"
dependencies = [ dependencies = [
"memchr", "memchr",
"regex-automata", "regex-automata",
@@ -218,9 +218,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.8.0" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]] [[package]]
name = "cargo-husky" name = "cargo-husky"
@@ -245,9 +245,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.1" version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
@@ -319,14 +319,14 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
@@ -407,7 +407,7 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crossterm_winapi", "crossterm_winapi",
"mio 1.0.2", "mio 1.0.3",
"parking_lot", "parking_lot",
"rustix", "rustix",
"signal-hook", "signal-hook",
@@ -444,6 +444,41 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "darling"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.90",
]
[[package]]
name = "darling_macro"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
"syn 2.0.90",
]
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.11" version = "0.3.11"
@@ -464,12 +499,30 @@ dependencies = [
"syn 1.0.109", "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]] [[package]]
name = "destructure_traitobject" name = "destructure_traitobject"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
[[package]]
name = "deunicode"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
[[package]] [[package]]
name = "diff" name = "diff"
version = "0.1.13" version = "0.1.13"
@@ -532,7 +585,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -576,19 +629,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.9" version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.2.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]] [[package]]
name = "fnv" name = "fnv"
@@ -688,7 +741,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -762,7 +815,7 @@ dependencies = [
"futures-sink", "futures-sink",
"futures-util", "futures-util",
"http 0.2.12", "http 0.2.12",
"indexmap 2.6.0", "indexmap 2.7.0",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
@@ -780,8 +833,8 @@ dependencies = [
"fnv", "fnv",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http 1.1.0", "http 1.2.0",
"indexmap 2.6.0", "indexmap 2.7.0",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
@@ -830,9 +883,9 @@ dependencies = [
[[package]] [[package]]
name = "http" name = "http"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@@ -857,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [ dependencies = [
"bytes", "bytes",
"http 1.1.0", "http 1.2.0",
] ]
[[package]] [[package]]
@@ -868,7 +921,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-util", "futures-util",
"http 1.1.0", "http 1.2.0",
"http-body 1.0.1", "http-body 1.0.1",
"pin-project-lite", "pin-project-lite",
] ]
@@ -941,7 +994,7 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"h2 0.4.7", "h2 0.4.7",
"http 1.1.0", "http 1.2.0",
"http-body 1.0.1", "http-body 1.0.1",
"httparse", "httparse",
"itoa", "itoa",
@@ -958,7 +1011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"http 1.1.0", "http 1.2.0",
"hyper 1.5.1", "hyper 1.5.1",
"hyper-util", "hyper-util",
"rustls", "rustls",
@@ -993,7 +1046,7 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"http 1.1.0", "http 1.2.0",
"http-body 1.0.1", "http-body 1.0.1",
"hyper 1.5.1", "hyper 1.5.1",
"pin-project-lite", "pin-project-lite",
@@ -1141,9 +1194,15 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "1.0.3" version = "1.0.3"
@@ -1177,9 +1236,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.6.0" version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.2", "hashbrown 0.15.2",
@@ -1211,7 +1270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -1243,10 +1302,11 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.72" version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
dependencies = [ dependencies = [
"once_cell",
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -1258,9 +1318,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.165" version = "0.2.168"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb4d3d38eab6c5239a362fa8bae48c03baf980a6e7079f063942d563ef3533e" checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
[[package]] [[package]]
name = "libredox" name = "libredox"
@@ -1353,7 +1413,7 @@ dependencies = [
[[package]] [[package]]
name = "managarr" name = "managarr"
version = "0.3.1" version = "0.3.7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assert_cmd", "assert_cmd",
@@ -1369,6 +1429,8 @@ dependencies = [
"crossterm", "crossterm",
"ctrlc", "ctrlc",
"derivative", "derivative",
"derive_setters",
"deunicode",
"dirs-next", "dirs-next",
"human-panic", "human-panic",
"indicatif", "indicatif",
@@ -1379,6 +1441,8 @@ dependencies = [
"managarr-tree-widget", "managarr-tree-widget",
"mockall", "mockall",
"mockito", "mockito",
"openssl",
"paste",
"pretty_assertions", "pretty_assertions",
"ratatui", "ratatui",
"regex", "regex",
@@ -1438,11 +1502,10 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.0.2" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [ dependencies = [
"hermit-abi",
"libc", "libc",
"log", "log",
"wasi", "wasi",
@@ -1472,7 +1535,7 @@ dependencies = [
"cfg-if", "cfg-if",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -1602,7 +1665,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -1611,6 +1674,15 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "300.4.0+3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.104" version = "0.9.104"
@@ -1619,6 +1691,7 @@ checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
"openssl-src",
"pkg-config", "pkg-config",
"vcpkg", "vcpkg",
] ]
@@ -1640,9 +1713,9 @@ dependencies = [
[[package]] [[package]]
name = "os_info" name = "os_info"
version = "3.8.2" version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" checksum = "e5ca711d8b83edbb00b44d504503cd247c9c0bd8b0fa2694f2a1a3d8165379ce"
dependencies = [ dependencies = [
"log", "log",
"serde", "serde",
@@ -1852,9 +1925,9 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.7" version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
@@ -1917,7 +1990,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2 0.4.7", "h2 0.4.7",
"http 1.1.0", "http 1.2.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.5.1", "hyper 1.5.1",
@@ -1989,7 +2062,7 @@ dependencies = [
"regex", "regex",
"relative-path", "relative-path",
"rustc_version", "rustc_version",
"syn 2.0.89", "syn 2.0.90",
"unicode-ident", "unicode-ident",
] ]
@@ -2010,22 +2083,22 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.41" version = "0.38.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.18" version = "0.23.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"rustls-pki-types", "rustls-pki-types",
@@ -2112,9 +2185,9 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.23" version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
[[package]] [[package]]
name = "serde" name = "serde"
@@ -2143,7 +2216,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2226,7 +2299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [ dependencies = [
"libc", "libc",
"mio 1.0.2", "mio 1.0.3",
"signal-hook", "signal-hook",
] ]
@@ -2262,9 +2335,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.7" version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys 0.52.0",
@@ -2313,7 +2386,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2335,9 +2408,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.89" version = "2.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2361,7 +2434,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2421,7 +2494,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2436,9 +2509,9 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.36" version = "0.3.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
dependencies = [ dependencies = [
"deranged", "deranged",
"libc", "libc",
@@ -2492,7 +2565,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -2507,12 +2580,11 @@ dependencies = [
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.0" version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
dependencies = [ dependencies = [
"rustls", "rustls",
"rustls-pki-types",
"tokio", "tokio",
] ]
@@ -2557,7 +2629,7 @@ version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [ dependencies = [
"indexmap 2.6.0", "indexmap 2.7.0",
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
@@ -2572,9 +2644,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.40" version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [ dependencies = [
"pin-project-lite", "pin-project-lite",
"tracing-core", "tracing-core",
@@ -2736,9 +2808,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.95" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
@@ -2747,36 +2819,36 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.95" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
"once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.45" version = "0.4.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
"once_cell",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
] ]
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.95" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -2784,28 +2856,28 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.95" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.95" version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.72" version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@@ -3086,7 +3158,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
"synstructure", "synstructure",
] ]
@@ -3108,7 +3180,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
[[package]] [[package]]
@@ -3128,7 +3200,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
"synstructure", "synstructure",
] ]
@@ -3157,5 +3229,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.89", "syn 2.0.90",
] ]
+9 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "managarr" name = "managarr"
version = "0.3.1" version = "0.3.7"
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"] authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
description = "A TUI and CLI to manage your Servarrs" description = "A TUI and CLI to manage your Servarrs"
keywords = ["managarr", "ratatui", "dashboard", "servarr", "tui"] keywords = ["managarr", "ratatui", "dashboard", "servarr", "tui"]
@@ -36,7 +36,10 @@ strum = { version = "0.26.3", features = ["derive"] }
strum_macros = "0.26.4" strum_macros = "0.26.4"
tokio = { version = "1.36.0", features = ["full"] } tokio = { version = "1.36.0", features = ["full"] }
tokio-util = "0.7.8" 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" urlencoding = "2.1.2"
clap = { version = "4.5.20", features = ["derive", "cargo", "env"] } clap = { version = "4.5.20", features = ["derive", "cargo", "env"] }
clap_complete = "4.5.33" clap_complete = "4.5.33"
@@ -47,6 +50,10 @@ async-trait = "0.1.83"
dirs-next = "2.0.0" dirs-next = "2.0.0"
managarr-tree-widget = "0.24.0" managarr-tree-widget = "0.24.0"
indicatif = "0.17.9" 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] [dev-dependencies]
assert_cmd = "2.0.16" assert_cmd = "2.0.16"
+3 -3
View File
@@ -1,4 +1,4 @@
FROM clux/muslrust:stable AS builder FROM messense/rust-musl-cross:x86_64-musl AS builder
WORKDIR /usr/src WORKDIR /usr/src
# Download and compile Rust dependencies in an empty project and cache as a separate Docker layer # Download and compile Rust dependencies in an empty project and cache as a separate Docker layer
@@ -6,7 +6,7 @@ RUN USER=root cargo new --bin managarr-temp
WORKDIR /usr/src/managarr-temp WORKDIR /usr/src/managarr-temp
COPY Cargo.* . COPY Cargo.* .
RUN cargo build --release --target x86_64-unknown-linux-musl RUN cargo build --release
# remove src from empty project # remove src from empty project
RUN rm -r src RUN rm -r src
COPY src ./src COPY src ./src
@@ -15,7 +15,7 @@ RUN rm ./target/x86_64-unknown-linux-musl/release/deps/managarr*
RUN --mount=type=cache,target=/volume/target \ RUN --mount=type=cache,target=/volume/target \
--mount=type=cache,target=/root/.cargo/registry \ --mount=type=cache,target=/root/.cargo/registry \
cargo build --release --target x86_64-unknown-linux-musl --bin managarr cargo build --release --bin managarr
RUN mv target/x86_64-unknown-linux-musl/release/managarr . RUN mv target/x86_64-unknown-linux-musl/release/managarr .
FROM debian:stable-slim FROM debian:stable-slim
+27
View File
@@ -0,0 +1,27 @@
FROM messense/rust-musl-cross:armv7-musleabihf AS builder
WORKDIR /usr/src
# Download and compile Rust dependencies in an empty project and cache as a separate Docker layer
RUN USER=root cargo new --bin managarr-temp
RUN apt update && apt install -y libssl-dev pkg-config
WORKDIR /usr/src/managarr-temp
COPY Cargo.* .
RUN cargo build --release
# remove src from empty project
RUN rm -r src
COPY src ./src
# remove previous deps
RUN rm ./target/armv7-unknown-linux-musleabihf/release/deps/managarr*
RUN --mount=type=cache,target=/volume/target \
--mount=type=cache,target=/root/.cargo/registry \
cargo build --release --bin managarr
RUN mv target/armv7-unknown-linux-musleabihf/release/managarr .
FROM debian:stable-slim
# Copy the compiled binary from the builder container
COPY --from=builder --chown=nonroot:nonroot /usr/src/managarr-temp/managarr /usr/local/bin
ENTRYPOINT [ "/usr/local/bin/managarr" ]
+97 -54
View File
@@ -5,13 +5,16 @@
![License](https://img.shields.io/badge/license-MIT-blueviolet.svg) ![License](https://img.shields.io/badge/license-MIT-blueviolet.svg)
![LOC](https://tokei.rs/b1/github/Dark-Alex-17/managarr?category=code) ![LOC](https://tokei.rs/b1/github/Dark-Alex-17/managarr?category=code)
[![crates.io link](https://img.shields.io/crates/v/managarr.svg)](https://crates.io/crates/managarr) [![crates.io link](https://img.shields.io/crates/v/managarr.svg)](https://crates.io/crates/managarr)
![Docker Release](https://img.shields.io/docker/v/darkalex17/managarr?label=Docker%20version)
![Release](https://img.shields.io/github/v/release/Dark-Alex-17/managarr?color=%23c694ff) ![Release](https://img.shields.io/github/v/release/Dark-Alex-17/managarr?color=%23c694ff)
[![codecov](https://codecov.io/gh/Dark-Alex-17/managarr/graph/badge.svg?token=33G179TW67)](https://codecov.io/gh/Dark-Alex-17/managarr) [![codecov](https://codecov.io/gh/Dark-Alex-17/managarr/graph/badge.svg?token=33G179TW67)](https://codecov.io/gh/Dark-Alex-17/managarr)
![Crate.io downloads](https://img.shields.io/crates/d/managarr?label=Crate%20downloads) ![Crate.io downloads](https://img.shields.io/crates/d/managarr?label=Crate%20downloads)
[![GitHub Downloads](https://img.shields.io/github/downloads/Dark-Alex-17/managarr/total.svg?label=GitHub%20downloads)](https://github.com/Dark-Alex-17/managarr/releases)
![Docker pulls](https://img.shields.io/docker/pulls/darkalex17/managarr?label=Docker%20downloads)
Managarr is a TUI and CLI to help you manage your HTPC (Home Theater PC). Built with 🤎 in Rust! Managarr is a TUI and CLI to help you manage your HTPC (Home Theater PC). Built with 🤎 in Rust!
![library](screenshots/library.png) ![library](screenshots/sonarr/sonarr_library.png)
## What Servarrs are supported? ## What Servarrs are supported?
@@ -46,64 +49,94 @@ cargo install --locked managarr
### Docker ### Docker
Run Managarr as a docker container by mounting your `config.yml` file to `/root/.config/managarr/config.yml`. For example: Run Managarr as a docker container by mounting your `config.yml` file to `/root/.config/managarr/config.yml`. For example:
```shell ```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
```
For ARM64 users, you can use the `arm64` tag:
```shell
docker run --rm -it -v /home/aclarke/.config/managarr/config.yml:/root/.config/managarr/config.yml darkalex17/managarr:arm64
``` ```
You can also clone this repo and run `make docker` to build a docker image locally and run it using the above command. 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. 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.
### 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 ## Features
Key: Key:
| Symbol | Status | | Symbol | Status |
|--------------------|-----------| |--------|-----------|
| :white_check_mark: | Supported | | | Supported |
| :x: | Missing | | | Missing |
| :clock3: | Planned | | 🕒 | Planned |
| :no_entry_sign: | Won't Add | | 🚫 | Won't Add |
### Radarr ### Radarr
| TUI | CLI | Feature | | TUI | CLI | Feature |
|--------------------|--------------------|----------------------------------------------------------------------------------------------------------------| |-----|-----|----------------------------------------------------------------------------------------------------------------|
| :white_check_mark: | :white_check_mark: | View your library, downloads, collections, and blocklist | | ✅ | ✅ | View your library, downloads, collections, and blocklist |
| :white_check_mark: | :white_check_mark: | View details of a specific movie including description, history, downloaded file info, or the credits | | ✅ | ✅ | View details of a specific movie including description, history, downloaded file info, or the credits |
| :white_check_mark: | :white_check_mark: | View details of any collection and the movies in them | | ✅ | ✅ | View details of any collection and the movies in them |
| :no_entry_sign: | :white_check_mark: | View your host and security configs from the CLI to programmatically fetch the API token, among other settings | | 🚫 | ✅ | View your host and security configs from the CLI to programmatically fetch the API token, among other settings |
| :white_check_mark: | :white_check_mark: | Search your library or collections | | ✅ | ✅ | Search your library or collections |
| :white_check_mark: | :white_check_mark: | Add movies to your library | | ✅ | ✅ | Add movies to your library |
| :white_check_mark: | :white_check_mark: | Delete movies, downloads, and indexers | | ✅ | ✅ | Delete movies, downloads, and indexers |
| :white_check_mark: | :white_check_mark: | Trigger automatic searches for movies | | ✅ | ✅ | Trigger automatic searches for movies |
| :white_check_mark: | :white_check_mark: | Trigger refresh and disk scan for movies, downloads, and collections | | ✅ | ✅ | Trigger refresh and disk scan for movies, downloads, and collections |
| :white_check_mark: | :white_check_mark: | Manually search for movies | | ✅ | ✅ | Manually search for movies |
| :white_check_mark: | :white_check_mark: | Edit your movies, collections, and indexers | | ✅ | ✅ | Edit your movies, collections, and indexers |
| :white_check_mark: | :white_check_mark: | Manage your tags | | ✅ | ✅ | Manage your tags |
| :white_check_mark: | :white_check_mark: | Manage your root folders | | ✅ | ✅ | Manage your root folders |
| :white_check_mark: | :white_check_mark: | Manage your blocklist | | ✅ | ✅ | Manage your blocklist |
| :white_check_mark: | :white_check_mark: | View and browse logs, tasks, events queues, and updates | | ✅ | ✅ | View and browse logs, tasks, events queues, and updates |
| :white_check_mark: | :white_check_mark: | Manually trigger scheduled tasks | | ✅ | ✅ | Manually trigger scheduled tasks |
### Sonarr ### Sonarr
| TUI | CLI | Feature | | TUI | CLI | Feature |
|----------|--------------------|--------------------------------------------------------------------------------------------------------------------| |-----|-----|--------------------------------------------------------------------------------------------------------------------|
| :clock3: | :white_check_mark: | View your library, downloads, blocklist, episodes | | ✅ | ✅ | View your library, downloads, blocklist, episodes |
| :clock3: | :white_check_mark: | View details of a specific series, or episode including description, history, downloaded file info, or the credits | | ✅ | ✅ | View details of a specific series, or episode including description, history, downloaded file info, or the credits |
| :clock3: | :white_check_mark: | View your host and security configs from the CLI to programmatically fetch the API token, among other settings | | 🚫 | ✅ | View your host and security configs from the CLI to programmatically fetch the API token, among other settings |
| :clock3: | :white_check_mark: | Search your library | | ✅ | ✅ | Search your library |
| :clock3: | :white_check_mark: | Add series to your library | | ✅ | ✅ | Add series to your library |
| :clock3: | :white_check_mark: | Delete series, downloads, indexers, root folders, and episode files | | ✅ | ✅ | Delete series, downloads, indexers, root folders, and episode files |
| :clock3: | :white_check_mark: | Mark history events as failed | | ✅ | ✅ | Trigger automatic searches for series, seasons, or episodes |
| :clock3: | :white_check_mark: | Trigger automatic searches for series, seasons, or episodes | | ✅ | ✅ | Trigger refresh and disk scan for series and downloads |
| :clock3: | :white_check_mark: | Trigger refresh and disk scan for series and downloads | | ✅ | ✅ | Manually search for series, seasons, or episodes |
| :clock3: | :white_check_mark: | Manually search for series, seasons, or episodes | | ✅ | ✅ | Edit your series and indexers |
| :clock3: | :white_check_mark: | Edit your series and indexers | | ✅ | ✅ | Manage your tags |
| :clock3: | :white_check_mark: | Manage your tags | | ✅ | ✅ | Manage your root folders |
| :clock3: | :white_check_mark: | Manage your root folders | | ✅ | ✅ | Manage your blocklist |
| :clock3: | :white_check_mark: | Manage your blocklist | | ✅ | ✅ | View and browse logs, tasks, events queues, and updates |
| :clock3: | :white_check_mark: | View and browse logs, tasks, events queues, and updates | | ✅ | ✅ | Manually trigger scheduled tasks |
| :clock3: | :white_check_mark: | Manually trigger scheduled tasks |
### Readarr ### Readarr
@@ -141,7 +174,7 @@ To see all available commands, simply run `managarr --help`:
```shell ```shell
$ managarr --help $ managarr --help
managarr 0.3.0 managarr 0.4.0
Alex Clarke <alex.j.tusa@gmail.com> Alex Clarke <alex.j.tusa@gmail.com>
A TUI and CLI to manage your Servarrs A TUI and CLI to manage your Servarrs
@@ -186,6 +219,8 @@ Commands:
start-task Start the specified Sonarr task 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-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 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) help Print this message or the help of the given subcommand(s)
Options: Options:
@@ -275,7 +310,6 @@ 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_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` | | `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!) ## 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) Progress for the beta release can be followed on my [Wekan Board](https://wekan.alexjclarke.com/b/dHoGjBb44MHM9HSv4/managarr)
@@ -283,13 +317,22 @@ with all items tagged `Beta`.
## Screenshots ## Screenshots
![library](screenshots/library.png) ### Radarr
![manual_search](screenshots/manual_search.png) ![radarr_library](screenshots/radarr/radarr_library.png)
![logs](screenshots/logs.png) ![manual_search](screenshots/radarr/manual_search.png)
![new_movie_search](screenshots/new_movie_search.png) ![new_movie_search](screenshots/radarr/new_movie_search.png)
![add_new_movie](screenshots/add_new_movie.png) ![add_new_movie](screenshots/radarr/add_new_movie.png)
![collection_details](screenshots/collection_details.png) ![collection_details](screenshots/radarr/collection_details.png)
![indexers](screenshots/indexers.png)
### Sonarr
![sonarr_library](screenshots/sonarr/sonarr_library.png)
![series_details](screenshots/sonarr/series_details.png)
![season_details](screenshots/sonarr/season_details.png)
![manual_episode_search](screenshots/sonarr/manual_episode_search.png)
### General
![logs](screenshots/radarr/logs.png)
![indexers](screenshots/radarr/indexers.png)
## Dependencies ## Dependencies
* [ratatui](https://github.com/tui-rs-revival/ratatui) * [ratatui](https://github.com/tui-rs-revival/ratatui)
@@ -301,7 +344,7 @@ with all items tagged `Beta`.
## Servarr Requirements ## Servarr Requirements
* [Radarr >= 5.3.6.8612](https://radarr.video/docs/api/) * [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/) * [Readarr v1](https://readarr.com/docs/api/)
* [Lidarr v1](https://lidarr.audio/docs/api/) * [Lidarr v1](https://lidarr.audio/docs/api/)
* [Whisparr >= v3](https://whisparr.com/docs/api/) * [Whisparr >= v3](https://whisparr.com/docs/api/)

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

+33 -21
View File
@@ -19,6 +19,7 @@ mod tests {
assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]); assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]);
assert!(app.network_tx.is_none()); assert!(app.network_tx.is_none());
assert!(!app.cancellation_token.is_cancelled()); assert!(!app.cancellation_token.is_cancelled());
assert!(app.is_first_render);
assert_eq!(app.error, HorizontallyScrollableText::default()); assert_eq!(app.error, HorizontallyScrollableText::default());
assert_eq!(app.server_tabs.index, 0); assert_eq!(app.server_tabs.index, 0);
assert_eq!( assert_eq!(
@@ -55,14 +56,11 @@ mod tests {
fn test_navigation_stack_methods() { fn test_navigation_stack_methods() {
let mut app = App::default(); let mut app = App::default();
assert_eq!(app.get_current_route(), &DEFAULT_ROUTE); assert_eq!(app.get_current_route(), DEFAULT_ROUTE);
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
assert_eq!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert!(app.is_routing); assert!(app.is_routing);
app.is_routing = false; app.is_routing = false;
@@ -70,20 +68,20 @@ mod tests {
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::Collections.into() ActiveRadarrBlock::Collections.into()
); );
assert!(app.is_routing); assert!(app.is_routing);
app.is_routing = false; app.is_routing = false;
app.pop_navigation_stack(); app.pop_navigation_stack();
assert_eq!(app.get_current_route(), &DEFAULT_ROUTE); assert_eq!(app.get_current_route(), DEFAULT_ROUTE);
assert!(app.is_routing); assert!(app.is_routing);
app.is_routing = false; app.is_routing = false;
app.pop_navigation_stack(); app.pop_navigation_stack();
assert_eq!(app.get_current_route(), &DEFAULT_ROUTE); assert_eq!(app.get_current_route(), DEFAULT_ROUTE);
assert!(app.is_routing); assert!(app.is_routing);
} }
@@ -120,19 +118,23 @@ mod tests {
#[test] #[test]
fn test_reset() { 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 { let mut app = App {
tick_count: 2, tick_count: 2,
error: "Test error".to_owned().into(), error: "Test error".to_owned().into(),
data: Data { is_first_render: false,
radarr_data: RadarrData { data,
version: "test".to_owned(),
..RadarrData::default()
},
sonarr_data: SonarrData {
version: "test".to_owned(),
..SonarrData::default()
},
},
..App::default() ..App::default()
}; };
@@ -140,6 +142,7 @@ mod tests {
assert_eq!(app.tick_count, 0); assert_eq!(app.tick_count, 0);
assert_eq!(app.error, HorizontallyScrollableText::default()); assert_eq!(app.error, HorizontallyScrollableText::default());
assert!(app.is_first_render);
assert!(app.data.radarr_data.version.is_empty()); assert!(app.data.radarr_data.version.is_empty());
assert!(app.data.sonarr_data.version.is_empty()); assert!(app.data.sonarr_data.version.is_empty());
} }
@@ -188,12 +191,13 @@ mod tests {
let mut app = App { let mut app = App {
tick_until_poll: 2, tick_until_poll: 2,
network_tx: Some(sync_network_tx), network_tx: Some(sync_network_tx),
is_first_render: true,
..App::default() ..App::default()
}; };
assert_eq!(app.tick_count, 0); assert_eq!(app.tick_count, 0);
app.on_tick(true).await; app.on_tick().await;
assert_eq!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
@@ -219,6 +223,14 @@ mod tests {
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetStatus.into() 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!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetMovies.into() RadarrEvent::GetMovies.into()
@@ -237,7 +249,7 @@ mod tests {
..App::default() ..App::default()
}; };
app.on_tick(false).await; app.on_tick().await;
assert!(!app.is_routing); assert!(!app.is_routing);
} }
@@ -250,7 +262,7 @@ mod tests {
..App::default() ..App::default()
}; };
app.on_tick(false).await; app.on_tick().await;
assert!(!app.should_refresh); assert!(!app.should_refresh);
} }
+69 -2
View File
@@ -14,10 +14,77 @@ pub fn build_context_clue_string(context_clues: &[(KeyBinding, &str)]) -> String
.join(" | ") .join(" | ")
} }
pub static SERVARR_CONTEXT_CLUES: [ContextClue; 2] = [ pub static SERVARR_CONTEXT_CLUES: [ContextClue; 3] = [
(DEFAULT_KEYBINDINGS.tab, "change servarr"), (
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), (DEFAULT_KEYBINDINGS.quit, DEFAULT_KEYBINDINGS.quit.desc),
]; ];
pub static BARE_POPUP_CONTEXT_CLUES: [ContextClue; 1] = pub static BARE_POPUP_CONTEXT_CLUES: [ContextClue; 1] =
[(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc)]; [(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,
),
];
+168 -3
View File
@@ -2,7 +2,11 @@
mod test { mod test {
use pretty_assertions::{assert_eq, assert_str_eq}; 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}; use crate::app::{context_clues::build_context_clue_string, key_binding::DEFAULT_KEYBINDINGS};
#[test] #[test]
@@ -24,8 +28,13 @@ mod test {
let (key_binding, description) = servarr_context_clues_iter.next().unwrap(); let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.tab); assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.next_servarr);
assert_str_eq!(*description, "change 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(); 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_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
assert_eq!(bare_popup_context_clues_iter.next(), None); 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);
}
} }
+21 -6
View File
@@ -15,8 +15,11 @@ generate_keybindings! {
left, left,
right, right,
backspace, backspace,
next_servarr,
previous_servarr,
clear, clear,
search, search,
auto_search,
settings, settings,
filter, filter,
sort, sort,
@@ -25,12 +28,12 @@ generate_keybindings! {
tasks, tasks,
test, test,
test_all, test_all,
toggle_monitoring,
refresh, refresh,
update, update,
events, events,
home, home,
end, end,
tab,
delete, delete,
submit, submit,
confirm, confirm,
@@ -69,16 +72,28 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
key: Key::Backspace, key: Key::Backspace,
desc: "backspace", desc: "backspace",
}, },
next_servarr: KeyBinding {
key: Key::Tab,
desc: "next servarr",
},
previous_servarr: KeyBinding {
key: Key::BackTab,
desc: "previous servarr",
},
clear: KeyBinding { clear: KeyBinding {
key: Key::Char('c'), key: Key::Char('c'),
desc: "clear", desc: "clear",
}, },
auto_search: KeyBinding {
key: Key::Char('S'),
desc: "auto search",
},
search: KeyBinding { search: KeyBinding {
key: Key::Char('s'), key: Key::Char('s'),
desc: "search", desc: "search",
}, },
settings: KeyBinding { settings: KeyBinding {
key: Key::Char('s'), key: Key::Char('S'),
desc: "settings", desc: "settings",
}, },
filter: KeyBinding { filter: KeyBinding {
@@ -113,6 +128,10 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
key: Key::Char('T'), key: Key::Char('T'),
desc: "test all", desc: "test all",
}, },
toggle_monitoring: KeyBinding {
key: Key::Char('m'),
desc: "toggle monitoring",
},
refresh: KeyBinding { refresh: KeyBinding {
key: Key::Ctrl('r'), key: Key::Ctrl('r'),
desc: "refresh", desc: "refresh",
@@ -129,10 +148,6 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
key: Key::End, key: Key::End,
desc: "end", desc: "end",
}, },
tab: KeyBinding {
key: Key::Tab,
desc: "tab",
},
delete: KeyBinding { delete: KeyBinding {
key: Key::Delete, key: Key::Delete,
desc: "delete", desc: "delete",
+5 -2
View File
@@ -13,9 +13,12 @@ mod test {
#[case(DEFAULT_KEYBINDINGS.left, Key::Left, "left")] #[case(DEFAULT_KEYBINDINGS.left, Key::Left, "left")]
#[case(DEFAULT_KEYBINDINGS.right, Key::Right, "right")] #[case(DEFAULT_KEYBINDINGS.right, Key::Right, "right")]
#[case(DEFAULT_KEYBINDINGS.backspace, Key::Backspace, "backspace")] #[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.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.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.filter, Key::Char('f'), "filter")]
#[case(DEFAULT_KEYBINDINGS.sort, Key::Char('o'), "sort")] #[case(DEFAULT_KEYBINDINGS.sort, Key::Char('o'), "sort")]
#[case(DEFAULT_KEYBINDINGS.edit, Key::Char('e'), "edit")] #[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.tasks, Key::Char('t'), "tasks")]
#[case(DEFAULT_KEYBINDINGS.test, Key::Char('t'), "test")] #[case(DEFAULT_KEYBINDINGS.test, Key::Char('t'), "test")]
#[case(DEFAULT_KEYBINDINGS.test_all, Key::Char('T'), "test all")] #[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.refresh, Key::Ctrl('r'), "refresh")]
#[case(DEFAULT_KEYBINDINGS.update, Key::Char('u'), "update")] #[case(DEFAULT_KEYBINDINGS.update, Key::Char('u'), "update")]
#[case(DEFAULT_KEYBINDINGS.home, Key::Home, "home")] #[case(DEFAULT_KEYBINDINGS.home, Key::Home, "home")]
#[case(DEFAULT_KEYBINDINGS.end, Key::End, "end")] #[case(DEFAULT_KEYBINDINGS.end, Key::End, "end")]
#[case(DEFAULT_KEYBINDINGS.tab, Key::Tab, "tab")]
#[case(DEFAULT_KEYBINDINGS.delete, Key::Delete, "delete")] #[case(DEFAULT_KEYBINDINGS.delete, Key::Delete, "delete")]
#[case(DEFAULT_KEYBINDINGS.submit, Key::Enter, "submit")] #[case(DEFAULT_KEYBINDINGS.submit, Key::Enter, "submit")]
#[case(DEFAULT_KEYBINDINGS.confirm, Key::Ctrl('s'), "submit")] #[case(DEFAULT_KEYBINDINGS.confirm, Key::Ctrl('s'), "submit")]
+14 -11
View File
@@ -1,6 +1,6 @@
use std::process; use std::process;
use anyhow::anyhow; use anyhow::{anyhow, Error};
use colored::Colorize; use colored::Colorize;
use log::{debug, error}; use log::{debug, error};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -21,6 +21,7 @@ pub mod context_clues;
pub mod key_binding; pub mod key_binding;
mod key_binding_tests; mod key_binding_tests;
pub mod radarr; pub mod radarr;
pub mod sonarr;
const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies, None); const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies, None);
@@ -28,6 +29,7 @@ pub struct App<'a> {
navigation_stack: Vec<Route>, navigation_stack: Vec<Route>,
network_tx: Option<Sender<NetworkEvent>>, network_tx: Option<Sender<NetworkEvent>>,
cancellation_token: CancellationToken, cancellation_token: CancellationToken,
pub is_first_render: bool,
pub server_tabs: TabState, pub server_tabs: TabState,
pub error: HorizontallyScrollableText, pub error: HorizontallyScrollableText,
pub tick_until_poll: u64, pub tick_until_poll: u64,
@@ -76,26 +78,26 @@ impl<'a> App<'a> {
self.tick_count = 0; 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)] #[allow(dead_code)]
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.reset_tick_count(); self.reset_tick_count();
self.error = HorizontallyScrollableText::default(); self.error = HorizontallyScrollableText::default();
self.is_first_render = true;
self.data = Data::default(); 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() { if self.error.text.is_empty() {
self.error = error.to_string().into(); 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 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() { match self.get_current_route() {
self Route::Radarr(active_radarr_block, _) => self.radarr_on_tick(active_radarr_block).await,
.radarr_on_tick(*active_radarr_block, is_first_render) Route::Sonarr(active_sonarr_block, _) => self.sonarr_on_tick(active_sonarr_block).await,
.await; _ => (),
} }
self.is_routing = false; self.is_routing = false;
@@ -130,8 +132,8 @@ impl<'a> App<'a> {
self.push_navigation_stack(route); self.push_navigation_stack(route);
} }
pub fn get_current_route(&self) -> &Route { pub fn get_current_route(&self) -> Route {
self.navigation_stack.last().unwrap_or(&DEFAULT_ROUTE) *self.navigation_stack.last().unwrap_or(&DEFAULT_ROUTE)
} }
} }
@@ -142,6 +144,7 @@ impl<'a> Default for App<'a> {
network_tx: None, network_tx: None,
cancellation_token: CancellationToken::new(), cancellation_token: CancellationToken::new(),
error: HorizontallyScrollableText::default(), error: HorizontallyScrollableText::default(),
is_first_render: true,
server_tabs: TabState::new(vec![ server_tabs: TabState::new(vec![
TabRoute { TabRoute {
title: "Radarr", title: "Radarr",
@@ -176,7 +179,7 @@ impl<'a> Default for App<'a> {
#[derive(Default)] #[derive(Default)]
pub struct Data<'a> { pub struct Data<'a> {
pub radarr_data: RadarrData<'a>, pub radarr_data: RadarrData<'a>,
pub sonarr_data: SonarrData, pub sonarr_data: SonarrData<'a>,
} }
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone)]
+31 -13
View File
@@ -17,11 +17,23 @@ impl<'a> App<'a> {
.await; .await;
} }
ActiveRadarrBlock::Collections => { ActiveRadarrBlock::Collections => {
self
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
.await;
self self
.dispatch_network_event(RadarrEvent::GetCollections.into()) .dispatch_network_event(RadarrEvent::GetCollections.into())
.await; .await;
self
.dispatch_network_event(RadarrEvent::GetMovies.into())
.await;
} }
ActiveRadarrBlock::CollectionDetails => { ActiveRadarrBlock::CollectionDetails => {
self
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
.await;
self
.dispatch_network_event(RadarrEvent::GetTags.into())
.await;
self.is_loading = true; self.is_loading = true;
self.populate_movie_collection_table().await; self.populate_movie_collection_table().await;
self.is_loading = false; self.is_loading = false;
@@ -37,6 +49,12 @@ impl<'a> App<'a> {
.await; .await;
} }
ActiveRadarrBlock::Movies => { ActiveRadarrBlock::Movies => {
self
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
.await;
self
.dispatch_network_event(RadarrEvent::GetTags.into())
.await;
self self
.dispatch_network_event(RadarrEvent::GetMovies.into()) .dispatch_network_event(RadarrEvent::GetMovies.into())
.await; .await;
@@ -45,6 +63,9 @@ impl<'a> App<'a> {
.await; .await;
} }
ActiveRadarrBlock::Indexers => { ActiveRadarrBlock::Indexers => {
self
.dispatch_network_event(RadarrEvent::GetTags.into())
.await;
self self
.dispatch_network_event(RadarrEvent::GetIndexers.into()) .dispatch_network_event(RadarrEvent::GetIndexers.into())
.await; .await;
@@ -119,11 +140,11 @@ impl<'a> App<'a> {
_ => (), _ => (),
} }
self.check_for_prompt_action().await; self.check_for_radarr_prompt_action().await;
self.reset_tick_count(); 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 { if self.data.radarr_data.prompt_confirm {
self.data.radarr_data.prompt_confirm = false; self.data.radarr_data.prompt_confirm = false;
if let Some(radarr_event) = &self.data.radarr_data.prompt_confirm_action { if let Some(radarr_event) = &self.data.radarr_data.prompt_confirm_action {
@@ -136,19 +157,17 @@ impl<'a> App<'a> {
} }
} }
pub(super) async fn radarr_on_tick( pub(super) async fn radarr_on_tick(&mut self, active_radarr_block: ActiveRadarrBlock) {
&mut self, if self.is_first_render {
active_radarr_block: ActiveRadarrBlock, self.refresh_radarr_metadata().await;
is_first_render: bool,
) {
if is_first_render {
self.refresh_metadata().await;
self.dispatch_by_radarr_block(&active_radarr_block).await; self.dispatch_by_radarr_block(&active_radarr_block).await;
self.is_first_render = false;
return;
} }
if self.should_refresh { if self.should_refresh {
self.dispatch_by_radarr_block(&active_radarr_block).await; self.dispatch_by_radarr_block(&active_radarr_block).await;
self.refresh_metadata().await; self.refresh_radarr_metadata().await;
} }
if self.is_routing { if self.is_routing {
@@ -156,16 +175,15 @@ impl<'a> App<'a> {
self.cancellation_token.cancel(); self.cancellation_token.cancel();
} else { } else {
self.dispatch_by_radarr_block(&active_radarr_block).await; self.dispatch_by_radarr_block(&active_radarr_block).await;
self.refresh_metadata().await;
} }
} }
if self.tick_count % self.tick_until_poll == 0 { 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 self
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into()) .dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
.await; .await;
+10 -62
View File
@@ -35,60 +35,6 @@ pub static COLLECTIONS_CONTEXT_CLUES: [ContextClue; 8] = [
(DEFAULT_KEYBINDINGS.esc, "cancel filter"), (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] = [ pub static MOVIE_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
( (
DEFAULT_KEYBINDINGS.refresh, DEFAULT_KEYBINDINGS.refresh,
@@ -96,7 +42,10 @@ pub static MOVIE_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
), ),
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc), (DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.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), (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.update, DEFAULT_KEYBINDINGS.update.desc),
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc), (DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.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), (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"), (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] = [ pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
(DEFAULT_KEYBINDINGS.submit, "start task"), (DEFAULT_KEYBINDINGS.submit, "start task"),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc), (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.submit, "show overview/add movie"),
(DEFAULT_KEYBINDINGS.edit, "edit collection"),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc), (DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
]; ];
+13 -160
View File
@@ -4,11 +4,10 @@ mod tests {
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::radarr_context_clues::{ use crate::app::radarr::radarr_context_clues::{
ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES, ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES,
COLLECTION_DETAILS_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, COLLECTION_DETAILS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES,
INDEXERS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
MANUAL_MOVIE_SEARCH_CONTEXT_CLUES, MOVIE_DETAILS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, MOVIE_DETAILS_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
SYSTEM_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
}; };
#[test] #[test]
@@ -113,141 +112,6 @@ mod tests {
assert_eq!(collections_context_clues.next(), None); 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] #[test]
fn test_movie_details_context_clues() { fn test_movie_details_context_clues() {
let mut movie_details_context_clues_iter = MOVIE_DETAILS_CONTEXT_CLUES.iter(); 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(); let (key_binding, description) = movie_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search); assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, "auto search"); assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = movie_details_context_clues_iter.next().unwrap(); 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(); let (key_binding, description) = manual_movie_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search); assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, "auto search"); assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = manual_movie_search_context_clues_iter.next().unwrap(); 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); 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] #[test]
fn test_system_tasks_context_clues() { fn test_system_tasks_context_clues() {
let mut system_tasks_context_clues_iter = SYSTEM_TASKS_CONTEXT_CLUES.iter(); 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(); 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_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc); assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
assert_eq!(collection_details_context_clues_iter.next(), None); assert_eq!(collection_details_context_clues_iter.next(), None);
+51 -38
View File
@@ -38,17 +38,25 @@ mod tests {
.await; .await;
assert!(app.is_loading); assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetQualityProfiles.into()
);
assert_eq!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetCollections.into() RadarrEvent::GetCollections.into()
); );
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetMovies.into()
);
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0); assert_eq!(app.tick_count, 0);
} }
#[tokio::test] #[tokio::test]
async fn test_dispatch_by_collection_details_block() { 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 { app.data.radarr_data.collections.set_items(vec![Collection {
movies: Some(vec![CollectionMovie::default()]), movies: Some(vec![CollectionMovie::default()]),
@@ -60,6 +68,14 @@ mod tests {
.await; .await;
assert!(!app.is_loading); 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!(!app.data.radarr_data.collection_movies.items.is_empty());
assert_eq!(app.tick_count, 0); assert_eq!(app.tick_count, 0);
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
@@ -80,6 +96,14 @@ mod tests {
.await; .await;
assert!(app.is_loading); 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!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
RadarrEvent::AddMovie(None).into() RadarrEvent::AddMovie(None).into()
@@ -132,6 +156,14 @@ mod tests {
.await; .await;
assert!(app.is_loading); 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!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetMovies.into() RadarrEvent::GetMovies.into()
@@ -153,6 +185,10 @@ mod tests {
.await; .await;
assert!(app.is_loading); assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetTags.into()
);
assert_eq!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetIndexers.into() RadarrEvent::GetIndexers.into()
@@ -459,22 +495,22 @@ mod tests {
} }
#[tokio::test] #[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(); let mut app = App::default();
app.data.radarr_data.prompt_confirm = false; 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.data.radarr_data.prompt_confirm);
assert!(!app.should_refresh); assert!(!app.should_refresh);
} }
#[tokio::test] #[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(); let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::GetStatus); 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!(!app.data.radarr_data.prompt_confirm);
assert_eq!( assert_eq!(
@@ -490,7 +526,7 @@ mod tests {
let (mut app, mut sync_network_rx) = construct_app_unit(); let (mut app, mut sync_network_rx) = construct_app_unit();
app.is_routing = true; app.is_routing = true;
app.refresh_metadata().await; app.refresh_radarr_metadata().await;
assert_eq!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
@@ -522,8 +558,9 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_radarr_on_tick_first_render() { async fn test_radarr_on_tick_first_render() {
let (mut app, mut sync_network_rx) = construct_app_unit(); 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!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
@@ -551,6 +588,7 @@ mod tests {
); );
assert!(app.is_loading); assert!(app.is_loading);
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
assert!(!app.is_first_render);
} }
#[tokio::test] #[tokio::test]
@@ -559,26 +597,8 @@ mod tests {
app.is_routing = true; app.is_routing = true;
app.should_refresh = true; app.should_refresh = true;
app app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.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!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetDownloads.into() RadarrEvent::GetDownloads.into()
@@ -592,9 +612,7 @@ mod tests {
app.is_routing = true; app.is_routing = true;
app.should_refresh = false; app.should_refresh = false;
app app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
assert!(app.cancellation_token.is_cancelled()); assert!(app.cancellation_token.is_cancelled());
} }
@@ -604,9 +622,7 @@ mod tests {
let (mut app, mut sync_network_rx) = construct_app_unit(); let (mut app, mut sync_network_rx) = construct_app_unit();
app.should_refresh = true; app.should_refresh = true;
app app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
assert_eq!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
@@ -623,9 +639,7 @@ mod tests {
app.is_routing = true; app.is_routing = true;
app.should_refresh = true; app.should_refresh = true;
app app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
assert_eq!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
@@ -643,9 +657,7 @@ mod tests {
app.tick_count = 2; app.tick_count = 2;
app.tick_until_poll = 2; app.tick_until_poll = 2;
app app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
assert_eq!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
@@ -701,6 +713,7 @@ mod tests {
let mut app = App { let mut app = App {
network_tx: Some(sync_network_tx), network_tx: Some(sync_network_tx),
tick_count: 1, tick_count: 1,
is_first_render: false,
..App::default() ..App::default()
}; };
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
+245
View File
@@ -0,0 +1,245 @@
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(None).into())
.await;
}
ActiveSonarrBlock::SeasonDetails => {
self
.dispatch_network_event(SonarrEvent::GetEpisodes(None).into())
.await;
self
.dispatch_network_event(SonarrEvent::GetEpisodeFiles(None).into())
.await;
self
.dispatch_network_event(SonarrEvent::GetDownloads.into())
.await;
}
ActiveSonarrBlock::SeasonHistory => {
self
.dispatch_network_event(SonarrEvent::GetSeasonHistory(None).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(None).into())
.await;
}
_ => (),
}
}
ActiveSonarrBlock::EpisodeDetails | ActiveSonarrBlock::EpisodeFile => {
self
.dispatch_network_event(SonarrEvent::GetEpisodeDetails(None).into())
.await;
}
ActiveSonarrBlock::EpisodeHistory => {
self
.dispatch_network_event(SonarrEvent::GetEpisodeHistory(None).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(None).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(None).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(None).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(None).into())
.await;
}
ActiveSonarrBlock::AddSeriesSearchResults => {
self
.dispatch_network_event(SonarrEvent::SearchNewSeries(None).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);
}
}
+159
View File
@@ -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);
}
}
+743
View File
@@ -0,0 +1,743 @@
#[cfg(test)]
mod tests {
mod sonarr_tests {
use pretty_assertions::{assert_eq, assert_str_eq};
use tokio::sync::mpsc;
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
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeriesHistory)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetSeriesHistory(None).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
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonDetails)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodes(None).into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeFiles(None).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
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonHistory)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetSeasonHistory(None).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() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualSeasonSearch)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetSeasonReleases(None).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
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeDetails)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeDetails(None).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
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeFile)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeDetails(None).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();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeHistory)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeHistory(None).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 season_details_modal = SeasonDetailsModal {
episode_details_modal: Some(EpisodeDetailsModal::default()),
..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_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeReleases(None).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(None).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
.dispatch_by_sonarr_block(&ActiveSonarrBlock::TestIndexer)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::TestIndexer(None).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(None).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
.dispatch_by_sonarr_block(&ActiveSonarrBlock::AddSeriesSearchResults)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::SearchNewSeries(None).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"
);
}
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)
}
}
}
+45
View File
@@ -33,6 +33,15 @@ pub enum SonarrListCommand {
)] )]
series_id: i64, 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")] #[command(about = "Fetch all history events for the episode with the given ID")]
EpisodeHistory { EpisodeHistory {
#[arg( #[arg(
@@ -67,6 +76,23 @@ pub enum SonarrListCommand {
QueuedEvents, QueuedEvents,
#[command(about = "List all root folders in Sonarr")] #[command(about = "List all root folders in Sonarr")]
RootFolders, 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")] #[command(about = "List all series in your Sonarr library")]
Series, Series,
#[command(about = "Fetch all history events for the series with the given ID")] #[command(about = "Fetch all history events for the series with the given ID")]
@@ -141,6 +167,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
.await?; .await?;
serde_json::to_string_pretty(&resp)? serde_json::to_string_pretty(&resp)?
} }
SonarrListCommand::EpisodeFiles { series_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::GetEpisodeFiles(Some(series_id)).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrListCommand::EpisodeHistory { episode_id } => { SonarrListCommand::EpisodeHistory { episode_id } => {
let resp = self let resp = self
.network .network
@@ -207,6 +240,18 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
.await?; .await?;
serde_json::to_string_pretty(&resp)? serde_json::to_string_pretty(&resp)?
} }
SonarrListCommand::SeasonHistory {
series_id,
season_number,
} => {
let resp = self
.network
.handle_network_event(
SonarrEvent::GetSeasonHistory(Some((series_id, season_number))).into(),
)
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrListCommand::Series => { SonarrListCommand::Series => {
let resp = self let resp = self
.network .network
@@ -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] #[test]
fn test_list_episode_history_requires_series_id() { fn test_list_episode_history_requires_series_id() {
let result = 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] #[test]
fn test_list_series_history_requires_series_id() { fn test_list_series_history_requires_series_id() {
let result = let result =
@@ -263,6 +348,32 @@ mod tests {
assert!(result.is_ok()); 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(Some(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] #[tokio::test]
async fn test_handle_list_history_command() { async fn test_handle_list_history_command() {
let expected_events = 1000; let expected_events = 1000;
@@ -368,5 +479,35 @@ mod tests {
assert!(result.is_ok()); 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(Some((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());
}
} }
} }
+45
View File
@@ -120,6 +120,32 @@ pub enum SonarrCommand {
}, },
#[command(about = "Test all Sonarr indexers")] #[command(about = "Test all Sonarr indexers")]
TestAllIndexers, 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 { impl From<SonarrCommand> for Command {
@@ -245,6 +271,25 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrCommand> for SonarrCliHandler<'a, '
.await?; .await?;
serde_json::to_string_pretty(&resp)? serde_json::to_string_pretty(&resp)?
} }
SonarrCommand::ToggleEpisodeMonitoring { episode_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::ToggleEpisodeMonitoring(Some(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(Some((series_id, season_number))).into(),
)
.await?;
serde_json::to_string_pretty(&resp)?
}
}; };
Ok(result) Ok(result)
+138
View File
@@ -142,6 +142,80 @@ mod tests {
assert!(result.is_ok()); 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 { mod handler {
@@ -616,5 +690,69 @@ mod tests {
assert!(result.is_ok()); 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(Some(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(Some((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());
}
} }
} }
+7
View File
@@ -19,6 +19,7 @@ pub enum Key {
Home, Home,
End, End,
Tab, Tab,
BackTab,
Delete, Delete,
Ctrl(char), Ctrl(char),
Char(char), Char(char),
@@ -40,6 +41,7 @@ impl Display for Key {
Key::Home => write!(f, "<home>"), Key::Home => write!(f, "<home>"),
Key::End => write!(f, "<end>"), Key::End => write!(f, "<end>"),
Key::Tab => write!(f, "<tab>"), Key::Tab => write!(f, "<tab>"),
Key::BackTab => write!(f, "<shift-tab>"),
Key::Delete => write!(f, "<del>"), Key::Delete => write!(f, "<del>"),
_ => write!(f, "<{self:?}>"), _ => write!(f, "<{self:?}>"),
} }
@@ -75,6 +77,11 @@ impl From<KeyEvent> for Key {
KeyEvent { KeyEvent {
code: KeyCode::End, .. code: KeyCode::End, ..
} => Key::End, } => Key::End,
KeyEvent {
code: KeyCode::BackTab,
modifiers: KeyModifiers::SHIFT,
..
} => Key::BackTab,
KeyEvent { KeyEvent {
code: KeyCode::Tab, .. code: KeyCode::Tab, ..
} => Key::Tab, } => Key::Tab,
+14
View File
@@ -17,6 +17,7 @@ mod tests {
#[case(Key::Home, "home")] #[case(Key::Home, "home")]
#[case(Key::End, "end")] #[case(Key::End, "end")]
#[case(Key::Tab, "tab")] #[case(Key::Tab, "tab")]
#[case(Key::BackTab, "shift-tab")]
#[case(Key::Delete, "del")] #[case(Key::Delete, "del")]
#[case(Key::Char('q'), "q")] #[case(Key::Char('q'), "q")]
#[case(Key::Ctrl('q'), "ctrl-q")] #[case(Key::Ctrl('q'), "ctrl-q")]
@@ -67,6 +68,19 @@ mod tests {
assert_eq!(Key::from(KeyEvent::from(KeyCode::Tab)), Key::Tab); 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] #[test]
fn test_key_from_delete() { fn test_key_from_delete() {
assert_eq!(Key::from(KeyEvent::from(KeyCode::Delete)), Key::Delete); assert_eq!(Key::from(KeyEvent::from(KeyCode::Delete)), Key::Delete);
+195 -68
View File
@@ -99,86 +99,96 @@ mod test_utils {
#[macro_export] #[macro_export]
macro_rules! test_iterable_scroll { 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] #[rstest]
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) { fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
let mut app = App::default(); let mut app = App::default();
app.push_navigation_stack($block.into());
app app
.data .data
.radarr_data .$servarr_data
.$data_ref .$data_ref
.set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]); .set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]);
$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(), "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(); $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] #[rstest]
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) { fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
let mut app = App::default(); let mut app = App::default();
app.push_navigation_stack($block.into());
app app
.data .data
.radarr_data .$servarr_data
.$data_ref .$data_ref
.set_items(simple_stateful_iterable_vec!($items)); .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!( pretty_assertions::assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field, app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 2" "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.$data_ref.current_selection().$field, app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 1" "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] #[rstest]
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) { fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
let mut app = App::default(); 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.$data_ref.current_selection().$field, app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 2" "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.$data_ref.current_selection().$field, app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 1" "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] #[rstest]
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) { fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
let mut app = App::default(); 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 app
.data .data
.radarr_data .$servarr_data
.$data_ref .$data_ref
.current_selection() .current_selection()
.$field .$field
@@ -186,12 +196,12 @@ mod test_utils {
"Test 2" "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 app
.data .data
.radarr_data .$servarr_data
.$data_ref .$data_ref
.current_selection() .current_selection()
.$field .$field
@@ -204,86 +214,96 @@ mod test_utils {
#[macro_export] #[macro_export]
macro_rules! test_iterable_home_and_end { 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] #[test]
fn $func() { fn $func() {
let mut app = App::default(); 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 1".to_owned(),
"Test 2".to_owned(), "Test 2".to_owned(),
"Test 3".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] #[test]
fn $func() { fn $func() {
let mut app = App::default(); let mut app = App::default();
app.push_navigation_stack($block.into());
app app
.data .data
.radarr_data .$servarr_data
.$data_ref .$data_ref
.set_items(extended_stateful_iterable_vec!($items)); .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!( pretty_assertions::assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field, app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 3" "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.$data_ref.current_selection().$field, app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 1" "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] #[test]
fn $func() { fn $func() {
let mut app = App::default(); 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.$data_ref.current_selection().$field, app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 3" "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.$data_ref.current_selection().$field, app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 1" "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] #[test]
fn $func() { fn $func() {
let mut app = App::default(); 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 app
.data .data
.radarr_data .$servarr_data
.$data_ref .$data_ref
.current_selection() .current_selection()
.$field .$field
@@ -291,12 +311,12 @@ mod test_utils {
"Test 3" "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 app
.data .data
.radarr_data .$servarr_data
.$data_ref .$data_ref
.current_selection() .current_selection()
.$field .$field
@@ -311,18 +331,125 @@ mod test_utils {
macro_rules! test_handler_delegation { macro_rules! test_handler_delegation {
($handler:ident, $base:expr, $active_block:expr) => { ($handler:ident, $base:expr, $active_block:expr) => {
let mut app = App::default(); let mut app = App::default();
app.push_navigation_stack($base.clone().into()); app.data.sonarr_data.history.set_items(vec![
app.push_navigation_stack($active_block.clone().into()); $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( $handler::with(DEFAULT_KEYBINDINGS.esc.key, &mut app, $active_block, None).handle();
&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);
}; };
} }
} }
+82 -3
View File
@@ -1,10 +1,19 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::models::radarr_models::Movie;
use crate::models::sonarr_models::Series;
use pretty_assertions::assert_eq;
use rstest::rstest; use rstest::rstest;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::handle_events;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle}; 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] #[test]
fn test_handle_clear_errors() { fn test_handle_clear_errors() {
@@ -17,17 +26,87 @@ mod tests {
} }
#[rstest] #[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(); 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());
app.server_tabs.set_index(index);
app.is_first_render = false;
app.error = "Test".into();
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());
}
#[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); 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); 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); 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);
}
} }
+60 -22
View File
@@ -1,4 +1,5 @@
use radarr_handlers::RadarrHandler; use radarr_handlers::RadarrHandler;
use sonarr_handlers::SonarrHandler;
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
@@ -6,6 +7,7 @@ use crate::event::Key;
use crate::models::{HorizontallyScrollableText, Route}; use crate::models::{HorizontallyScrollableText, Route};
mod radarr_handlers; mod radarr_handlers;
mod sonarr_handlers;
#[cfg(test)] #[cfg(test)]
#[path = "handlers_tests.rs"] #[path = "handlers_tests.rs"]
@@ -14,45 +16,46 @@ mod handlers_tests;
#[cfg(test)] #[cfg(test)]
#[path = "handler_test_utils.rs"] #[path = "handler_test_utils.rs"]
pub mod handler_test_utils; 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) { fn handle_key_event(&mut self) {
let key = self.get_key(); let key = self.get_key();
match key { match key {
_ if *key == DEFAULT_KEYBINDINGS.up.key => { _ if key == DEFAULT_KEYBINDINGS.up.key => {
if self.is_ready() { if self.is_ready() {
self.handle_scroll_up(); self.handle_scroll_up();
} }
} }
_ if *key == DEFAULT_KEYBINDINGS.down.key => { _ if key == DEFAULT_KEYBINDINGS.down.key => {
if self.is_ready() { if self.is_ready() {
self.handle_scroll_down(); self.handle_scroll_down();
} }
} }
_ if *key == DEFAULT_KEYBINDINGS.home.key => { _ if key == DEFAULT_KEYBINDINGS.home.key => {
if self.is_ready() { if self.is_ready() {
self.handle_home(); self.handle_home();
} }
} }
_ if *key == DEFAULT_KEYBINDINGS.end.key => { _ if key == DEFAULT_KEYBINDINGS.end.key => {
if self.is_ready() { if self.is_ready() {
self.handle_end(); self.handle_end();
} }
} }
_ if *key == DEFAULT_KEYBINDINGS.delete.key => { _ if key == DEFAULT_KEYBINDINGS.delete.key => {
if self.is_ready() { if self.is_ready() {
self.handle_delete(); 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() self.handle_left_right_action()
} }
_ if *key == DEFAULT_KEYBINDINGS.submit.key => { _ if key == DEFAULT_KEYBINDINGS.submit.key => {
if self.is_ready() { if self.is_ready() {
self.handle_submit(); 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() { if self.is_ready() {
self.handle_char_key_event(); self.handle_char_key_event();
@@ -65,9 +68,9 @@ pub trait KeyEventHandler<'a, 'b, T: Into<Route>> {
self.handle_key_event(); self.handle_key_event();
} }
fn accepts(active_block: &'a T) -> bool; fn accepts(active_block: T) -> bool;
fn with(key: &'a Key, app: &'a mut App<'b>, active_block: &'a T, context: &'a Option<T>) -> Self; fn with(key: Key, app: &'a mut App<'b>, active_block: T, context: Option<T>) -> Self;
fn get_key(&self) -> &Key; fn get_key(&self) -> Key;
fn is_ready(&self) -> bool; fn is_ready(&self) -> bool;
fn handle_scroll_up(&mut self); fn handle_scroll_up(&mut self);
fn handle_scroll_down(&mut self); fn handle_scroll_down(&mut self);
@@ -81,8 +84,24 @@ pub trait KeyEventHandler<'a, 'b, T: Into<Route>> {
} }
pub fn handle_events(key: Key, app: &mut App<'_>) { pub fn handle_events(key: Key, app: &mut App<'_>) {
if let Route::Radarr(active_radarr_block, context) = *app.get_current_route() { if key == DEFAULT_KEYBINDINGS.next_servarr.key {
RadarrHandler::with(&key, app, &active_radarr_block, &context).handle() app.reset();
app.server_tabs.next();
app.pop_and_push_navigation_stack(app.server_tabs.get_active_route());
} 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());
} 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 +111,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 { match key {
_ if *key == DEFAULT_KEYBINDINGS.left.key || *key == DEFAULT_KEYBINDINGS.right.key => { _ if key == DEFAULT_KEYBINDINGS.left.key || key == DEFAULT_KEYBINDINGS.right.key => {
if let Route::Radarr(_, _) = *app.get_current_route() { match app.get_current_route() {
app.data.radarr_data.prompt_confirm = !app.data.radarr_data.prompt_confirm; 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 +132,10 @@ fn handle_prompt_toggle(app: &mut App<'_>, key: &Key) {
macro_rules! handle_text_box_left_right_keys { macro_rules! handle_text_box_left_right_keys {
($self:expr, $key:expr, $input:expr) => { ($self:expr, $key:expr, $input:expr) => {
match $self.key { match $self.key {
_ if *$key == DEFAULT_KEYBINDINGS.left.key => { _ if $key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.left.key => {
$input.scroll_left(); $input.scroll_left();
} }
_ if *$key == DEFAULT_KEYBINDINGS.right.key => { _ if $key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.right.key => {
$input.scroll_right(); $input.scroll_right();
} }
_ => (), _ => (),
@@ -122,13 +147,26 @@ macro_rules! handle_text_box_left_right_keys {
macro_rules! handle_text_box_keys { macro_rules! handle_text_box_keys {
($self:expr, $key:expr, $input:expr) => { ($self:expr, $key:expr, $input:expr) => {
match $self.key { match $self.key {
_ if *$key == DEFAULT_KEYBINDINGS.backspace.key => { _ if $key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.backspace.key => {
$input.pop(); $input.pop();
} }
Key::Char(character) => { 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::radarr_models::{BlocklistItem, BlocklistItemMovie};
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS};
use crate::models::servarr_models::{Language, Quality, QualityWrapper}; 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 { mod test_handle_delete {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@@ -267,11 +27,11 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.blocklist.set_items(blocklist_vec()); 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!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::DeleteBlocklistItemPrompt.into() ActiveRadarrBlock::DeleteBlocklistItemPrompt.into()
); );
} }
@@ -282,12 +42,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
app.data.radarr_data.blocklist.set_items(blocklist_vec()); 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!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
} }
} }
@@ -304,21 +61,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(3); app.data.radarr_data.main_tabs.set_index(3);
BlocklistHandler::with( BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Blocklist,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Downloads.into() ActiveRadarrBlock::Downloads.into()
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
); );
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
} }
#[rstest] #[rstest]
@@ -328,20 +82,20 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(3); app.data.radarr_data.main_tabs.set_index(3);
BlocklistHandler::with( BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Blocklist,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
} }
@@ -356,11 +110,11 @@ mod tests {
) { ) {
let mut app = App::default(); 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); 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); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -382,11 +136,11 @@ mod tests {
app.data.radarr_data.blocklist.set_items(blocklist_vec()); app.data.radarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); 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!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::BlocklistItemDetails.into() ActiveRadarrBlock::BlocklistItemDetails.into()
); );
} }
@@ -397,12 +151,9 @@ mod tests {
app.data.radarr_data.blocklist.set_items(blocklist_vec()); app.data.radarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); 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!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
} }
#[rstest] #[rstest]
@@ -427,14 +178,14 @@ mod tests {
app.push_navigation_stack(base_route.into()); app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.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!(app.data.radarr_data.prompt_confirm);
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(expected_action) Some(expected_action)
); );
assert_eq!(app.get_current_route(), &base_route.into()); assert_eq!(app.get_current_route(), base_route.into());
} }
#[rstest] #[rstest]
@@ -450,42 +201,11 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
app.push_navigation_stack(prompt_block.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!(!app.data.radarr_data.prompt_confirm);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert_eq!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
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);
} }
} }
@@ -517,9 +237,9 @@ mod tests {
app.push_navigation_stack(prompt_block.into()); app.push_navigation_stack(prompt_block.into());
app.data.radarr_data.prompt_confirm = true; 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); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -530,37 +250,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::BlocklistItemDetails.into()); app.push_navigation_stack(ActiveRadarrBlock::BlocklistItemDetails.into());
BlocklistHandler::with( BlocklistHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::BlocklistItemDetails, ActiveRadarrBlock::BlocklistItemDetails,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
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()
);
} }
#[rstest] #[rstest]
@@ -571,12 +268,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
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!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert!(app.error.text.is_empty()); assert!(app.error.text.is_empty());
} }
} }
@@ -596,17 +290,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
BlocklistHandler::with( BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Blocklist,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert!(app.should_refresh); assert!(app.should_refresh);
} }
@@ -618,17 +309,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
BlocklistHandler::with( BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Blocklist,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert!(!app.should_refresh); assert!(!app.should_refresh);
} }
@@ -638,16 +326,16 @@ mod tests {
app.data.radarr_data.blocklist.set_items(blocklist_vec()); app.data.radarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with( BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.clear.key, DEFAULT_KEYBINDINGS.clear.key,
&mut app, &mut app,
&ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Blocklist,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::BlocklistClearAllItemsPrompt.into() ActiveRadarrBlock::BlocklistClearAllItemsPrompt.into()
); );
} }
@@ -659,64 +347,14 @@ mod tests {
app.data.radarr_data.blocklist.set_items(blocklist_vec()); app.data.radarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with( BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.clear.key, DEFAULT_KEYBINDINGS.clear.key,
&mut app, &mut app,
&ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Blocklist,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
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);
} }
#[rstest] #[rstest]
@@ -741,10 +379,10 @@ mod tests {
app.push_navigation_stack(prompt_block.into()); app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with( BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key, DEFAULT_KEYBINDINGS.confirm.key,
&mut app, &mut app,
&prompt_block, prompt_block,
&None, None,
) )
.handle(); .handle();
@@ -753,7 +391,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(expected_action) Some(expected_action)
); );
assert_eq!(app.get_current_route(), &base_route.into()); assert_eq!(app.get_current_route(), base_route.into());
} }
} }
@@ -896,9 +534,9 @@ mod tests {
fn test_blocklist_handler_accepts() { fn test_blocklist_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if BLOCKLIST_BLOCKS.contains(&active_radarr_block) { if BLOCKLIST_BLOCKS.contains(&active_radarr_block) {
assert!(BlocklistHandler::accepts(&active_radarr_block)); assert!(BlocklistHandler::accepts(active_radarr_block));
} else { } else {
assert!(!BlocklistHandler::accepts(&active_radarr_block)); assert!(!BlocklistHandler::accepts(active_radarr_block));
} }
}) })
} }
@@ -909,10 +547,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = BlocklistHandler::with( let handler = BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Blocklist,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -924,10 +562,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = BlocklistHandler::with( let handler = BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Blocklist,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -944,10 +582,10 @@ mod tests {
.set_items(vec![BlocklistItem::default()]); .set_items(vec![BlocklistItem::default()]);
let handler = BlocklistHandler::with( let handler = BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Blocklist,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
@@ -1029,15 +667,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())
}),
}]
}
} }
+42 -99
View File
@@ -1,12 +1,13 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key; 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::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler}; use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::BlocklistItem; use crate::models::radarr_models::BlocklistItem;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS};
use crate::models::stateful_table::SortOption; use crate::models::stateful_table::SortOption;
use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
#[cfg(test)] #[cfg(test)]
@@ -14,22 +15,43 @@ use crate::network::radarr_network::RadarrEvent;
mod blocklist_handler_tests; mod blocklist_handler_tests;
pub(super) struct BlocklistHandler<'a, 'b> { pub(super) struct BlocklistHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> BlocklistHandler<'a, 'b> {
handle_table_events!(
self,
blocklist,
self.app.data.radarr_data.blocklist,
BlocklistItem
);
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn handle(&mut self) {
BLOCKLIST_BLOCKS.contains(active_block) 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( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> Self { ) -> Self {
BlocklistHandler { BlocklistHandler {
key, key,
@@ -39,7 +61,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -47,72 +69,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
!self.app.is_loading && !self.app.data.radarr_data.blocklist.is_empty() !self.app.is_loading && !self.app.data.radarr_data.blocklist.is_empty()
} }
fn handle_scroll_up(&mut self) { 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_down(&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_home(&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_end(&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_delete(&mut self) { fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Blocklist { if self.active_radarr_block == ActiveRadarrBlock::Blocklist {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::DeleteBlocklistItemPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::DeleteBlocklistItemPrompt.into());
@@ -145,18 +111,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
self.app.pop_navigation_stack(); 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 => { ActiveRadarrBlock::Blocklist => {
self self
.app .app
@@ -173,7 +127,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false; self.app.data.radarr_data.prompt_confirm = false;
} }
ActiveRadarrBlock::BlocklistItemDetails | ActiveRadarrBlock::BlocklistSortPrompt => { ActiveRadarrBlock::BlocklistItemDetails => {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
} }
_ => handle_clear_errors(self.app), _ => handle_clear_errors(self.app),
@@ -184,29 +138,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
let key = self.key; let key = self.key;
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::Blocklist => match self.key { ActiveRadarrBlock::Blocklist => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true; self.app.should_refresh = true;
} }
_ if *key == DEFAULT_KEYBINDINGS.clear.key => { _ if key == DEFAULT_KEYBINDINGS.clear.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::BlocklistClearAllItemsPrompt.into()); .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 => { 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 = true;
self.app.data.radarr_data.prompt_confirm_action = self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteBlocklistItem(None)); Some(RadarrEvent::DeleteBlocklistItem(None));
@@ -215,7 +158,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
} }
} }
ActiveRadarrBlock::BlocklistClearAllItemsPrompt => { 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 = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::ClearBlocklist); 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::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::KeyEventHandler; use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::CollectionMovie;
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, ADD_MOVIE_SELECTION_BLOCKS, COLLECTION_DETAILS_BLOCKS, ActiveRadarrBlock, ADD_MOVIE_SELECTION_BLOCKS, COLLECTION_DETAILS_BLOCKS,
EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS,
}; };
use crate::models::stateful_table::StatefulTable; use crate::models::stateful_table::StatefulTable;
use crate::models::{BlockSelectionState, Scrollable}; use crate::models::BlockSelectionState;
#[cfg(test)] #[cfg(test)]
#[path = "collection_details_handler_tests.rs"] #[path = "collection_details_handler_tests.rs"]
mod collection_details_handler_tests; mod collection_details_handler_tests;
pub(super) struct CollectionDetailsHandler<'a, 'b> { pub(super) struct CollectionDetailsHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<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> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn handle(&mut self) {
COLLECTION_DETAILS_BLOCKS.contains(active_block) 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( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
) -> CollectionDetailsHandler<'a, 'b> { ) -> CollectionDetailsHandler<'a, 'b> {
CollectionDetailsHandler { CollectionDetailsHandler {
key, 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 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() !self.app.is_loading && !self.app.data.radarr_data.collection_movies.is_empty()
} }
fn handle_scroll_up(&mut self) { 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_down(&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_home(&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_end(&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_delete(&mut self) {} fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {} fn handle_left_right_action(&mut self) {}
fn handle_submit(&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 let tmdb_id = self
.app .app
.data .data
@@ -111,7 +111,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan
.into(), .into(),
); );
self.app.data.radarr_data.selected_block = 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()); 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) { fn handle_char_key_event(&mut self) {
if *self.active_radarr_block == ActiveRadarrBlock::CollectionDetails if self.active_radarr_block == ActiveRadarrBlock::CollectionDetails
&& *self.key == DEFAULT_KEYBINDINGS.edit.key && self.key == DEFAULT_KEYBINDINGS.edit.key
{ {
self.app.push_navigation_stack( self.app.push_navigation_stack(
( (
ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
Some(*self.active_radarr_block), Some(self.active_radarr_block),
) )
.into(), .into(),
); );
self.app.data.radarr_data.edit_collection_modal = Some((&self.app.data.radarr_data).into()); self.app.data.radarr_data.edit_collection_modal = Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block = 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::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, COLLECTION_DETAILS_BLOCKS, 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 { mod test_handle_submit {
use bimap::BiMap; use bimap::BiMap;
@@ -171,24 +35,24 @@ mod tests {
.set_items(vec![CollectionMovie::default()]); .set_items(vec![CollectionMovie::default()]);
app.data.radarr_data.quality_profile_map = app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1, "B - Test 2".to_owned()), (0, "A - Test 1".to_owned())]); 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 app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(ADD_MOVIE_SELECTION_BLOCKS.len() - 1); .set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
CollectionDetailsHandler::with( CollectionDetailsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::CollectionDetails, ActiveRadarrBlock::CollectionDetails,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&( (
ActiveRadarrBlock::AddMoviePrompt, ActiveRadarrBlock::AddMoviePrompt,
Some(ActiveRadarrBlock::CollectionDetails) Some(ActiveRadarrBlock::CollectionDetails)
) )
@@ -205,7 +69,7 @@ mod tests {
.is_empty()); .is_empty());
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::AddMovieSelectRootFolder ActiveRadarrBlock::AddMovieSelectRootFolder
); );
assert!(!app assert!(!app
.data .data
@@ -250,16 +114,16 @@ mod tests {
.set_items(vec![CollectionMovie::default()]); .set_items(vec![CollectionMovie::default()]);
CollectionDetailsHandler::with( CollectionDetailsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::CollectionDetails, ActiveRadarrBlock::CollectionDetails,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::CollectionDetails.into() ActiveRadarrBlock::CollectionDetails.into()
); );
assert!(app.data.radarr_data.add_movie_modal.is_none()); assert!(app.data.radarr_data.add_movie_modal.is_none());
} }
@@ -279,16 +143,16 @@ mod tests {
.set_items(vec![Movie::default()]); .set_items(vec![Movie::default()]);
CollectionDetailsHandler::with( CollectionDetailsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::CollectionDetails, ActiveRadarrBlock::CollectionDetails,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::ViewMovieOverview.into() ActiveRadarrBlock::ViewMovieOverview.into()
); );
} }
} }
@@ -313,16 +177,16 @@ mod tests {
.set_items(vec![CollectionMovie::default()]); .set_items(vec![CollectionMovie::default()]);
CollectionDetailsHandler::with( CollectionDetailsHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::CollectionDetails, ActiveRadarrBlock::CollectionDetails,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::Collections.into() ActiveRadarrBlock::Collections.into()
); );
assert!(app.data.radarr_data.collection_movies.items.is_empty()); assert!(app.data.radarr_data.collection_movies.items.is_empty());
} }
@@ -334,16 +198,16 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::ViewMovieOverview.into()); app.push_navigation_stack(ActiveRadarrBlock::ViewMovieOverview.into());
CollectionDetailsHandler::with( CollectionDetailsHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::ViewMovieOverview, ActiveRadarrBlock::ViewMovieOverview,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::CollectionDetails.into() ActiveRadarrBlock::CollectionDetails.into()
); );
} }
} }
@@ -367,7 +231,7 @@ mod tests {
test_edit_collection_key!( test_edit_collection_key!(
CollectionDetailsHandler, CollectionDetailsHandler,
ActiveRadarrBlock::CollectionDetails, ActiveRadarrBlock::CollectionDetails,
ActiveRadarrBlock::CollectionDetails Some(ActiveRadarrBlock::CollectionDetails)
); );
} }
@@ -388,16 +252,16 @@ mod tests {
app.data.radarr_data = radarr_data; app.data.radarr_data = radarr_data;
CollectionDetailsHandler::with( CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.edit.key, DEFAULT_KEYBINDINGS.edit.key,
&mut app, &mut app,
&ActiveRadarrBlock::CollectionDetails, ActiveRadarrBlock::CollectionDetails,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::CollectionDetails.into() ActiveRadarrBlock::CollectionDetails.into()
); );
assert!(app.data.radarr_data.edit_collection_modal.is_none()); assert!(app.data.radarr_data.edit_collection_modal.is_none());
} }
@@ -407,9 +271,9 @@ mod tests {
fn test_collection_details_handler_accepts() { fn test_collection_details_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if COLLECTION_DETAILS_BLOCKS.contains(&active_radarr_block) { if COLLECTION_DETAILS_BLOCKS.contains(&active_radarr_block) {
assert!(CollectionDetailsHandler::accepts(&active_radarr_block)); assert!(CollectionDetailsHandler::accepts(active_radarr_block));
} else { } else {
assert!(!CollectionDetailsHandler::accepts(&active_radarr_block)); assert!(!CollectionDetailsHandler::accepts(active_radarr_block));
} }
}); });
} }
@@ -420,10 +284,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = CollectionDetailsHandler::with( let handler = CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::CollectionDetails, ActiveRadarrBlock::CollectionDetails,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -435,10 +299,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = CollectionDetailsHandler::with( let handler = CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::CollectionDetails, ActiveRadarrBlock::CollectionDetails,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -455,10 +319,10 @@ mod tests {
.set_items(vec![CollectionMovie::default()]); .set_items(vec![CollectionMovie::default()]);
let handler = CollectionDetailsHandler::with( let handler = CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::CollectionDetails, ActiveRadarrBlock::CollectionDetails,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
File diff suppressed because it is too large Load Diff
@@ -12,22 +12,22 @@ use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
mod edit_collection_handler_tests; mod edit_collection_handler_tests;
pub(super) struct EditCollectionHandler<'a, 'b> { pub(super) struct EditCollectionHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
EDIT_COLLECTION_BLOCKS.contains(active_block) EDIT_COLLECTION_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> EditCollectionHandler<'a, 'b> { ) -> EditCollectionHandler<'a, 'b> {
EditCollectionHandler { EditCollectionHandler {
key, key,
@@ -37,7 +37,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -65,9 +65,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
.unwrap() .unwrap()
.quality_profile_list .quality_profile_list
.scroll_up(), .scroll_up(),
ActiveRadarrBlock::EditCollectionPrompt => { ActiveRadarrBlock::EditCollectionPrompt => self.app.data.radarr_data.selected_block.up(),
self.app.data.radarr_data.selected_block.previous()
}
_ => (), _ => (),
} }
} }
@@ -92,7 +90,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
.unwrap() .unwrap()
.quality_profile_list .quality_profile_list
.scroll_down(), .scroll_down(),
ActiveRadarrBlock::EditCollectionPrompt => self.app.data.radarr_data.selected_block.next(), ActiveRadarrBlock::EditCollectionPrompt => self.app.data.radarr_data.selected_block.down(),
_ => (), _ => (),
} }
} }
@@ -203,8 +201,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
| ActiveRadarrBlock::EditCollectionSelectQualityProfile => { | ActiveRadarrBlock::EditCollectionSelectQualityProfile => {
self.app.push_navigation_stack( 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(),
*self.context, self.context,
) )
.into(), .into(),
) )
@@ -212,8 +210,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
ActiveRadarrBlock::EditCollectionRootFolderPathInput => { ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
self.app.push_navigation_stack( 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(),
*self.context, self.context,
) )
.into(), .into(),
); );
@@ -308,8 +306,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
} }
ActiveRadarrBlock::EditCollectionPrompt => { ActiveRadarrBlock::EditCollectionPrompt => {
if self.app.data.radarr_data.selected_block.get_active_block() if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::EditCollectionConfirmPrompt == ActiveRadarrBlock::EditCollectionConfirmPrompt
&& *key == DEFAULT_KEYBINDINGS.confirm.key && key == DEFAULT_KEYBINDINGS.confirm.key
{ {
self.app.data.radarr_data.prompt_confirm = true; 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(None));
@@ -44,10 +44,10 @@ mod tests {
if key == Key::Up { if key == Key::Up {
for i in (0..minimum_availability_vec.len()).rev() { for i in (0..minimum_availability_vec.len()).rev() {
EditCollectionHandler::with( EditCollectionHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability, ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
&None, None,
) )
.handle(); .handle();
@@ -66,10 +66,10 @@ mod tests {
} else { } else {
for i in 0..minimum_availability_vec.len() { for i in 0..minimum_availability_vec.len() {
EditCollectionHandler::with( EditCollectionHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability, ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
&None, None,
) )
.handle(); .handle();
@@ -104,10 +104,10 @@ mod tests {
.set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]); .set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]);
EditCollectionHandler::with( EditCollectionHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionSelectQualityProfile, ActiveRadarrBlock::EditCollectionSelectQualityProfile,
&None, None,
) )
.handle(); .handle();
@@ -124,10 +124,10 @@ mod tests {
); );
EditCollectionHandler::with( EditCollectionHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionSelectQualityProfile, ActiveRadarrBlock::EditCollectionSelectQualityProfile,
&None, None,
) )
.handle(); .handle();
@@ -149,26 +149,21 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next(); app.data.radarr_data.selected_block.down();
EditCollectionHandler::with( EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
&key, .handle();
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
if key == Key::Up { if key == Key::Up {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditCollectionToggleMonitored ActiveRadarrBlock::EditCollectionToggleMonitored
); );
} else { } else {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditCollectionSelectQualityProfile ActiveRadarrBlock::EditCollectionSelectQualityProfile
); );
} }
} }
@@ -181,20 +176,15 @@ mod tests {
app.is_loading = true; app.is_loading = true;
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next(); app.data.radarr_data.selected_block.down();
EditCollectionHandler::with( EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
&key, .handle();
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
); );
} }
} }
@@ -224,10 +214,10 @@ mod tests {
.set_items(minimum_availability_vec.clone()); .set_items(minimum_availability_vec.clone());
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability, ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
&None, None,
) )
.handle(); .handle();
@@ -244,10 +234,10 @@ mod tests {
); );
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability, ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
&None, None,
) )
.handle(); .handle();
@@ -282,10 +272,10 @@ mod tests {
]); ]);
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionSelectQualityProfile, ActiveRadarrBlock::EditCollectionSelectQualityProfile,
&None, None,
) )
.handle(); .handle();
@@ -302,10 +292,10 @@ mod tests {
); );
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionSelectQualityProfile, ActiveRadarrBlock::EditCollectionSelectQualityProfile,
&None, None,
) )
.handle(); .handle();
@@ -331,10 +321,10 @@ mod tests {
}); });
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput, ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None, None,
) )
.handle(); .handle();
@@ -352,10 +342,10 @@ mod tests {
); );
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput, ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None, None,
) )
.handle(); .handle();
@@ -386,23 +376,13 @@ mod tests {
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) { fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default(); let mut app = App::default();
EditCollectionHandler::with( EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
&key, .handle();
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
assert!(app.data.radarr_data.prompt_confirm); assert!(app.data.radarr_data.prompt_confirm);
EditCollectionHandler::with( EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
&key, .handle();
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -416,10 +396,10 @@ mod tests {
}); });
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput, ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None, None,
) )
.handle(); .handle();
@@ -437,10 +417,10 @@ mod tests {
); );
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput, ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None, None,
) )
.handle(); .handle();
@@ -484,10 +464,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into());
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput, ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None, None,
) )
.handle(); .handle();
@@ -503,7 +483,7 @@ mod tests {
.is_empty()); .is_empty());
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into() ActiveRadarrBlock::EditCollectionPrompt.into()
); );
} }
@@ -514,24 +494,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); .set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::Collections.into() ActiveRadarrBlock::Collections.into()
); );
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
} }
@@ -544,24 +524,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); .set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::Collections.into() ActiveRadarrBlock::Collections.into()
); );
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
@@ -579,24 +559,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); .set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into() ActiveRadarrBlock::EditCollectionPrompt.into()
); );
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh); assert!(!app.should_refresh);
@@ -611,18 +591,18 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app.push_navigation_stack(current_route); app.push_navigation_stack(current_route);
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections), Some(ActiveRadarrBlock::Collections),
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &current_route); assert_eq!(app.get_current_route(), current_route);
assert_eq!( assert_eq!(
app app
.data .data
@@ -635,14 +615,14 @@ mod tests {
); );
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections), Some(ActiveRadarrBlock::Collections),
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &current_route); assert_eq!(app.get_current_route(), current_route);
assert_eq!( assert_eq!(
app app
.data .data
@@ -664,23 +644,23 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .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); app.push_navigation_stack(current_route);
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections), Some(ActiveRadarrBlock::Collections),
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &current_route); assert_eq!(app.get_current_route(), current_route);
assert_eq!( assert_eq!(
app app
.data .data
@@ -693,14 +673,14 @@ mod tests {
); );
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections), Some(ActiveRadarrBlock::Collections),
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &current_route); assert_eq!(app.get_current_route(), current_route);
assert_eq!( assert_eq!(
app app
.data .data
@@ -731,20 +711,20 @@ mod tests {
.into(), .into(),
); );
app.data.radarr_data.selected_block = 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(index); app.data.radarr_data.selected_block.set_index(0, index);
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections), Some(ActiveRadarrBlock::Collections),
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), 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); assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
@@ -768,16 +748,16 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into()); app.push_navigation_stack(active_radarr_block.into());
EditCollectionHandler::with( EditCollectionHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&Some(ActiveRadarrBlock::Collections), Some(ActiveRadarrBlock::Collections),
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into() ActiveRadarrBlock::EditCollectionPrompt.into()
); );
if active_radarr_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput { if active_radarr_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput {
@@ -806,17 +786,17 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into());
EditCollectionHandler::with( EditCollectionHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput, ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None, None,
) )
.handle(); .handle();
assert!(!app.should_ignore_quit_key); assert!(!app.should_ignore_quit_key);
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into() ActiveRadarrBlock::EditCollectionPrompt.into()
); );
} }
@@ -828,16 +808,16 @@ mod tests {
app.data.radarr_data = create_test_radarr_data(); app.data.radarr_data = create_test_radarr_data();
EditCollectionHandler::with( EditCollectionHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::Collections.into() ActiveRadarrBlock::Collections.into()
); );
let radarr_data = &app.data.radarr_data; let radarr_data = &app.data.radarr_data;
@@ -860,11 +840,11 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(active_radarr_block.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!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::Collections.into() ActiveRadarrBlock::Collections.into()
); );
} }
} }
@@ -890,10 +870,10 @@ mod tests {
}); });
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key, DEFAULT_KEYBINDINGS.backspace.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput, ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None, None,
) )
.handle(); .handle();
@@ -916,10 +896,10 @@ mod tests {
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
EditCollectionHandler::with( EditCollectionHandler::with(
&Key::Char('h'), Key::Char('h'),
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput, ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None, None,
) )
.handle(); .handle();
@@ -943,24 +923,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); .set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
EditCollectionHandler::with( EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key, DEFAULT_KEYBINDINGS.confirm.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::Collections.into() ActiveRadarrBlock::Collections.into()
); );
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
@@ -974,9 +954,9 @@ mod tests {
fn test_edit_collection_handler_accepts() { fn test_edit_collection_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if EDIT_COLLECTION_BLOCKS.contains(&active_radarr_block) { if EDIT_COLLECTION_BLOCKS.contains(&active_radarr_block) {
assert!(EditCollectionHandler::accepts(&active_radarr_block)); assert!(EditCollectionHandler::accepts(active_radarr_block));
} else { } else {
assert!(!EditCollectionHandler::accepts(&active_radarr_block)); assert!(!EditCollectionHandler::accepts(active_radarr_block));
} }
}); });
} }
@@ -987,10 +967,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = EditCollectionHandler::with( let handler = EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -1002,10 +982,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = EditCollectionHandler::with( let handler = EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -1018,10 +998,10 @@ mod tests {
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
let handler = EditCollectionHandler::with( let handler = EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditCollectionPrompt, ActiveRadarrBlock::EditCollectionPrompt,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
+58 -274
View File
@@ -1,18 +1,19 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key; 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::collection_details_handler::CollectionDetailsHandler;
use crate::handlers::radarr_handlers::collections::edit_collection_handler::EditCollectionHandler; use crate::handlers::radarr_handlers::collections::edit_collection_handler::EditCollectionHandler;
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys; 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::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::Collection; use crate::models::radarr_models::Collection;
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, COLLECTIONS_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS, ActiveRadarrBlock, COLLECTIONS_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS,
}; };
use crate::models::stateful_table::SortOption; use crate::models::stateful_table::SortOption;
use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable}; use crate::models::BlockSelectionState;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
mod collection_details_handler; mod collection_details_handler;
mod edit_collection_handler; mod edit_collection_handler;
@@ -22,38 +23,66 @@ mod edit_collection_handler;
mod collections_handler_tests; mod collections_handler_tests;
pub(super) struct CollectionsHandler<'a, 'b> { pub(super) struct CollectionsHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
context: &'a Option<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> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'a, 'b> {
fn handle(&mut self) { fn handle(&mut self) {
match self.active_radarr_block { let collections_table_handling_config =
_ if CollectionDetailsHandler::accepts(self.active_radarr_block) => { TableHandlingConfig::new(ActiveRadarrBlock::Collections.into())
CollectionDetailsHandler::with(self.key, self.app, self.active_radarr_block, self.context) .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,
)
.handle(); .handle();
}
_ if EditCollectionHandler::accepts(self.active_radarr_block) => {
EditCollectionHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ => self.handle_key_event(),
} }
_ if EditCollectionHandler::accepts(self.active_radarr_block) => {
EditCollectionHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ => self.handle_key_event(),
} }
} }
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
CollectionDetailsHandler::accepts(active_block) CollectionDetailsHandler::accepts(active_block)
|| EditCollectionHandler::accepts(active_block) || EditCollectionHandler::accepts(active_block)
|| COLLECTIONS_BLOCKS.contains(active_block) || COLLECTIONS_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> CollectionsHandler<'a, 'b> { ) -> CollectionsHandler<'a, 'b> {
CollectionsHandler { CollectionsHandler {
key, 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 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() !self.app.is_loading && !self.app.data.radarr_data.collections.is_empty()
} }
fn handle_scroll_up(&mut self) { 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_down(&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_home(&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_end(&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_delete(&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 { match self.active_radarr_block {
ActiveRadarrBlock::Collections => handle_change_tab_left_right_keys(self.app, self.key), ActiveRadarrBlock::Collections => handle_change_tab_left_right_keys(self.app, self.key),
ActiveRadarrBlock::UpdateAllCollectionsPrompt => handle_prompt_toggle(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 ActiveRadarrBlock::Collections => self
.app .app
.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()), .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 => { ActiveRadarrBlock::UpdateAllCollectionsPrompt => {
if self.app.data.radarr_data.prompt_confirm { if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateCollections); 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(); 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) { fn handle_esc(&mut self) {
match self.active_radarr_block { 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 => { ActiveRadarrBlock::UpdateAllCollectionsPrompt => {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false; 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); handle_clear_errors(self.app);
} }
} }
@@ -306,87 +150,27 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
let key = self.key; let key = self.key;
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::Collections => match self.key { ActiveRadarrBlock::Collections => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.search.key => { _ if key == DEFAULT_KEYBINDINGS.edit.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::SearchCollection.into()); .push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.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(),
);
self.app.data.radarr_data.edit_collection_modal = self.app.data.radarr_data.edit_collection_modal =
Some((&self.app.data.radarr_data).into()); Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block = 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 self
.app .app
.push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true; 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 => { 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 = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateCollections); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateCollections);
@@ -1,6 +1,5 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_str_eq;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
@@ -11,109 +10,6 @@ mod tests {
use crate::models::radarr_models::DownloadRecord; use crate::models::radarr_models::DownloadRecord;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLOADS_BLOCKS}; 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 { mod test_handle_delete {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@@ -130,11 +26,11 @@ mod tests {
.downloads .downloads
.set_items(vec![DownloadRecord::default()]); .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!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::DeleteDownloadPrompt.into() ActiveRadarrBlock::DeleteDownloadPrompt.into()
); );
} }
@@ -149,12 +45,9 @@ mod tests {
.downloads .downloads
.set_items(vec![DownloadRecord::default()]); .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!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
} }
} }
@@ -171,20 +64,20 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(2); app.data.radarr_data.main_tabs.set_index(2);
DownloadsHandler::with( DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Collections.into() ActiveRadarrBlock::Collections.into()
); );
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::Collections.into() ActiveRadarrBlock::Collections.into()
); );
} }
@@ -195,21 +88,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(2); app.data.radarr_data.main_tabs.set_index(2);
DownloadsHandler::with( DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Blocklist.into() ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
); );
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
} }
#[rstest] #[rstest]
@@ -223,11 +113,11 @@ mod tests {
) { ) {
let mut app = App::default(); 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); 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); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -269,14 +159,14 @@ mod tests {
app.push_navigation_stack(base_route.into()); app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.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!(app.data.radarr_data.prompt_confirm);
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(expected_action) Some(expected_action)
); );
assert_eq!(app.get_current_route(), &base_route.into()); assert_eq!(app.get_current_route(), base_route.into());
} }
#[rstest] #[rstest]
@@ -295,11 +185,11 @@ mod tests {
app.push_navigation_stack(base_route.into()); app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.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!(!app.data.radarr_data.prompt_confirm);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); 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 +213,9 @@ mod tests {
app.push_navigation_stack(prompt_block.into()); app.push_navigation_stack(prompt_block.into());
app.data.radarr_data.prompt_confirm = true; 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); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -337,12 +227,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
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!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert!(app.error.text.is_empty()); assert!(app.error.text.is_empty());
} }
} }
@@ -365,16 +252,16 @@ mod tests {
.set_items(vec![DownloadRecord::default()]); .set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with( DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.update.key, DEFAULT_KEYBINDINGS.update.key,
&mut app, &mut app,
&ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::UpdateDownloadsPrompt.into() ActiveRadarrBlock::UpdateDownloadsPrompt.into()
); );
} }
@@ -390,17 +277,14 @@ mod tests {
.set_items(vec![DownloadRecord::default()]); .set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with( DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.update.key, DEFAULT_KEYBINDINGS.update.key,
&mut app, &mut app,
&ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
} }
#[test] #[test]
@@ -414,17 +298,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
DownloadsHandler::with( DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert!(app.should_refresh); assert!(app.should_refresh);
} }
@@ -440,17 +321,14 @@ mod tests {
.set_items(vec![DownloadRecord::default()]); .set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with( DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert!(!app.should_refresh); assert!(!app.should_refresh);
} }
@@ -480,10 +358,10 @@ mod tests {
app.push_navigation_stack(prompt_block.into()); app.push_navigation_stack(prompt_block.into());
DownloadsHandler::with( DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key, DEFAULT_KEYBINDINGS.confirm.key,
&mut app, &mut app,
&prompt_block, prompt_block,
&None, None,
) )
.handle(); .handle();
@@ -492,7 +370,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(expected_action) Some(expected_action)
); );
assert_eq!(app.get_current_route(), &base_route.into()); assert_eq!(app.get_current_route(), base_route.into());
} }
} }
@@ -500,9 +378,9 @@ mod tests {
fn test_downloads_handler_accepts() { fn test_downloads_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if DOWNLOADS_BLOCKS.contains(&active_radarr_block) { if DOWNLOADS_BLOCKS.contains(&active_radarr_block) {
assert!(DownloadsHandler::accepts(&active_radarr_block)); assert!(DownloadsHandler::accepts(active_radarr_block));
} else { } else {
assert!(!DownloadsHandler::accepts(&active_radarr_block)); assert!(!DownloadsHandler::accepts(active_radarr_block));
} }
}) })
} }
@@ -513,10 +391,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = DownloadsHandler::with( let handler = DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -528,10 +406,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = DownloadsHandler::with( let handler = DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -548,10 +426,10 @@ mod tests {
.downloads .downloads
.set_items(vec![DownloadRecord::default()]); .set_items(vec![DownloadRecord::default()]);
let handler = DownloadsHandler::with( let handler = DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
+39 -35
View File
@@ -1,10 +1,12 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key; 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::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler}; 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::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLOADS_BLOCKS};
use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
#[cfg(test)] #[cfg(test)]
@@ -12,22 +14,40 @@ use crate::network::radarr_network::RadarrEvent;
mod downloads_handler_tests; mod downloads_handler_tests;
pub(super) struct DownloadsHandler<'a, 'b> { pub(super) struct DownloadsHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> DownloadsHandler<'a, 'b> {
handle_table_events!(
self,
downloads,
self.app.data.radarr_data.downloads,
DownloadRecord
);
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn handle(&mut self) {
DOWNLOADS_BLOCKS.contains(active_block) 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( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
) -> DownloadsHandler<'a, 'b> { ) -> DownloadsHandler<'a, 'b> {
DownloadsHandler { DownloadsHandler {
key, key,
@@ -37,7 +57,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -45,32 +65,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
!self.app.is_loading && !self.app.data.radarr_data.downloads.is_empty() !self.app.is_loading && !self.app.data.radarr_data.downloads.is_empty()
} }
fn handle_scroll_up(&mut self) { fn handle_scroll_up(&mut self) {}
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
self.app.data.radarr_data.downloads.scroll_up()
}
}
fn handle_scroll_down(&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_home(&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_end(&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_delete(&mut self) { fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Downloads { if self.active_radarr_block == ActiveRadarrBlock::Downloads {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::DeleteDownloadPrompt.into()) .push_navigation_stack(ActiveRadarrBlock::DeleteDownloadPrompt.into())
@@ -121,18 +125,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
let key = self.key; let key = self.key;
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::Downloads => match self.key { ActiveRadarrBlock::Downloads => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.update.key => { _ if key == DEFAULT_KEYBINDINGS.update.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::UpdateDownloadsPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::UpdateDownloadsPrompt.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true; self.app.should_refresh = true;
} }
_ => (), _ => (),
}, },
ActiveRadarrBlock::DeleteDownloadPrompt => { 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 = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload(None)); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload(None));
@@ -140,7 +144,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
} }
} }
ActiveRadarrBlock::UpdateDownloadsPrompt => { 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 = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateDownloads); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateDownloads);
@@ -4,29 +4,29 @@ use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
use crate::network::radarr_network::RadarrEvent; 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)] #[cfg(test)]
#[path = "edit_indexer_handler_tests.rs"] #[path = "edit_indexer_handler_tests.rs"]
mod edit_indexer_handler_tests; mod edit_indexer_handler_tests;
pub(super) struct EditIndexerHandler<'a, 'b> { pub(super) struct EditIndexerHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
EDIT_INDEXER_BLOCKS.contains(active_block) EDIT_INDEXER_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
) -> EditIndexerHandler<'a, 'b> { ) -> EditIndexerHandler<'a, 'b> {
EditIndexerHandler { EditIndexerHandler {
key, key,
@@ -36,7 +36,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -45,14 +45,42 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
} }
fn handle_scroll_up(&mut self) { fn handle_scroll_up(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::EditIndexerPrompt { match self.active_radarr_block {
self.app.data.radarr_data.selected_block.previous(); 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) { fn handle_scroll_down(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::EditIndexerPrompt { match self.active_radarr_block {
self.app.data.radarr_data.selected_block.next(); 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 +211,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
fn handle_left_right_action(&mut self) { fn handle_left_right_action(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::EditIndexerPrompt => { ActiveRadarrBlock::EditIndexerPrompt => {
if self.app.data.radarr_data.selected_block.get_active_block() handle_prompt_left_right_keys!(
== &ActiveRadarrBlock::EditIndexerConfirmPrompt self,
{ ActiveRadarrBlock::EditIndexerConfirmPrompt,
handle_prompt_toggle(self.app, self.key); radarr_data
} 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;
}
} }
ActiveRadarrBlock::EditIndexerNameInput => { ActiveRadarrBlock::EditIndexerNameInput => {
handle_text_box_left_right_keys!( handle_text_box_left_right_keys!(
@@ -270,7 +294,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
fn handle_submit(&mut self) { fn handle_submit(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::EditIndexerPrompt => { 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 { match selected_block {
ActiveRadarrBlock::EditIndexerConfirmPrompt => { ActiveRadarrBlock::EditIndexerConfirmPrompt => {
let radarr_data = &mut self.app.data.radarr_data; let radarr_data = &mut self.app.data.radarr_data;
@@ -291,6 +315,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
self.app.push_navigation_stack(selected_block.into()); self.app.push_navigation_stack(selected_block.into());
self.app.should_ignore_quit_key = true; self.app.should_ignore_quit_key = true;
} }
ActiveRadarrBlock::EditIndexerPriorityInput => self
.app
.push_navigation_stack(ActiveRadarrBlock::EditIndexerPriorityInput.into()),
ActiveRadarrBlock::EditIndexerToggleEnableRss => { ActiveRadarrBlock::EditIndexerToggleEnableRss => {
let indexer = self let indexer = self
.app .app
@@ -334,6 +361,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false; self.app.should_ignore_quit_key = false;
} }
ActiveRadarrBlock::EditIndexerPriorityInput => self.app.pop_navigation_stack(),
_ => (), _ => (),
} }
} }
@@ -349,6 +377,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
| ActiveRadarrBlock::EditIndexerUrlInput | ActiveRadarrBlock::EditIndexerUrlInput
| ActiveRadarrBlock::EditIndexerApiKeyInput | ActiveRadarrBlock::EditIndexerApiKeyInput
| ActiveRadarrBlock::EditIndexerSeedRatioInput | ActiveRadarrBlock::EditIndexerSeedRatioInput
| ActiveRadarrBlock::EditIndexerPriorityInput
| ActiveRadarrBlock::EditIndexerTagsInput => { | ActiveRadarrBlock::EditIndexerTagsInput => {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false; self.app.should_ignore_quit_key = false;
@@ -431,8 +460,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
} }
ActiveRadarrBlock::EditIndexerPrompt => { ActiveRadarrBlock::EditIndexerPrompt => {
if self.app.data.radarr_data.selected_block.get_active_block() if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::EditIndexerConfirmPrompt == ActiveRadarrBlock::EditIndexerConfirmPrompt
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key && self.key == DEFAULT_KEYBINDINGS.confirm.key
{ {
self.app.data.radarr_data.prompt_confirm = true; 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(None));
File diff suppressed because it is too large Load Diff
@@ -6,29 +6,29 @@ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS, ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS,
}; };
use crate::network::radarr_network::RadarrEvent; 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)] #[cfg(test)]
#[path = "edit_indexer_settings_handler_tests.rs"] #[path = "edit_indexer_settings_handler_tests.rs"]
mod edit_indexer_settings_handler_tests; mod edit_indexer_settings_handler_tests;
pub(super) struct IndexerSettingsHandler<'a, 'b> { pub(super) struct IndexerSettingsHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
INDEXER_SETTINGS_BLOCKS.contains(active_block) INDEXER_SETTINGS_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
) -> IndexerSettingsHandler<'a, 'b> { ) -> IndexerSettingsHandler<'a, 'b> {
IndexerSettingsHandler { IndexerSettingsHandler {
key, key,
@@ -38,7 +38,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -50,7 +50,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap(); let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::AllIndexerSettingsPrompt => { ActiveRadarrBlock::AllIndexerSettingsPrompt => {
self.app.data.radarr_data.selected_block.previous(); self.app.data.radarr_data.selected_block.up();
} }
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => { ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
indexer_settings.minimum_age += 1; indexer_settings.minimum_age += 1;
@@ -75,7 +75,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap(); let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::AllIndexerSettingsPrompt => { ActiveRadarrBlock::AllIndexerSettingsPrompt => {
self.app.data.radarr_data.selected_block.next() self.app.data.radarr_data.selected_block.down()
} }
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => { ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
if indexer_settings.minimum_age > 0 { if indexer_settings.minimum_age > 0 {
@@ -105,7 +105,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
} }
fn handle_home(&mut self) { fn handle_home(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput { if self.active_radarr_block == ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
self self
.app .app
.data .data
@@ -119,7 +119,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
} }
fn handle_end(&mut self) { fn handle_end(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput { if self.active_radarr_block == ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
self self
.app .app
.data .data
@@ -137,15 +137,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
fn handle_left_right_action(&mut self) { fn handle_left_right_action(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::AllIndexerSettingsPrompt => { ActiveRadarrBlock::AllIndexerSettingsPrompt => {
if self.app.data.radarr_data.selected_block.get_active_block() handle_prompt_left_right_keys!(
== &ActiveRadarrBlock::IndexerSettingsConfirmPrompt self,
{ ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
handle_prompt_toggle(self.app, self.key); radarr_data
} 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;
}
} }
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput => { ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput => {
handle_text_box_left_right_keys!( handle_text_box_left_right_keys!(
@@ -187,7 +183,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
| ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput => { | ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput => {
self.app.push_navigation_stack( 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, None,
) )
.into(), .into(),
@@ -258,8 +254,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
} }
ActiveRadarrBlock::AllIndexerSettingsPrompt => { ActiveRadarrBlock::AllIndexerSettingsPrompt => {
if self.app.data.radarr_data.selected_block.get_active_block() if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::IndexerSettingsConfirmPrompt == ActiveRadarrBlock::IndexerSettingsConfirmPrompt
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key && self.key == DEFAULT_KEYBINDINGS.confirm.key
{ {
self.app.data.radarr_data.prompt_confirm = true; self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = self.app.data.radarr_data.prompt_confirm_action =
@@ -27,7 +27,7 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::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 { if $key == Key::Up {
assert_eq!( assert_eq!(
@@ -64,7 +64,7 @@ mod tests {
0 0
); );
IndexerSettingsHandler::with(&Key::Up, &mut app, &$block, &None).handle(); IndexerSettingsHandler::with(Key::Up, &mut app, $block, None).handle();
assert_eq!( assert_eq!(
app app
@@ -77,7 +77,7 @@ mod tests {
1 1
); );
IndexerSettingsHandler::with(&$key, &mut app, &$block, &None).handle(); IndexerSettingsHandler::with($key, &mut app, $block, None).handle();
assert_eq!( assert_eq!(
app app
.data .data
@@ -98,26 +98,26 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next(); app.data.radarr_data.selected_block.down();
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
if key == Key::Up { if key == Key::Up {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::IndexerSettingsMinimumAgeInput ActiveRadarrBlock::IndexerSettingsMinimumAgeInput
); );
} else { } else {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::IndexerSettingsMaximumSizeInput ActiveRadarrBlock::IndexerSettingsMaximumSizeInput
); );
} }
} }
@@ -130,20 +130,20 @@ mod tests {
app.is_loading = true; app.is_loading = true;
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next(); app.data.radarr_data.selected_block.down();
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::IndexerSettingsRetentionInput ActiveRadarrBlock::IndexerSettingsRetentionInput
); );
} }
@@ -218,10 +218,10 @@ mod tests {
}); });
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput, ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -239,10 +239,10 @@ mod tests {
); );
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput, ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -276,24 +276,24 @@ mod tests {
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) { fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.index = INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1; app.data.radarr_data.selected_block.y = INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1;
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert!(app.data.radarr_data.prompt_confirm); assert!(app.data.radarr_data.prompt_confirm);
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
@@ -323,44 +323,44 @@ mod tests {
)] )]
fn test_left_right_block_toggle( fn test_left_right_block_toggle(
#[values(Key::Left, Key::Right)] key: Key, #[values(Key::Left, Key::Right)] key: Key,
#[case] starting_index: usize, #[case] starting_y_index: usize,
#[case] left_block: ActiveRadarrBlock, #[case] left_block: ActiveRadarrBlock,
#[case] right_block: ActiveRadarrBlock, #[case] right_block: ActiveRadarrBlock,
) { ) {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.index = starting_index; app.data.radarr_data.selected_block.y = starting_y_index;
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&left_block left_block
); );
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&right_block right_block
); );
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&left_block left_block
); );
} }
@@ -373,10 +373,10 @@ mod tests {
}); });
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput, ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -394,10 +394,10 @@ mod tests {
); );
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput, ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -438,23 +438,23 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .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()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .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_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh); assert!(!app.should_refresh);
assert_eq!(app.data.radarr_data.indexer_settings, None); assert_eq!(app.data.radarr_data.indexer_settings, None);
@@ -466,24 +466,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .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()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditAllIndexerSettings(None)) Some(RadarrEvent::EditAllIndexerSettings(None))
@@ -502,71 +502,80 @@ mod tests {
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into() ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
); );
assert!(!app.should_refresh); assert!(!app.should_refresh);
} }
#[rstest] #[rstest]
#[case(ActiveRadarrBlock::IndexerSettingsMinimumAgeInput, 0)] #[case(ActiveRadarrBlock::IndexerSettingsMinimumAgeInput, 0, 0)]
#[case(ActiveRadarrBlock::IndexerSettingsRetentionInput, 1)] #[case(ActiveRadarrBlock::IndexerSettingsRetentionInput, 1, 0)]
#[case(ActiveRadarrBlock::IndexerSettingsMaximumSizeInput, 2)] #[case(ActiveRadarrBlock::IndexerSettingsMaximumSizeInput, 2, 0)]
#[case(ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput, 5)] #[case(ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput, 0, 1)]
#[case(ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput, 6)] #[case(ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput, 1, 1)]
fn test_edit_indexer_settings_prompt_submit_selected_block( fn test_edit_indexer_settings_prompt_submit_selected_block(
#[case] selected_block: ActiveRadarrBlock, #[case] selected_block: ActiveRadarrBlock,
#[case] index: usize, #[case] y_index: usize,
#[case] x_index: usize,
) { ) {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block = 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(index); app
.data
.radarr_data
.selected_block
.set_index(x_index, y_index);
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &selected_block.into()); assert_eq!(app.get_current_route(), selected_block.into());
} }
#[rstest] #[rstest]
fn test_edit_indexer_settings_prompt_submit_selected_block_no_op_when_not_ready( 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(); let mut app = App::default();
app.is_loading = true; app.is_loading = true;
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block = 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(index); app
.data
.radarr_data
.selected_block
.set_index(index.1, index.0);
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into() ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
); );
} }
@@ -576,20 +585,20 @@ mod tests {
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block = 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(7); app.data.radarr_data.selected_block.set_index(1, 2);
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput.into() ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput.into()
); );
assert!(app.should_ignore_quit_key); assert!(app.should_ignore_quit_key);
} }
@@ -599,21 +608,21 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.selected_block = 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(3); app.data.radarr_data.selected_block.set_index(0, 3);
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into() ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
); );
assert!( assert!(
app app
@@ -626,16 +635,16 @@ mod tests {
); );
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into() ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
); );
assert!( assert!(
!app !app
@@ -653,21 +662,21 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.selected_block = 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(8); app.data.radarr_data.selected_block.set_index(1, 3);
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into() ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
); );
assert!( assert!(
app app
@@ -680,16 +689,16 @@ mod tests {
); );
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into() ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
); );
assert!( assert!(
!app !app
@@ -716,10 +725,10 @@ mod tests {
); );
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput, ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -735,7 +744,7 @@ mod tests {
.is_empty()); .is_empty());
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into() ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
); );
} }
@@ -755,11 +764,11 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.push_navigation_stack(active_radarr_block.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!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into() ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
); );
} }
} }
@@ -783,14 +792,14 @@ mod tests {
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .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.prompt_confirm);
assert_eq!(app.data.radarr_data.indexer_settings, None); assert_eq!(app.data.radarr_data.indexer_settings, None);
} }
@@ -806,14 +815,14 @@ mod tests {
app.should_ignore_quit_key = true; app.should_ignore_quit_key = true;
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput, ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None, None,
) )
.handle(); .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!(!app.should_ignore_quit_key);
assert_eq!( assert_eq!(
app.data.radarr_data.indexer_settings, app.data.radarr_data.indexer_settings,
@@ -838,9 +847,9 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into()); app.push_navigation_stack(active_radarr_block.into());
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); 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!( assert_eq!(
app.data.radarr_data.indexer_settings, app.data.radarr_data.indexer_settings,
Some(IndexerSettings::default()) Some(IndexerSettings::default())
@@ -870,10 +879,10 @@ mod tests {
}); });
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key, DEFAULT_KEYBINDINGS.backspace.key,
&mut app, &mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput, ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -896,10 +905,10 @@ mod tests {
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&Key::Char('h'), Key::Char('h'),
&mut app, &mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput, ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -922,23 +931,23 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .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()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with( IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key, DEFAULT_KEYBINDINGS.confirm.key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditAllIndexerSettings(None)) Some(RadarrEvent::EditAllIndexerSettings(None))
@@ -952,9 +961,9 @@ mod tests {
fn test_indexer_settings_handler_accepts() { fn test_indexer_settings_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if INDEXER_SETTINGS_BLOCKS.contains(&active_radarr_block) { if INDEXER_SETTINGS_BLOCKS.contains(&active_radarr_block) {
assert!(IndexerSettingsHandler::accepts(&active_radarr_block)); assert!(IndexerSettingsHandler::accepts(active_radarr_block));
} else { } else {
assert!(!IndexerSettingsHandler::accepts(&active_radarr_block)); assert!(!IndexerSettingsHandler::accepts(active_radarr_block));
} }
}) })
} }
@@ -965,10 +974,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = IndexerSettingsHandler::with( let handler = IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -980,10 +989,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = IndexerSettingsHandler::with( let handler = IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -996,10 +1005,10 @@ mod tests {
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
let handler = IndexerSettingsHandler::with( let handler = IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt, ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
@@ -1,6 +1,5 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest; use rstest::rstest;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@@ -15,107 +14,6 @@ mod tests {
use crate::models::servarr_models::Indexer; use crate::models::servarr_models::Indexer;
use crate::test_handler_delegation; 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 { mod test_handle_delete {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@@ -132,11 +30,11 @@ mod tests {
.indexers .indexers
.set_items(vec![Indexer::default()]); .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!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::DeleteIndexerPrompt.into() ActiveRadarrBlock::DeleteIndexerPrompt.into()
); );
} }
@@ -151,9 +49,9 @@ mod tests {
.indexers .indexers
.set_items(vec![Indexer::default()]); .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 +68,20 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(5); app.data.radarr_data.main_tabs.set_index(5);
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
} }
@@ -194,18 +92,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(5); app.data.radarr_data.main_tabs.set_index(5);
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), 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] #[rstest]
@@ -214,23 +112,11 @@ mod tests {
) { ) {
let mut app = App::default(); let mut app = App::default();
IndexersHandler::with( IndexersHandler::with(key, &mut app, ActiveRadarrBlock::DeleteIndexerPrompt, None).handle();
&key,
&mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt,
&None,
)
.handle();
assert!(app.data.radarr_data.prompt_confirm); assert!(app.data.radarr_data.prompt_confirm);
IndexersHandler::with( IndexersHandler::with(key, &mut app, ActiveRadarrBlock::DeleteIndexerPrompt, None).handle();
&key,
&mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt,
&None,
)
.handle();
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -306,11 +192,11 @@ mod tests {
radarr_data.indexers.set_items(vec![indexer]); radarr_data.indexers.set_items(vec![indexer]);
app.data.radarr_data = radarr_data; 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!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditIndexerPrompt.into() ActiveRadarrBlock::EditIndexerPrompt.into()
); );
assert_eq!( assert_eq!(
app.data.radarr_data.edit_indexer_modal, app.data.radarr_data.edit_indexer_modal,
@@ -323,12 +209,12 @@ mod tests {
if torrent_protocol { if torrent_protocol {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.blocks, app.data.radarr_data.selected_block.blocks,
&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS EDIT_INDEXER_TORRENT_SELECTION_BLOCKS
); );
} else { } else {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.blocks, app.data.radarr_data.selected_block.blocks,
&EDIT_INDEXER_NZB_SELECTION_BLOCKS EDIT_INDEXER_NZB_SELECTION_BLOCKS
); );
} }
} }
@@ -344,9 +230,9 @@ mod tests {
.indexers .indexers
.set_items(vec![Indexer::default()]); .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); assert_eq!(app.data.radarr_data.edit_indexer_modal, None);
} }
@@ -363,10 +249,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with( IndexersHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt, ActiveRadarrBlock::DeleteIndexerPrompt,
&None, None,
) )
.handle(); .handle();
@@ -375,7 +261,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteIndexer(None)) Some(RadarrEvent::DeleteIndexer(None))
); );
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
} }
#[test] #[test]
@@ -390,16 +276,16 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with( IndexersHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt, ActiveRadarrBlock::DeleteIndexerPrompt,
&None, None,
) )
.handle(); .handle();
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); 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 +305,14 @@ mod tests {
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
IndexersHandler::with( IndexersHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt, ActiveRadarrBlock::DeleteIndexerPrompt,
&None, None,
) )
.handle(); .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.prompt_confirm);
} }
@@ -434,14 +320,14 @@ mod tests {
fn test_test_indexer_esc(#[values(true, false)] is_ready: bool) { fn test_test_indexer_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default(); let mut app = App::default();
app.is_loading = is_ready; 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::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::TestIndexer.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.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!(app.data.radarr_data.indexer_test_error, None); assert_eq!(app.data.radarr_data.indexer_test_errors, None);
} }
#[rstest] #[rstest]
@@ -452,9 +338,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
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()); assert!(app.error.text.is_empty());
} }
} }
@@ -469,51 +355,6 @@ mod tests {
use super::*; 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] #[test]
fn test_refresh_indexers_key() { fn test_refresh_indexers_key() {
let mut app = App::default(); let mut app = App::default();
@@ -525,14 +366,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(app.should_refresh); assert!(app.should_refresh);
} }
@@ -548,14 +389,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(!app.should_refresh); assert!(!app.should_refresh);
} }
@@ -569,20 +410,20 @@ mod tests {
.set_items(vec![Indexer::default()]); .set_items(vec![Indexer::default()]);
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.settings.key, DEFAULT_KEYBINDINGS.settings.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into() ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
); );
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.blocks, app.data.radarr_data.selected_block.blocks,
&INDEXER_SETTINGS_SELECTION_BLOCKS INDEXER_SETTINGS_SELECTION_BLOCKS
); );
} }
@@ -598,14 +439,14 @@ mod tests {
.set_items(vec![Indexer::default()]); .set_items(vec![Indexer::default()]);
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.settings.key, DEFAULT_KEYBINDINGS.settings.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
} }
#[test] #[test]
@@ -618,16 +459,16 @@ mod tests {
.set_items(vec![Indexer::default()]); .set_items(vec![Indexer::default()]);
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.test.key, DEFAULT_KEYBINDINGS.test.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::TestIndexer.into() ActiveRadarrBlock::TestIndexer.into()
); );
} }
@@ -643,14 +484,14 @@ mod tests {
.set_items(vec![Indexer::default()]); .set_items(vec![Indexer::default()]);
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.test.key, DEFAULT_KEYBINDINGS.test.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
} }
#[test] #[test]
@@ -663,16 +504,16 @@ mod tests {
.set_items(vec![Indexer::default()]); .set_items(vec![Indexer::default()]);
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.test_all.key, DEFAULT_KEYBINDINGS.test_all.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::TestAllIndexers.into() ActiveRadarrBlock::TestAllIndexers.into()
); );
} }
@@ -688,14 +529,14 @@ mod tests {
.set_items(vec![Indexer::default()]); .set_items(vec![Indexer::default()]);
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.test_all.key, DEFAULT_KEYBINDINGS.test_all.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
} }
#[test] #[test]
@@ -710,10 +551,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with( IndexersHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key, DEFAULT_KEYBINDINGS.confirm.key,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt, ActiveRadarrBlock::DeleteIndexerPrompt,
&None, None,
) )
.handle(); .handle();
@@ -722,7 +563,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteIndexer(None)) Some(RadarrEvent::DeleteIndexer(None))
); );
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
} }
} }
@@ -791,9 +632,9 @@ mod tests {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if indexers_blocks.contains(&active_radarr_block) { if indexers_blocks.contains(&active_radarr_block) {
assert!(IndexersHandler::accepts(&active_radarr_block)); assert!(IndexersHandler::accepts(active_radarr_block));
} else { } else {
assert!(!IndexersHandler::accepts(&active_radarr_block)); assert!(!IndexersHandler::accepts(active_radarr_block));
} }
}) })
} }
@@ -804,10 +645,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = IndexersHandler::with( let handler = IndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -819,10 +660,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = IndexersHandler::with( let handler = IndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -839,10 +680,10 @@ mod tests {
.set_items(vec![Indexer::default()]); .set_items(vec![Indexer::default()]);
let handler = IndexersHandler::with( let handler = IndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Indexers,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
+48 -58
View File
@@ -1,17 +1,19 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key; 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::handle_change_tab_left_right_keys;
use crate::handlers::radarr_handlers::indexers::edit_indexer_handler::EditIndexerHandler; 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::edit_indexer_settings_handler::IndexerSettingsHandler;
use crate::handlers::radarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler; 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::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, ActiveRadarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
INDEXERS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS,
}; };
use crate::models::servarr_models::Indexer;
use crate::models::BlockSelectionState; use crate::models::BlockSelectionState;
use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
mod edit_indexer_handler; mod edit_indexer_handler;
@@ -23,43 +25,52 @@ mod test_all_indexers_handler;
mod indexers_handler_tests; mod indexers_handler_tests;
pub(super) struct IndexersHandler<'a, 'b> { pub(super) struct IndexersHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> IndexersHandler<'a, 'b> {
handle_table_events!(self, indexers, self.app.data.radarr_data.indexers, Indexer);
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, 'b> {
fn handle(&mut self) { fn handle(&mut self) {
match self.active_radarr_block { let indexer_table_handling_config =
_ if EditIndexerHandler::accepts(self.active_radarr_block) => { TableHandlingConfig::new(ActiveRadarrBlock::Indexers.into());
EditIndexerHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle() 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)
.handle()
}
_ if IndexerSettingsHandler::accepts(self.active_radarr_block) => {
IndexerSettingsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle()
}
_ if TestAllIndexersHandler::accepts(self.active_radarr_block) => {
TestAllIndexersHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle()
}
_ => self.handle_key_event(),
} }
_ if IndexerSettingsHandler::accepts(self.active_radarr_block) => {
IndexerSettingsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle()
}
_ if TestAllIndexersHandler::accepts(self.active_radarr_block) => {
TestAllIndexersHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle()
}
_ => self.handle_key_event(),
} }
} }
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
EditIndexerHandler::accepts(active_block) EditIndexerHandler::accepts(active_block)
|| IndexerSettingsHandler::accepts(active_block) || IndexerSettingsHandler::accepts(active_block)
|| TestAllIndexersHandler::accepts(active_block) || TestAllIndexersHandler::accepts(active_block)
|| INDEXERS_BLOCKS.contains(active_block) || INDEXERS_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> IndexersHandler<'a, 'b> { ) -> IndexersHandler<'a, 'b> {
IndexersHandler { IndexersHandler {
key, key,
@@ -69,7 +80,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -77,32 +88,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
!self.app.is_loading && !self.app.data.radarr_data.indexers.is_empty() !self.app.is_loading && !self.app.data.radarr_data.indexers.is_empty()
} }
fn handle_scroll_up(&mut self) { fn handle_scroll_up(&mut self) {}
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
self.app.data.radarr_data.indexers.scroll_up();
}
}
fn handle_scroll_down(&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_home(&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_end(&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_delete(&mut self) { fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Indexers { if self.active_radarr_block == ActiveRadarrBlock::Indexers {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
@@ -141,10 +136,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
.protocol; .protocol;
if protocol == "torrent" { if protocol == "torrent" {
self.app.data.radarr_data.selected_block = self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_INDEXER_TORRENT_SELECTION_BLOCKS);
} else { } else {
self.app.data.radarr_data.selected_block = self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_INDEXER_NZB_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_INDEXER_NZB_SELECTION_BLOCKS);
} }
} }
_ => (), _ => (),
@@ -159,7 +154,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
} }
ActiveRadarrBlock::TestIndexer => { ActiveRadarrBlock::TestIndexer => {
self.app.pop_navigation_stack(); 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), _ => handle_clear_errors(self.app),
} }
@@ -169,35 +164,30 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
let key = self.key; let key = self.key;
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::Indexers => match self.key { ActiveRadarrBlock::Indexers => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.add.key => { _ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::AddIndexer.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true; self.app.should_refresh = true;
} }
_ if *key == DEFAULT_KEYBINDINGS.test.key => { _ if key == DEFAULT_KEYBINDINGS.test.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::TestIndexer.into()); .push_navigation_stack(ActiveRadarrBlock::TestIndexer.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.test_all.key => { _ if key == DEFAULT_KEYBINDINGS.test_all.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::TestAllIndexers.into()); .push_navigation_stack(ActiveRadarrBlock::TestAllIndexers.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.settings.key => { _ if key == DEFAULT_KEYBINDINGS.settings.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
self.app.data.radarr_data.selected_block = self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
} }
_ => (), _ => (),
}, },
ActiveRadarrBlock::DeleteIndexerPrompt => { 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 = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(None)); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(None));
@@ -1,30 +1,58 @@
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::KeyEventHandler; use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::Scrollable;
#[cfg(test)] #[cfg(test)]
#[path = "test_all_indexers_handler_tests.rs"] #[path = "test_all_indexers_handler_tests.rs"]
mod test_all_indexers_handler_tests; mod test_all_indexers_handler_tests;
pub(super) struct TestAllIndexersHandler<'a, 'b> { pub(super) struct TestAllIndexersHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<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> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn handle(&mut self) {
active_block == &ActiveRadarrBlock::TestAllIndexers 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( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
) -> TestAllIndexersHandler<'a, 'b> { ) -> TestAllIndexersHandler<'a, 'b> {
TestAllIndexersHandler { TestAllIndexersHandler {
key, 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 self.key
} }
@@ -48,57 +76,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
!self.app.is_loading && table_is_ready !self.app.is_loading && table_is_ready
} }
fn handle_scroll_up(&mut self) { 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_down(&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_home(&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_end(&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_delete(&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_submit(&mut self) {}
fn handle_esc(&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.pop_navigation_stack();
self.app.data.radarr_data.indexer_test_all_results = None; self.app.data.radarr_data.indexer_test_all_results = None;
} }
@@ -2,7 +2,6 @@
mod tests { mod tests {
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler; use crate::handlers::radarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler;
use crate::handlers::KeyEventHandler; use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::modals::IndexerTestResultModalItem; use crate::models::servarr_data::modals::IndexerTestResultModalItem;
@@ -10,220 +9,6 @@ mod tests {
use crate::models::stateful_table::StatefulTable; use crate::models::stateful_table::StatefulTable;
use strum::IntoEnumIterator; 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 { mod test_handle_esc {
use super::*; use super::*;
use crate::models::stateful_table::StatefulTable; use crate::models::stateful_table::StatefulTable;
@@ -239,14 +24,14 @@ mod tests {
app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default()); app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default());
TestAllIndexersHandler::with( TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::TestAllIndexers, ActiveRadarrBlock::TestAllIndexers,
&None, None,
) )
.handle(); .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.prompt_confirm);
assert!(app.data.radarr_data.indexer_test_all_results.is_none()); assert!(app.data.radarr_data.indexer_test_all_results.is_none());
} }
@@ -256,9 +41,9 @@ mod tests {
fn test_test_all_indexers_handler_accepts() { fn test_test_all_indexers_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if active_radarr_block == ActiveRadarrBlock::TestAllIndexers { if active_radarr_block == ActiveRadarrBlock::TestAllIndexers {
assert!(TestAllIndexersHandler::accepts(&active_radarr_block)); assert!(TestAllIndexersHandler::accepts(active_radarr_block));
} else { } else {
assert!(!TestAllIndexersHandler::accepts(&active_radarr_block)); assert!(!TestAllIndexersHandler::accepts(active_radarr_block));
} }
}); });
} }
@@ -269,10 +54,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = TestAllIndexersHandler::with( let handler = TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::TestAllIndexers, ActiveRadarrBlock::TestAllIndexers,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -284,10 +69,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = TestAllIndexersHandler::with( let handler = TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::TestAllIndexers, ActiveRadarrBlock::TestAllIndexers,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -300,10 +85,10 @@ mod tests {
app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default()); app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default());
let handler = TestAllIndexersHandler::with( let handler = TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::TestAllIndexers, ActiveRadarrBlock::TestAllIndexers,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -318,10 +103,10 @@ mod tests {
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results); app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
let handler = TestAllIndexersHandler::with( let handler = TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::TestAllIndexers, ActiveRadarrBlock::TestAllIndexers,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
@@ -1,33 +1,59 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::AddMovieSearchResult;
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS, ActiveRadarrBlock, ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS,
}; };
use crate::models::{BlockSelectionState, Scrollable}; use crate::models::{BlockSelectionState, Scrollable};
use crate::network::radarr_network::RadarrEvent; 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)] #[cfg(test)]
#[path = "add_movie_handler_tests.rs"] #[path = "add_movie_handler_tests.rs"]
mod add_movie_handler_tests; mod add_movie_handler_tests;
pub(super) struct AddMovieHandler<'a, 'b> { pub(super) struct AddMovieHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
context: &'a Option<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(),
AddMovieSearchResult
);
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn handle(&mut self) {
ADD_MOVIE_BLOCKS.contains(active_block) 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( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> AddMovieHandler<'a, 'b> { ) -> AddMovieHandler<'a, 'b> {
AddMovieHandler { AddMovieHandler {
key, key,
@@ -37,7 +63,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -47,14 +73,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
fn handle_scroll_up(&mut self) { fn handle_scroll_up(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::AddMovieSearchResults => self
.app
.data
.radarr_data
.add_searched_movies
.as_mut()
.unwrap()
.scroll_up(),
ActiveRadarrBlock::AddMovieSelectMonitor => self ActiveRadarrBlock::AddMovieSelectMonitor => self
.app .app
.data .data
@@ -91,21 +109,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
.unwrap() .unwrap()
.root_folder_list .root_folder_list
.scroll_up(), .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) { fn handle_scroll_down(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::AddMovieSearchResults => self
.app
.data
.radarr_data
.add_searched_movies
.as_mut()
.unwrap()
.scroll_down(),
ActiveRadarrBlock::AddMovieSelectMonitor => self ActiveRadarrBlock::AddMovieSelectMonitor => self
.app .app
.data .data
@@ -142,21 +152,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
.unwrap() .unwrap()
.root_folder_list .root_folder_list
.scroll_down(), .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) { fn handle_home(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::AddMovieSearchResults => self
.app
.data
.radarr_data
.add_searched_movies
.as_mut()
.unwrap()
.scroll_to_top(),
ActiveRadarrBlock::AddMovieSelectMonitor => self ActiveRadarrBlock::AddMovieSelectMonitor => self
.app .app
.data .data
@@ -216,14 +218,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
fn handle_end(&mut self) { fn handle_end(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::AddMovieSearchResults => self
.app
.data
.radarr_data
.add_searched_movies
.as_mut()
.unwrap()
.scroll_to_bottom(),
ActiveRadarrBlock::AddMovieSelectMonitor => self ActiveRadarrBlock::AddMovieSelectMonitor => self
.app .app
.data .data
@@ -313,7 +307,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
fn handle_submit(&mut self) { fn handle_submit(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
_ if *self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchInput _ if self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchInput
&& !self && !self
.app .app
.data .data
@@ -329,7 +323,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchResults.into()); .push_navigation_stack(ActiveRadarrBlock::AddMovieSearchResults.into());
self.app.should_ignore_quit_key = false; 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() => && self.app.data.radarr_data.add_searched_movies.is_some() =>
{ {
let tmdb_id = self let tmdb_id = self
@@ -360,7 +354,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); .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.add_movie_modal = Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block = self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS); BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
} }
} }
ActiveRadarrBlock::AddMoviePrompt => { ActiveRadarrBlock::AddMoviePrompt => {
@@ -377,16 +371,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
| ActiveRadarrBlock::AddMovieSelectQualityProfile | ActiveRadarrBlock::AddMovieSelectQualityProfile
| ActiveRadarrBlock::AddMovieSelectRootFolder => self.app.push_navigation_stack( | ActiveRadarrBlock::AddMovieSelectRootFolder => 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(),
*self.context, self.context,
) )
.into(), .into(),
), ),
ActiveRadarrBlock::AddMovieTagsInput => { ActiveRadarrBlock::AddMovieTagsInput => {
self.app.push_navigation_stack( 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(),
*self.context, self.context,
) )
.into(), .into(),
); );
@@ -463,8 +457,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
} }
ActiveRadarrBlock::AddMoviePrompt => { ActiveRadarrBlock::AddMoviePrompt => {
if self.app.data.radarr_data.selected_block.get_active_block() if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::AddMovieConfirmPrompt == ActiveRadarrBlock::AddMovieConfirmPrompt
&& *key == DEFAULT_KEYBINDINGS.confirm.key && key == DEFAULT_KEYBINDINGS.confirm.key
{ {
self.app.data.radarr_data.prompt_confirm = true; 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(None));
File diff suppressed because it is too large Load Diff
@@ -10,22 +10,22 @@ use crate::network::radarr_network::RadarrEvent;
mod delete_movie_handler_tests; mod delete_movie_handler_tests;
pub(super) struct DeleteMovieHandler<'a, 'b> { pub(super) struct DeleteMovieHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
DELETE_MOVIE_BLOCKS.contains(active_block) DELETE_MOVIE_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
) -> Self { ) -> Self {
DeleteMovieHandler { DeleteMovieHandler {
key, key,
@@ -35,7 +35,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -44,14 +44,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
} }
fn handle_scroll_up(&mut self) { fn handle_scroll_up(&mut self) {
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt { if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
self.app.data.radarr_data.selected_block.previous(); self.app.data.radarr_data.selected_block.up();
} }
} }
fn handle_scroll_down(&mut self) { fn handle_scroll_down(&mut self) {
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt { if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
self.app.data.radarr_data.selected_block.next(); self.app.data.radarr_data.selected_block.down();
} }
} }
@@ -62,13 +62,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
fn handle_delete(&mut self) {} fn handle_delete(&mut self) {}
fn handle_left_right_action(&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); handle_prompt_toggle(self.app, self.key);
} }
} }
fn handle_submit(&mut self) { 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() { match self.app.data.radarr_data.selected_block.get_active_block() {
ActiveRadarrBlock::DeleteMovieConfirmPrompt => { ActiveRadarrBlock::DeleteMovieConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm { if self.app.data.radarr_data.prompt_confirm {
@@ -94,7 +94,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
} }
fn handle_esc(&mut self) { 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.pop_navigation_stack();
self.app.data.radarr_data.reset_delete_movie_preferences(); self.app.data.radarr_data.reset_delete_movie_preferences();
self.app.data.radarr_data.prompt_confirm = false; self.app.data.radarr_data.prompt_confirm = false;
@@ -102,10 +102,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
} }
fn handle_char_key_event(&mut self) { 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() && self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::DeleteMovieConfirmPrompt == ActiveRadarrBlock::DeleteMovieConfirmPrompt
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key && self.key == DEFAULT_KEYBINDINGS.confirm.key
{ {
self.app.data.radarr_data.prompt_confirm = true; 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(None));
@@ -21,22 +21,20 @@ mod tests {
#[rstest] #[rstest]
fn test_delete_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) { fn test_delete_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block.down();
app.data.radarr_data.selected_block.next();
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None) DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
.handle();
if key == Key::Up { if key == Key::Up {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::DeleteMovieToggleDeleteFile ActiveRadarrBlock::DeleteMovieToggleDeleteFile
); );
} else { } else {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::DeleteMovieConfirmPrompt ActiveRadarrBlock::DeleteMovieConfirmPrompt
); );
} }
} }
@@ -47,16 +45,14 @@ mod tests {
) { ) {
let mut app = App::default(); let mut app = App::default();
app.is_loading = true; app.is_loading = true;
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block.down();
app.data.radarr_data.selected_block.next();
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None) DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
.handle();
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::DeleteMovieToggleAddListExclusion ActiveRadarrBlock::DeleteMovieToggleAddListExclusion
); );
} }
} }
@@ -70,13 +66,11 @@ mod tests {
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) { fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default(); let mut app = App::default();
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None) DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
.handle();
assert!(app.data.radarr_data.prompt_confirm); assert!(app.data.radarr_data.prompt_confirm);
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None) DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
.handle();
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -98,25 +92,24 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .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.delete_movie_files = true;
app.data.radarr_data.add_list_exclusion = true; app.data.radarr_data.add_list_exclusion = true;
DeleteMovieHandler::with( DeleteMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
&None, None,
) )
.handle(); .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_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
assert!(!app.data.radarr_data.delete_movie_files); assert!(!app.data.radarr_data.delete_movie_files);
@@ -131,23 +124,22 @@ mod tests {
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.delete_movie_files = true; app.data.radarr_data.delete_movie_files = true;
app.data.radarr_data.add_list_exclusion = true; app.data.radarr_data.add_list_exclusion = true;
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(DELETE_MOVIE_SELECTION_BLOCKS.len() - 1); .set_index(0, DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
DeleteMovieHandler::with( DeleteMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteMovie(None)) Some(RadarrEvent::DeleteMovie(None))
@@ -169,16 +161,16 @@ mod tests {
app.data.radarr_data.add_list_exclusion = true; app.data.radarr_data.add_list_exclusion = true;
DeleteMovieHandler::with( DeleteMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::DeleteMoviePrompt.into() ActiveRadarrBlock::DeleteMoviePrompt.into()
); );
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh); assert!(!app.should_refresh);
@@ -191,30 +183,29 @@ mod tests {
fn test_delete_movie_toggle_delete_files_submit() { fn test_delete_movie_toggle_delete_files_submit() {
let current_route = ActiveRadarrBlock::DeleteMoviePrompt.into(); let current_route = ActiveRadarrBlock::DeleteMoviePrompt.into();
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
DeleteMovieHandler::with( DeleteMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &current_route); assert_eq!(app.get_current_route(), current_route);
assert_eq!(app.data.radarr_data.delete_movie_files, true); assert_eq!(app.data.radarr_data.delete_movie_files, true);
DeleteMovieHandler::with( DeleteMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &current_route); assert_eq!(app.get_current_route(), current_route);
assert_eq!(app.data.radarr_data.delete_movie_files, false); assert_eq!(app.data.radarr_data.delete_movie_files, false);
} }
} }
@@ -236,14 +227,14 @@ mod tests {
app.data.radarr_data.add_list_exclusion = true; app.data.radarr_data.add_list_exclusion = true;
DeleteMovieHandler::with( DeleteMovieHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
&None, None,
) )
.handle(); .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.prompt_confirm);
assert!(!app.data.radarr_data.delete_movie_files); assert!(!app.data.radarr_data.delete_movie_files);
assert!(!app.data.radarr_data.add_list_exclusion); assert!(!app.data.radarr_data.add_list_exclusion);
@@ -267,23 +258,22 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
app.data.radarr_data.delete_movie_files = true; app.data.radarr_data.delete_movie_files = true;
app.data.radarr_data.add_list_exclusion = true; app.data.radarr_data.add_list_exclusion = true;
app.data.radarr_data.selected_block = app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(DELETE_MOVIE_SELECTION_BLOCKS.len() - 1); .set_index(0, DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
DeleteMovieHandler::with( DeleteMovieHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key, DEFAULT_KEYBINDINGS.confirm.key,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteMovie(None)) Some(RadarrEvent::DeleteMovie(None))
@@ -299,9 +289,9 @@ mod tests {
fn test_delete_movie_handler_accepts() { fn test_delete_movie_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if DELETE_MOVIE_BLOCKS.contains(&active_radarr_block) { if DELETE_MOVIE_BLOCKS.contains(&active_radarr_block) {
assert!(DeleteMovieHandler::accepts(&active_radarr_block)); assert!(DeleteMovieHandler::accepts(active_radarr_block));
} else { } else {
assert!(!DeleteMovieHandler::accepts(&active_radarr_block)); assert!(!DeleteMovieHandler::accepts(active_radarr_block));
} }
}); });
} }
@@ -312,10 +302,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = DeleteMovieHandler::with( let handler = DeleteMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -327,10 +317,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = DeleteMovieHandler::with( let handler = DeleteMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
@@ -12,22 +12,22 @@ use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
mod edit_movie_handler_tests; mod edit_movie_handler_tests;
pub(super) struct EditMovieHandler<'a, 'b> { pub(super) struct EditMovieHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
EDIT_MOVIE_BLOCKS.contains(active_block) EDIT_MOVIE_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> EditMovieHandler<'a, 'b> { ) -> EditMovieHandler<'a, 'b> {
EditMovieHandler { EditMovieHandler {
key, key,
@@ -37,7 +37,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -65,7 +65,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
.unwrap() .unwrap()
.quality_profile_list .quality_profile_list
.scroll_up(), .scroll_up(),
ActiveRadarrBlock::EditMoviePrompt => self.app.data.radarr_data.selected_block.previous(), ActiveRadarrBlock::EditMoviePrompt => self.app.data.radarr_data.selected_block.up(),
_ => (), _ => (),
} }
} }
@@ -90,7 +90,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
.unwrap() .unwrap()
.quality_profile_list .quality_profile_list
.scroll_down(), .scroll_down(),
ActiveRadarrBlock::EditMoviePrompt => self.app.data.radarr_data.selected_block.next(), ActiveRadarrBlock::EditMoviePrompt => self.app.data.radarr_data.selected_block.down(),
_ => (), _ => (),
} }
} }
@@ -231,16 +231,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability ActiveRadarrBlock::EditMovieSelectMinimumAvailability
| ActiveRadarrBlock::EditMovieSelectQualityProfile => self.app.push_navigation_stack( | ActiveRadarrBlock::EditMovieSelectQualityProfile => 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(),
*self.context, self.context,
) )
.into(), .into(),
), ),
ActiveRadarrBlock::EditMoviePathInput | ActiveRadarrBlock::EditMovieTagsInput => { ActiveRadarrBlock::EditMoviePathInput | ActiveRadarrBlock::EditMovieTagsInput => {
self.app.push_navigation_stack( 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(),
*self.context, self.context,
) )
.into(), .into(),
); );
@@ -329,8 +329,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
} }
ActiveRadarrBlock::EditMoviePrompt => { ActiveRadarrBlock::EditMoviePrompt => {
if self.app.data.radarr_data.selected_block.get_active_block() if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::EditMovieConfirmPrompt == ActiveRadarrBlock::EditMovieConfirmPrompt
&& *key == DEFAULT_KEYBINDINGS.confirm.key && key == DEFAULT_KEYBINDINGS.confirm.key
{ {
self.app.data.radarr_data.prompt_confirm = true; self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie(None)); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie(None));
@@ -42,10 +42,10 @@ mod tests {
if key == Key::Up { if key == Key::Up {
for i in (0..minimum_availability_vec.len()).rev() { for i in (0..minimum_availability_vec.len()).rev() {
EditMovieHandler::with( EditMovieHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability, ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
&None, None,
) )
.handle(); .handle();
@@ -64,10 +64,10 @@ mod tests {
} else { } else {
for i in 0..minimum_availability_vec.len() { for i in 0..minimum_availability_vec.len() {
EditMovieHandler::with( EditMovieHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability, ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
&None, None,
) )
.handle(); .handle();
@@ -102,10 +102,10 @@ mod tests {
.set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]); .set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]);
EditMovieHandler::with( EditMovieHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieSelectQualityProfile, ActiveRadarrBlock::EditMovieSelectQualityProfile,
&None, None,
) )
.handle(); .handle();
@@ -122,10 +122,10 @@ mod tests {
); );
EditMovieHandler::with( EditMovieHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieSelectQualityProfile, ActiveRadarrBlock::EditMovieSelectQualityProfile,
&None, None,
) )
.handle(); .handle();
@@ -146,20 +146,20 @@ mod tests {
fn test_edit_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) { fn test_edit_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next(); app.data.radarr_data.selected_block.down();
EditMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::EditMoviePrompt, &None).handle(); EditMovieHandler::with(key, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
if key == Key::Up { if key == Key::Up {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditMovieToggleMonitored ActiveRadarrBlock::EditMovieToggleMonitored
); );
} else { } else {
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditMovieSelectQualityProfile ActiveRadarrBlock::EditMovieSelectQualityProfile
); );
} }
} }
@@ -169,14 +169,14 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.is_loading = true; app.is_loading = true;
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next(); app.data.radarr_data.selected_block.down();
EditMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::EditMoviePrompt, &None).handle(); EditMovieHandler::with(key, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability ActiveRadarrBlock::EditMovieSelectMinimumAvailability
); );
} }
} }
@@ -205,10 +205,10 @@ mod tests {
.set_items(minimum_availability_vec.clone()); .set_items(minimum_availability_vec.clone());
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability, ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
&None, None,
) )
.handle(); .handle();
@@ -225,10 +225,10 @@ mod tests {
); );
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability, ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
&None, None,
) )
.handle(); .handle();
@@ -263,10 +263,10 @@ mod tests {
]); ]);
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieSelectQualityProfile, ActiveRadarrBlock::EditMovieSelectQualityProfile,
&None, None,
) )
.handle(); .handle();
@@ -283,10 +283,10 @@ mod tests {
); );
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieSelectQualityProfile, ActiveRadarrBlock::EditMovieSelectQualityProfile,
&None, None,
) )
.handle(); .handle();
@@ -312,10 +312,10 @@ mod tests {
}); });
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePathInput, ActiveRadarrBlock::EditMoviePathInput,
&None, None,
) )
.handle(); .handle();
@@ -333,10 +333,10 @@ mod tests {
); );
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePathInput, ActiveRadarrBlock::EditMoviePathInput,
&None, None,
) )
.handle(); .handle();
@@ -363,10 +363,10 @@ mod tests {
}); });
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieTagsInput, ActiveRadarrBlock::EditMovieTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -384,10 +384,10 @@ mod tests {
); );
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieTagsInput, ActiveRadarrBlock::EditMovieTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -418,11 +418,11 @@ mod tests {
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) { fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default(); let mut app = App::default();
EditMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::EditMoviePrompt, &None).handle(); EditMovieHandler::with(key, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
assert!(app.data.radarr_data.prompt_confirm); assert!(app.data.radarr_data.prompt_confirm);
EditMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::EditMoviePrompt, &None).handle(); EditMovieHandler::with(key, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -436,10 +436,10 @@ mod tests {
}); });
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePathInput, ActiveRadarrBlock::EditMoviePathInput,
&None, None,
) )
.handle(); .handle();
@@ -457,10 +457,10 @@ mod tests {
); );
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePathInput, ActiveRadarrBlock::EditMoviePathInput,
&None, None,
) )
.handle(); .handle();
@@ -487,10 +487,10 @@ mod tests {
}); });
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieTagsInput, ActiveRadarrBlock::EditMovieTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -508,10 +508,10 @@ mod tests {
); );
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieTagsInput, ActiveRadarrBlock::EditMovieTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -535,9 +535,7 @@ mod tests {
use rstest::rstest; use rstest::rstest;
use crate::models::servarr_data::radarr::modals::EditMovieModal; use crate::models::servarr_data::radarr::modals::EditMovieModal;
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::EDIT_MOVIE_SELECTION_BLOCKS;
EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS,
};
use crate::models::{BlockSelectionState, Route}; use crate::models::{BlockSelectionState, Route};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
@@ -557,10 +555,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePathInput.into()); app.push_navigation_stack(ActiveRadarrBlock::EditMoviePathInput.into());
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePathInput, ActiveRadarrBlock::EditMoviePathInput,
&None, None,
) )
.handle(); .handle();
@@ -576,7 +574,7 @@ mod tests {
.is_empty()); .is_empty());
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into() ActiveRadarrBlock::EditMoviePrompt.into()
); );
} }
@@ -592,10 +590,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePathInput.into()); app.push_navigation_stack(ActiveRadarrBlock::EditMoviePathInput.into());
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieTagsInput, ActiveRadarrBlock::EditMovieTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -611,7 +609,7 @@ mod tests {
.is_empty()); .is_empty());
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into() ActiveRadarrBlock::EditMoviePrompt.into()
); );
} }
@@ -621,22 +619,22 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); .set_index(0, EDIT_MOVIE_SELECTION_BLOCKS.len() - 1);
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&None, None,
) )
.handle(); .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_eq!(app.data.radarr_data.prompt_confirm_action, None);
} }
@@ -647,22 +645,22 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); .set_index(0, EDIT_MOVIE_SELECTION_BLOCKS.len() - 1);
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditMovie(None)) Some(RadarrEvent::EditMovie(None))
@@ -681,16 +679,16 @@ mod tests {
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into() ActiveRadarrBlock::EditMoviePrompt.into()
); );
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh); assert!(!app.should_refresh);
@@ -704,18 +702,18 @@ mod tests {
)); ));
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.push_navigation_stack(current_route); app.push_navigation_stack(current_route);
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&Some(ActiveRadarrBlock::Movies), Some(ActiveRadarrBlock::Movies),
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &current_route); assert_eq!(app.get_current_route(), current_route);
assert_eq!( assert_eq!(
app app
.data .data
@@ -728,14 +726,14 @@ mod tests {
); );
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&Some(ActiveRadarrBlock::Movies), Some(ActiveRadarrBlock::Movies),
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &current_route); assert_eq!(app.get_current_route(), current_route);
assert_eq!( assert_eq!(
app app
.data .data
@@ -755,7 +753,7 @@ mod tests {
#[case(ActiveRadarrBlock::EditMovieTagsInput, 4)] #[case(ActiveRadarrBlock::EditMovieTagsInput, 4)]
fn test_edit_movie_prompt_selected_block_submit( fn test_edit_movie_prompt_selected_block_submit(
#[case] selected_block: ActiveRadarrBlock, #[case] selected_block: ActiveRadarrBlock,
#[case] index: usize, #[case] y_index: usize,
) { ) {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
@@ -766,20 +764,20 @@ mod tests {
) )
.into(), .into(),
); );
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(index); app.data.radarr_data.selected_block.set_index(0, y_index);
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&Some(ActiveRadarrBlock::Movies), Some(ActiveRadarrBlock::Movies),
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&(selected_block, Some(ActiveRadarrBlock::Movies)).into() (selected_block, Some(ActiveRadarrBlock::Movies)).into()
); );
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
@@ -792,7 +790,7 @@ mod tests {
#[rstest] #[rstest]
fn test_edit_movie_prompt_selected_block_submit_no_op_when_not_ready( fn test_edit_movie_prompt_selected_block_submit_no_op_when_not_ready(
#[values(1, 2, 3, 4)] index: usize, #[values(1, 2, 3, 4)] y_index: usize,
) { ) {
let mut app = App::default(); let mut app = App::default();
app.is_loading = true; app.is_loading = true;
@@ -804,20 +802,20 @@ mod tests {
) )
.into(), .into(),
); );
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(index); app.data.radarr_data.selected_block.set_index(0, y_index);
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&Some(ActiveRadarrBlock::Movies), Some(ActiveRadarrBlock::Movies),
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&( (
ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
Some(ActiveRadarrBlock::Movies), Some(ActiveRadarrBlock::Movies),
) )
@@ -843,16 +841,16 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into()); app.push_navigation_stack(active_radarr_block.into());
EditMovieHandler::with( EditMovieHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&Some(ActiveRadarrBlock::Movies), Some(ActiveRadarrBlock::Movies),
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into() ActiveRadarrBlock::EditMoviePrompt.into()
); );
if active_radarr_block == ActiveRadarrBlock::EditMoviePathInput if active_radarr_block == ActiveRadarrBlock::EditMoviePathInput
@@ -888,12 +886,12 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.push_navigation_stack(active_radarr_block.into()); app.push_navigation_stack(active_radarr_block.into());
EditMovieHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle(); EditMovieHandler::with(ESC_KEY, &mut app, active_radarr_block, None).handle();
assert!(!app.should_ignore_quit_key); assert!(!app.should_ignore_quit_key);
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into() ActiveRadarrBlock::EditMoviePrompt.into()
); );
} }
@@ -904,15 +902,9 @@ mod tests {
app.data.radarr_data = create_test_radarr_data(); app.data.radarr_data = create_test_radarr_data();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
EditMovieHandler::with( EditMovieHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
&ESC_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&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.edit_movie_modal.is_none()); assert!(app.data.radarr_data.edit_movie_modal.is_none());
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
@@ -932,9 +924,9 @@ mod tests {
app.data.radarr_data = create_test_radarr_data(); app.data.radarr_data = create_test_radarr_data();
app.push_navigation_stack(active_radarr_block.into()); app.push_navigation_stack(active_radarr_block.into());
EditMovieHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle(); EditMovieHandler::with(ESC_KEY, &mut app, active_radarr_block, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
} }
} }
@@ -942,10 +934,7 @@ mod tests {
use super::*; use super::*;
use crate::{ use crate::{
models::{ models::{
servarr_data::radarr::{ servarr_data::radarr::{modals::EditMovieModal, radarr_data::EDIT_MOVIE_SELECTION_BLOCKS},
modals::EditMovieModal,
radarr_data::{EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS},
},
BlockSelectionState, BlockSelectionState,
}, },
network::radarr_network::RadarrEvent, network::radarr_network::RadarrEvent,
@@ -960,10 +949,10 @@ mod tests {
}); });
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key, DEFAULT_KEYBINDINGS.backspace.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePathInput, ActiveRadarrBlock::EditMoviePathInput,
&None, None,
) )
.handle(); .handle();
@@ -989,10 +978,10 @@ mod tests {
}); });
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key, DEFAULT_KEYBINDINGS.backspace.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieTagsInput, ActiveRadarrBlock::EditMovieTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -1015,10 +1004,10 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
EditMovieHandler::with( EditMovieHandler::with(
&Key::Char('h'), Key::Char('h'),
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePathInput, ActiveRadarrBlock::EditMoviePathInput,
&None, None,
) )
.handle(); .handle();
@@ -1041,10 +1030,10 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
EditMovieHandler::with( EditMovieHandler::with(
&Key::Char('h'), Key::Char('h'),
&mut app, &mut app,
&ActiveRadarrBlock::EditMovieTagsInput, ActiveRadarrBlock::EditMovieTagsInput,
&None, None,
) )
.handle(); .handle();
@@ -1067,22 +1056,22 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); .set_index(0, EDIT_MOVIE_SELECTION_BLOCKS.len() - 1);
EditMovieHandler::with( EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key, DEFAULT_KEYBINDINGS.confirm.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!( assert_eq!(
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditMovie(None)) Some(RadarrEvent::EditMovie(None))
@@ -1096,9 +1085,9 @@ mod tests {
fn test_edit_movie_handler_accepts() { fn test_edit_movie_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if EDIT_MOVIE_BLOCKS.contains(&active_radarr_block) { if EDIT_MOVIE_BLOCKS.contains(&active_radarr_block) {
assert!(EditMovieHandler::accepts(&active_radarr_block)); assert!(EditMovieHandler::accepts(active_radarr_block));
} else { } else {
assert!(!EditMovieHandler::accepts(&active_radarr_block)); assert!(!EditMovieHandler::accepts(active_radarr_block));
} }
}); });
} }
@@ -1109,10 +1098,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = EditMovieHandler::with( let handler = EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -1124,10 +1113,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = EditMovieHandler::with( let handler = EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -1140,10 +1129,10 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
let handler = EditMovieHandler::with( let handler = EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
File diff suppressed because it is too large Load Diff
+58 -253
View File
@@ -8,14 +8,15 @@ use crate::handlers::radarr_handlers::library::edit_movie_handler::EditMovieHand
use crate::handlers::radarr_handlers::library::movie_details_handler::MovieDetailsHandler; use crate::handlers::radarr_handlers::library::movie_details_handler::MovieDetailsHandler;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler}; use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::handle_table_events;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::models::radarr_models::Movie; use crate::models::radarr_models::Movie;
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, DELETE_MOVIE_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS, LIBRARY_BLOCKS, ActiveRadarrBlock, DELETE_MOVIE_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS, LIBRARY_BLOCKS,
}; };
use crate::models::stateful_table::SortOption; use crate::models::stateful_table::SortOption;
use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable}; use crate::models::{BlockSelectionState, HorizontallyScrollableText};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
mod add_movie_handler; mod add_movie_handler;
mod delete_movie_handler; mod delete_movie_handler;
@@ -27,46 +28,65 @@ mod movie_details_handler;
mod library_handler_tests; mod library_handler_tests;
pub(super) struct LibraryHandler<'a, 'b> { pub(super) struct LibraryHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> LibraryHandler<'a, 'b> {
handle_table_events!(self, movies, self.app.data.radarr_data.movies, Movie);
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, 'b> {
fn handle(&mut self) { fn handle(&mut self) {
match self.active_radarr_block { let movie_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::Movies.into())
_ if AddMovieHandler::accepts(self.active_radarr_block) => { .sorting_block(ActiveRadarrBlock::MoviesSortPrompt.into())
AddMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context).handle(); .sort_by_fn(|a: &Movie, b: &Movie| a.id.cmp(&b.id))
.sort_options(movies_sorting_options())
.searching_block(ActiveRadarrBlock::SearchMovie.into())
.search_error_block(ActiveRadarrBlock::SearchMovieError.into())
.search_field_fn(|movie| &movie.title.text)
.filtering_block(ActiveRadarrBlock::FilterMovies.into())
.filter_error_block(ActiveRadarrBlock::FilterMoviesError.into())
.filter_field_fn(|movie| &movie.title.text);
if !self.handle_movies_table_events(movie_table_handling_config) {
match self.active_radarr_block {
_ if AddMovieHandler::accepts(self.active_radarr_block) => {
AddMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ if DeleteMovieHandler::accepts(self.active_radarr_block) => {
DeleteMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ if EditMovieHandler::accepts(self.active_radarr_block) => {
EditMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ if MovieDetailsHandler::accepts(self.active_radarr_block) => {
MovieDetailsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ => self.handle_key_event(),
} }
_ if DeleteMovieHandler::accepts(self.active_radarr_block) => {
DeleteMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ if EditMovieHandler::accepts(self.active_radarr_block) => {
EditMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context).handle();
}
_ if MovieDetailsHandler::accepts(self.active_radarr_block) => {
MovieDetailsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ => self.handle_key_event(),
} }
} }
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
AddMovieHandler::accepts(active_block) AddMovieHandler::accepts(active_block)
|| DeleteMovieHandler::accepts(active_block) || DeleteMovieHandler::accepts(active_block)
|| EditMovieHandler::accepts(active_block) || EditMovieHandler::accepts(active_block)
|| MovieDetailsHandler::accepts(active_block) || MovieDetailsHandler::accepts(active_block)
|| LIBRARY_BLOCKS.contains(active_block) || LIBRARY_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> LibraryHandler<'a, 'b> { ) -> LibraryHandler<'a, 'b> {
LibraryHandler { LibraryHandler {
key, key,
@@ -76,7 +96,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -84,117 +104,21 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
!self.app.is_loading && !self.app.data.radarr_data.movies.is_empty() !self.app.is_loading && !self.app.data.radarr_data.movies.is_empty()
} }
fn handle_scroll_up(&mut self) { fn handle_scroll_up(&mut self) {}
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_up(),
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_up(),
_ => (),
}
}
fn handle_scroll_down(&mut self) { fn handle_scroll_down(&mut self) {}
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_down(),
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_down(),
_ => (),
}
}
fn handle_home(&mut self) { fn handle_home(&mut self) {}
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_to_top(),
ActiveRadarrBlock::SearchMovie => {
self
.app
.data
.radarr_data
.movies
.search
.as_mut()
.unwrap()
.scroll_home();
}
ActiveRadarrBlock::FilterMovies => {
self
.app
.data
.radarr_data
.movies
.filter
.as_mut()
.unwrap()
.scroll_home();
}
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_to_top(),
_ => (),
}
}
fn handle_end(&mut self) { fn handle_end(&mut self) {}
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_to_bottom(),
ActiveRadarrBlock::SearchMovie => self
.app
.data
.radarr_data
.movies
.search
.as_mut()
.unwrap()
.reset_offset(),
ActiveRadarrBlock::FilterMovies => self
.app
.data
.radarr_data
.movies
.filter
.as_mut()
.unwrap()
.reset_offset(),
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_to_bottom(),
_ => (),
}
}
fn handle_delete(&mut self) { fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Movies { if self.active_radarr_block == ActiveRadarrBlock::Movies {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into()); .push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
self.app.data.radarr_data.selected_block = self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS); BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
} }
} }
@@ -202,20 +126,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::Movies => handle_change_tab_left_right_keys(self.app, self.key), ActiveRadarrBlock::Movies => handle_change_tab_left_right_keys(self.app, self.key),
ActiveRadarrBlock::UpdateAllMoviesPrompt => handle_prompt_toggle(self.app, self.key), ActiveRadarrBlock::UpdateAllMoviesPrompt => handle_prompt_toggle(self.app, self.key),
ActiveRadarrBlock::SearchMovie => {
handle_text_box_left_right_keys!(
self,
self.key,
self.app.data.radarr_data.movies.search.as_mut().unwrap()
)
}
ActiveRadarrBlock::FilterMovies => {
handle_text_box_left_right_keys!(
self,
self.key,
self.app.data.radarr_data.movies.filter.as_mut().unwrap()
)
}
_ => (), _ => (),
} }
} }
@@ -225,44 +135,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
ActiveRadarrBlock::Movies => self ActiveRadarrBlock::Movies => self
.app .app
.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()), .push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()),
ActiveRadarrBlock::SearchMovie => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
if self.app.data.radarr_data.movies.search.is_some() {
let has_match = self
.app
.data
.radarr_data
.movies
.apply_search(|movie| &movie.title.text);
if !has_match {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SearchMovieError.into());
}
}
}
ActiveRadarrBlock::FilterMovies => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
if self.app.data.radarr_data.movies.filter.is_some() {
let has_matches = self
.app
.data
.radarr_data
.movies
.apply_filter(|movie| &movie.title.text);
if !has_matches {
self
.app
.push_navigation_stack(ActiveRadarrBlock::FilterMoviesError.into());
}
}
}
ActiveRadarrBlock::UpdateAllMoviesPrompt => { ActiveRadarrBlock::UpdateAllMoviesPrompt => {
if self.app.data.radarr_data.prompt_confirm { if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies);
@@ -270,44 +142,17 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
} }
ActiveRadarrBlock::MoviesSortPrompt => {
self
.app
.data
.radarr_data
.movies
.items
.sort_by(|a, b| a.id.cmp(&b.id));
self.app.data.radarr_data.movies.apply_sorting();
self.app.pop_navigation_stack();
}
_ => (), _ => (),
} }
} }
fn handle_esc(&mut self) { fn handle_esc(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::FilterMovies | ActiveRadarrBlock::FilterMoviesError => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.movies.reset_filter();
self.app.should_ignore_quit_key = false;
}
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchMovieError => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.movies.reset_search();
self.app.should_ignore_quit_key = false;
}
ActiveRadarrBlock::UpdateAllMoviesPrompt => { ActiveRadarrBlock::UpdateAllMoviesPrompt => {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false; self.app.data.radarr_data.prompt_confirm = false;
} }
ActiveRadarrBlock::MoviesSortPrompt => {
self.app.pop_navigation_stack();
}
_ => { _ => {
self.app.data.radarr_data.movies.reset_search();
self.app.data.radarr_data.movies.reset_filter();
handle_clear_errors(self.app); handle_clear_errors(self.app);
} }
} }
@@ -317,22 +162,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
let key = self.key; let key = self.key;
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::Movies => match self.key { ActiveRadarrBlock::Movies => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.search.key => { _ if key == DEFAULT_KEYBINDINGS.edit.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SearchMovie.into());
self.app.data.radarr_data.movies.search = Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true;
}
_ if *key == DEFAULT_KEYBINDINGS.filter.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::FilterMovies.into());
self.app.data.radarr_data.movies.reset_filter();
self.app.data.radarr_data.movies.filter = Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true;
}
_ if *key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack( self.app.push_navigation_stack(
( (
ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
@@ -342,52 +172,27 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
); );
self.app.data.radarr_data.edit_movie_modal = Some((&self.app.data.radarr_data).into()); self.app.data.radarr_data.edit_movie_modal = Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block = self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
} }
_ if *key == DEFAULT_KEYBINDINGS.add.key => { _ if key == DEFAULT_KEYBINDINGS.add.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchInput.into()); .push_navigation_stack(ActiveRadarrBlock::AddMovieSearchInput.into());
self.app.data.radarr_data.add_movie_search = Some(HorizontallyScrollableText::default()); self.app.data.radarr_data.add_movie_search = Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true; self.app.should_ignore_quit_key = true;
} }
_ if *key == DEFAULT_KEYBINDINGS.update.key => { _ if key == DEFAULT_KEYBINDINGS.update.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true; self.app.should_refresh = true;
} }
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
self
.app
.data
.radarr_data
.movies
.sorting(movies_sorting_options());
self
.app
.push_navigation_stack(ActiveRadarrBlock::MoviesSortPrompt.into());
}
_ => (), _ => (),
}, },
ActiveRadarrBlock::SearchMovie => {
handle_text_box_keys!(
self,
key,
self.app.data.radarr_data.movies.search.as_mut().unwrap()
)
}
ActiveRadarrBlock::FilterMovies => {
handle_text_box_keys!(
self,
key,
self.app.data.radarr_data.movies.filter.as_mut().unwrap()
)
}
ActiveRadarrBlock::UpdateAllMoviesPrompt => { ActiveRadarrBlock::UpdateAllMoviesPrompt => {
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 = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies);
@@ -3,8 +3,10 @@ use serde_json::Number;
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::RadarrRelease; use crate::models::radarr_models::{Credit, MovieHistoryItem, RadarrRelease};
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, EDIT_MOVIE_SELECTION_BLOCKS, MOVIE_DETAILS_BLOCKS, ActiveRadarrBlock, EDIT_MOVIE_SELECTION_BLOCKS, MOVIE_DETAILS_BLOCKS,
}; };
@@ -18,22 +20,96 @@ use crate::network::radarr_network::RadarrEvent;
mod movie_details_handler_tests; mod movie_details_handler_tests;
pub(super) struct MovieDetailsHandler<'a, 'b> { pub(super) struct MovieDetailsHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> MovieDetailsHandler<'a, 'b> {
handle_table_events!(
self,
movie_releases,
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases,
RadarrRelease
);
handle_table_events!(
self,
movie_history,
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history,
MovieHistoryItem
);
handle_table_events!(
self,
movie_cast,
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast,
Credit
);
handle_table_events!(
self,
movie_crew,
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew,
Credit
);
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn handle(&mut self) {
MOVIE_DETAILS_BLOCKS.contains(active_block) let movie_history_table_handling_config =
TableHandlingConfig::new(ActiveRadarrBlock::MovieHistory.into());
let movie_releases_table_handling_config =
TableHandlingConfig::new(ActiveRadarrBlock::ManualSearch.into())
.sorting_block(ActiveRadarrBlock::ManualSearchSortPrompt.into())
.sort_options(releases_sorting_options());
let movie_cast_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::Cast.into());
let movie_crew_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::Crew.into());
if !self.handle_movie_history_table_events(movie_history_table_handling_config)
&& !self.handle_movie_releases_table_events(movie_releases_table_handling_config)
&& !self.handle_movie_cast_table_events(movie_cast_table_handling_config)
&& !self.handle_movie_crew_table_events(movie_crew_table_handling_config)
{
self.handle_key_event();
}
}
fn accepts(active_block: ActiveRadarrBlock) -> bool {
MOVIE_DETAILS_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
) -> MovieDetailsHandler<'a, 'b> { ) -> MovieDetailsHandler<'a, 'b> {
MovieDetailsHandler { MovieDetailsHandler {
key, key,
@@ -43,7 +119,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -73,8 +149,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
} }
fn handle_scroll_up(&mut self) { fn handle_scroll_up(&mut self) {
match self.active_radarr_block { if self.active_radarr_block == ActiveRadarrBlock::MovieDetails {
ActiveRadarrBlock::MovieDetails => self self
.app .app
.data .data
.radarr_data .radarr_data
@@ -82,62 +158,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
.as_mut() .as_mut()
.unwrap() .unwrap()
.movie_details .movie_details
.scroll_up(), .scroll_up()
ActiveRadarrBlock::MovieHistory => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history
.scroll_up(),
ActiveRadarrBlock::Cast => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast
.scroll_up(),
ActiveRadarrBlock::Crew => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew
.scroll_up(),
ActiveRadarrBlock::ManualSearch => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.scroll_up(),
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sort
.as_mut()
.unwrap()
.scroll_up(),
_ => (),
} }
} }
fn handle_scroll_down(&mut self) { fn handle_scroll_down(&mut self) {
match self.active_radarr_block { if self.active_radarr_block == ActiveRadarrBlock::MovieDetails {
ActiveRadarrBlock::MovieDetails => self self
.app .app
.data .data
.radarr_data .radarr_data
@@ -145,62 +172,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
.as_mut() .as_mut()
.unwrap() .unwrap()
.movie_details .movie_details
.scroll_down(), .scroll_down()
ActiveRadarrBlock::MovieHistory => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history
.scroll_down(),
ActiveRadarrBlock::Cast => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast
.scroll_down(),
ActiveRadarrBlock::Crew => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew
.scroll_down(),
ActiveRadarrBlock::ManualSearch => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.scroll_down(),
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sort
.as_mut()
.unwrap()
.scroll_down(),
_ => (),
} }
} }
fn handle_home(&mut self) { fn handle_home(&mut self) {
match self.active_radarr_block { if self.active_radarr_block == ActiveRadarrBlock::MovieDetails {
ActiveRadarrBlock::MovieDetails => self self
.app .app
.data .data
.radarr_data .radarr_data
@@ -208,62 +186,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
.as_mut() .as_mut()
.unwrap() .unwrap()
.movie_details .movie_details
.scroll_to_top(), .scroll_to_top()
ActiveRadarrBlock::MovieHistory => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history
.scroll_to_top(),
ActiveRadarrBlock::Cast => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast
.scroll_to_top(),
ActiveRadarrBlock::Crew => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew
.scroll_to_top(),
ActiveRadarrBlock::ManualSearch => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.scroll_to_top(),
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sort
.as_mut()
.unwrap()
.scroll_to_top(),
_ => (),
} }
} }
fn handle_end(&mut self) { fn handle_end(&mut self) {
match self.active_radarr_block { if let ActiveRadarrBlock::MovieDetails = self.active_radarr_block {
ActiveRadarrBlock::MovieDetails => self self
.app .app
.data .data
.radarr_data .radarr_data
@@ -271,56 +200,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
.as_mut() .as_mut()
.unwrap() .unwrap()
.movie_details .movie_details
.scroll_to_bottom(), .scroll_to_bottom()
ActiveRadarrBlock::MovieHistory => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history
.scroll_to_bottom(),
ActiveRadarrBlock::Cast => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast
.scroll_to_bottom(),
ActiveRadarrBlock::Crew => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew
.scroll_to_bottom(),
ActiveRadarrBlock::ManualSearch => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.scroll_to_bottom(),
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sort
.as_mut()
.unwrap()
.scroll_to_bottom(),
_ => (),
} }
} }
@@ -334,16 +214,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
| ActiveRadarrBlock::Cast | ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew | ActiveRadarrBlock::Crew
| ActiveRadarrBlock::ManualSearch => match self.key { | ActiveRadarrBlock::ManualSearch => match self.key {
_ if *self.key == DEFAULT_KEYBINDINGS.left.key => { _ if self.key == DEFAULT_KEYBINDINGS.left.key => {
self.app.data.radarr_data.movie_info_tabs.previous(); self.app.data.radarr_data.movie_info_tabs.previous();
self.app.pop_and_push_navigation_stack( self.app.pop_and_push_navigation_stack(
*self.app.data.radarr_data.movie_info_tabs.get_active_route(), self.app.data.radarr_data.movie_info_tabs.get_active_route(),
); );
} }
_ if *self.key == DEFAULT_KEYBINDINGS.right.key => { _ if self.key == DEFAULT_KEYBINDINGS.right.key => {
self.app.data.radarr_data.movie_info_tabs.next(); self.app.data.radarr_data.movie_info_tabs.next();
self.app.pop_and_push_navigation_stack( self.app.pop_and_push_navigation_stack(
*self.app.data.radarr_data.movie_info_tabs.get_active_route(), self.app.data.radarr_data.movie_info_tabs.get_active_route(),
); );
} }
_ => (), _ => (),
@@ -385,18 +265,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
} }
ActiveRadarrBlock::ManualSearchSortPrompt => {
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.apply_sorting();
self.app.pop_navigation_stack();
}
_ => (), _ => (),
} }
} }
@@ -414,8 +282,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
} }
ActiveRadarrBlock::AutomaticallySearchMoviePrompt ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::UpdateAndScanPrompt | ActiveRadarrBlock::UpdateAndScanPrompt
| ActiveRadarrBlock::ManualSearchConfirmPrompt | ActiveRadarrBlock::ManualSearchConfirmPrompt => {
| ActiveRadarrBlock::ManualSearchSortPrompt => {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false; self.app.data.radarr_data.prompt_confirm = false;
} }
@@ -425,81 +292,62 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
fn handle_char_key_event(&mut self) { fn handle_char_key_event(&mut self) {
let key = self.key; let key = self.key;
match *self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory | ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo | ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast | ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew | ActiveRadarrBlock::Crew
| ActiveRadarrBlock::ManualSearch => match self.key { | ActiveRadarrBlock::ManualSearch => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.search.key => { _ if key == DEFAULT_KEYBINDINGS.auto_search.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::AutomaticallySearchMoviePrompt.into()); .push_navigation_stack(ActiveRadarrBlock::AutomaticallySearchMoviePrompt.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.edit.key => { _ if key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack( self.app.push_navigation_stack(
( (
ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
Some(*self.active_radarr_block), Some(self.active_radarr_block),
) )
.into(), .into(),
); );
self.app.data.radarr_data.edit_movie_modal = Some((&self.app.data.radarr_data).into()); self.app.data.radarr_data.edit_movie_modal = Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block = self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
} }
_ if *key == DEFAULT_KEYBINDINGS.update.key => { _ if key == DEFAULT_KEYBINDINGS.update.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::UpdateAndScanPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::UpdateAndScanPrompt.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self self
.app .app
.pop_and_push_navigation_stack((*self.active_radarr_block).into()); .pop_and_push_navigation_stack(self.active_radarr_block.into());
}
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sorting(releases_sorting_options());
self
.app
.push_navigation_stack(ActiveRadarrBlock::ManualSearchSortPrompt.into());
} }
_ => (), _ => (),
}, },
ActiveRadarrBlock::AutomaticallySearchMoviePrompt => { ActiveRadarrBlock::AutomaticallySearchMoviePrompt
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 = self.app.data.radarr_data.prompt_confirm = true;
Some(RadarrEvent::TriggerAutomaticSearch(None)); self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::TriggerAutomaticSearch(None));
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
}
} }
ActiveRadarrBlock::UpdateAndScanPrompt => { ActiveRadarrBlock::UpdateAndScanPrompt 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 = true; self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan(None));
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan(None));
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
}
} }
ActiveRadarrBlock::ManualSearchConfirmPrompt => { ActiveRadarrBlock::ManualSearchConfirmPrompt 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 = true; self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DownloadRelease(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DownloadRelease(None));
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
}
} }
_ => (), _ => (),
} }
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -27,10 +27,10 @@ mod radarr_handler_tests;
mod radarr_handler_test_utils; mod radarr_handler_test_utils;
pub(super) struct RadarrHandler<'a, 'b> { pub(super) struct RadarrHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b> {
@@ -63,15 +63,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
} }
} }
fn accepts(_active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(_active_block: ActiveRadarrBlock) -> bool {
true true
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> RadarrHandler<'a, 'b> { ) -> RadarrHandler<'a, 'b> {
RadarrHandler { RadarrHandler {
key, key,
@@ -81,7 +81,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -108,16 +108,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
fn handle_char_key_event(&mut self) {} fn handle_char_key_event(&mut self) {}
} }
pub fn handle_change_tab_left_right_keys(app: &mut App<'_>, key: &Key) { pub fn handle_change_tab_left_right_keys(app: &mut App<'_>, key: Key) {
let key_ref = key; let key_ref = key;
match key_ref { match key_ref {
_ if *key == DEFAULT_KEYBINDINGS.left.key => { _ if key == DEFAULT_KEYBINDINGS.left.key => {
app.data.radarr_data.main_tabs.previous(); app.data.radarr_data.main_tabs.previous();
app.pop_and_push_navigation_stack(*app.data.radarr_data.main_tabs.get_active_route()); app.pop_and_push_navigation_stack(app.data.radarr_data.main_tabs.get_active_route());
} }
_ if *key == DEFAULT_KEYBINDINGS.right.key => { _ if key == DEFAULT_KEYBINDINGS.right.key => {
app.data.radarr_data.main_tabs.next(); app.data.radarr_data.main_tabs.next();
app.pop_and_push_navigation_stack(*app.data.radarr_data.main_tabs.get_active_route()); app.pop_and_push_navigation_stack(app.data.radarr_data.main_tabs.get_active_route());
} }
_ => (), _ => (),
} }
@@ -23,15 +23,15 @@ mod utils {
}]); }]);
app.data.radarr_data = radarr_data; app.data.radarr_data = radarr_data;
$handler::with(&DEFAULT_KEYBINDINGS.edit.key, &mut app, &$block, &None).handle(); $handler::with(DEFAULT_KEYBINDINGS.edit.key, &mut app, $block, None).handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&(ActiveRadarrBlock::EditMoviePrompt, Some($context)).into() (ActiveRadarrBlock::EditMoviePrompt, Some($context)).into()
); );
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditMovieToggleMonitored ActiveRadarrBlock::EditMovieToggleMonitored
); );
assert_eq!( assert_eq!(
app app
@@ -111,7 +111,7 @@ mod utils {
); );
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.blocks, app.data.radarr_data.selected_block.blocks,
&EDIT_MOVIE_SELECTION_BLOCKS EDIT_MOVIE_SELECTION_BLOCKS
); );
}; };
} }
@@ -137,15 +137,15 @@ mod utils {
}]); }]);
app.data.radarr_data = radarr_data; app.data.radarr_data = radarr_data;
$handler::with(&DEFAULT_KEYBINDINGS.edit.key, &mut app, &$block, &None).handle(); $handler::with(DEFAULT_KEYBINDINGS.edit.key, &mut app, $block, None).handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&(ActiveRadarrBlock::EditCollectionPrompt, Some($context)).into() (ActiveRadarrBlock::EditCollectionPrompt, $context).into()
); );
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.get_active_block(), app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditCollectionToggleMonitored ActiveRadarrBlock::EditCollectionToggleMonitored
); );
assert_eq!( assert_eq!(
app app
@@ -224,52 +224,8 @@ mod utils {
); );
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block.blocks, app.data.radarr_data.selected_block.blocks,
&EDIT_COLLECTION_SELECTION_BLOCKS EDIT_COLLECTION_SELECTION_BLOCKS
); );
}; };
} }
#[macro_export]
macro_rules! assert_delete_prompt {
($block:expr, $expected_block:expr) => {
let mut app = App::default();
RadarrHandler::with(&DELETE_KEY, &mut app, &$block, &None).handle();
assert_eq!(app.get_current_route(), &$expected_block.into());
};
($handler:ident, $block:expr, $expected_block:expr) => {
let mut app = App::default();
$handler::with(&DELETE_KEY, &mut app, &$block, &None).handle();
assert_eq!(app.get_current_route(), &$expected_block.into());
};
($app:expr, $block:expr, $expected_block:expr) => {
RadarrHandler::with(&DELETE_KEY, &mut $app, &$block, &None).handle();
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();
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();
assert_eq!(app.get_current_route(), &$block.into());
assert!(app.should_refresh);
};
}
} }
@@ -27,23 +27,23 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.main_tabs.set_index(index); app.data.radarr_data.main_tabs.set_index(index);
handle_change_tab_left_right_keys(&mut app, &DEFAULT_KEYBINDINGS.left.key); handle_change_tab_left_right_keys(&mut app, DEFAULT_KEYBINDINGS.left.key);
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&left_block.into() left_block.into()
); );
assert_eq!(app.get_current_route(), &left_block.into()); assert_eq!(app.get_current_route(), left_block.into());
app.data.radarr_data.main_tabs.set_index(index); app.data.radarr_data.main_tabs.set_index(index);
handle_change_tab_left_right_keys(&mut app, &DEFAULT_KEYBINDINGS.right.key); handle_change_tab_left_right_keys(&mut app, DEFAULT_KEYBINDINGS.right.key);
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&right_block.into() right_block.into()
); );
assert_eq!(app.get_current_route(), &right_block.into()); assert_eq!(app.get_current_route(), right_block.into());
} }
#[rstest] #[rstest]
@@ -213,7 +213,7 @@ mod tests {
#[test] #[test]
fn test_radarr_handler_accepts() { fn test_radarr_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
assert!(RadarrHandler::accepts(&active_radarr_block)); assert!(RadarrHandler::accepts(active_radarr_block));
}) })
} }
@@ -223,10 +223,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = RadarrHandler::with( let handler = RadarrHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
@@ -2,33 +2,53 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys; 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::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::{HorizontallyScrollableText, Scrollable}; use crate::models::servarr_models::RootFolder;
use crate::models::HorizontallyScrollableText;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys}; use crate::{handle_table_events, handle_text_box_keys, handle_text_box_left_right_keys};
#[cfg(test)] #[cfg(test)]
#[path = "root_folders_handler_tests.rs"] #[path = "root_folders_handler_tests.rs"]
mod root_folders_handler_tests; mod root_folders_handler_tests;
pub(super) struct RootFoldersHandler<'a, 'b> { pub(super) struct RootFoldersHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> RootFoldersHandler<'a, 'b> {
handle_table_events!(
self,
root_folders,
self.app.data.radarr_data.root_folders,
RootFolder
);
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn handle(&mut self) {
ROOT_FOLDERS_BLOCKS.contains(active_block) let root_folder_table_handling_config =
TableHandlingConfig::new(ActiveRadarrBlock::RootFolders.into());
if !self.handle_root_folders_table_events(root_folder_table_handling_config) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveRadarrBlock) -> bool {
ROOT_FOLDERS_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
) -> RootFoldersHandler<'a, 'b> { ) -> RootFoldersHandler<'a, 'b> {
RootFoldersHandler { RootFoldersHandler {
key, key,
@@ -38,7 +58,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -46,50 +66,38 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
!self.app.is_loading && !self.app.data.radarr_data.root_folders.is_empty() !self.app.is_loading && !self.app.data.radarr_data.root_folders.is_empty()
} }
fn handle_scroll_up(&mut self) { fn handle_scroll_up(&mut self) {}
if self.active_radarr_block == &ActiveRadarrBlock::RootFolders {
self.app.data.radarr_data.root_folders.scroll_up()
}
}
fn handle_scroll_down(&mut self) { fn handle_scroll_down(&mut self) {}
if self.active_radarr_block == &ActiveRadarrBlock::RootFolders {
self.app.data.radarr_data.root_folders.scroll_down()
}
}
fn handle_home(&mut self) { fn handle_home(&mut self) {
match self.active_radarr_block { if self.active_radarr_block == ActiveRadarrBlock::AddRootFolderPrompt {
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_to_top(), self
ActiveRadarrBlock::AddRootFolderPrompt => self
.app .app
.data .data
.radarr_data .radarr_data
.edit_root_folder .edit_root_folder
.as_mut() .as_mut()
.unwrap() .unwrap()
.scroll_home(), .scroll_home()
_ => (),
} }
} }
fn handle_end(&mut self) { fn handle_end(&mut self) {
match self.active_radarr_block { if self.active_radarr_block == ActiveRadarrBlock::AddRootFolderPrompt {
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_to_bottom(), self
ActiveRadarrBlock::AddRootFolderPrompt => self
.app .app
.data .data
.radarr_data .radarr_data
.edit_root_folder .edit_root_folder
.as_mut() .as_mut()
.unwrap() .unwrap()
.reset_offset(), .reset_offset()
_ => (),
} }
} }
fn handle_delete(&mut self) { fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::RootFolders { if self.active_radarr_block == ActiveRadarrBlock::RootFolders {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into()) .push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into())
@@ -121,7 +129,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
} }
_ if *self.active_radarr_block == ActiveRadarrBlock::AddRootFolderPrompt _ if self.active_radarr_block == ActiveRadarrBlock::AddRootFolderPrompt
&& !self && !self
.app .app
.data .data
@@ -161,10 +169,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
let key = self.key; let key = self.key;
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::RootFolders => match self.key { ActiveRadarrBlock::RootFolders => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true; self.app.should_refresh = true;
} }
_ if *key == DEFAULT_KEYBINDINGS.add.key => { _ if key == DEFAULT_KEYBINDINGS.add.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into());
@@ -181,7 +189,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
) )
} }
ActiveRadarrBlock::DeleteRootFolderPrompt => { ActiveRadarrBlock::DeleteRootFolderPrompt => {
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 = true;
self.app.data.radarr_data.prompt_confirm_action = self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteRootFolder(None)); Some(RadarrEvent::DeleteRootFolder(None));
@@ -1,6 +1,5 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_str_eq;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
@@ -12,108 +11,12 @@ mod tests {
use crate::models::servarr_models::RootFolder; use crate::models::servarr_models::RootFolder;
use crate::models::HorizontallyScrollableText; use crate::models::HorizontallyScrollableText;
mod test_handle_scroll_up_and_down {
use rstest::rstest;
use crate::models::servarr_models::RootFolder;
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
use super::*;
test_iterable_scroll!(
test_root_folders_scroll,
RootFoldersHandler,
root_folders,
simple_stateful_iterable_vec!(RootFolder, String, path),
ActiveRadarrBlock::RootFolders,
None,
path
);
#[rstest]
fn test_root_folders_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
.root_folders
.set_items(simple_stateful_iterable_vec!(RootFolder, String, path));
RootFoldersHandler::with(&key, &mut app, &ActiveRadarrBlock::RootFolders, &None).handle();
assert_str_eq!(
app.data.radarr_data.root_folders.current_selection().path,
"Test 1"
);
RootFoldersHandler::with(&key, &mut app, &ActiveRadarrBlock::RootFolders, &None).handle();
assert_str_eq!(
app.data.radarr_data.root_folders.current_selection().path,
"Test 1"
);
}
}
mod test_handle_home_end { mod test_handle_home_end {
use pretty_assertions::assert_eq;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use pretty_assertions::assert_eq;
use crate::models::servarr_models::RootFolder;
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
use super::*; use super::*;
use crate::models::servarr_models::RootFolder;
test_iterable_home_and_end!(
test_root_folders_home_end,
RootFoldersHandler,
root_folders,
extended_stateful_iterable_vec!(RootFolder, String, path),
ActiveRadarrBlock::RootFolders,
None,
path
);
#[test]
fn test_root_folders_home_end_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app
.data
.radarr_data
.root_folders
.set_items(extended_stateful_iterable_vec!(RootFolder, String, path));
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
)
.handle();
assert_str_eq!(
app.data.radarr_data.root_folders.current_selection().path,
"Test 1"
);
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
)
.handle();
assert_str_eq!(
app.data.radarr_data.root_folders.current_selection().path,
"Test 1"
);
}
#[test] #[test]
fn test_add_root_folder_prompt_home_end_keys() { fn test_add_root_folder_prompt_home_end_keys() {
@@ -126,10 +29,10 @@ mod tests {
app.data.radarr_data.edit_root_folder = Some("Test".into()); app.data.radarr_data.edit_root_folder = Some("Test".into());
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::AddRootFolderPrompt, ActiveRadarrBlock::AddRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -146,10 +49,10 @@ mod tests {
); );
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::AddRootFolderPrompt, ActiveRadarrBlock::AddRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -183,17 +86,11 @@ mod tests {
.root_folders .root_folders
.set_items(vec![RootFolder::default()]); .set_items(vec![RootFolder::default()]);
RootFoldersHandler::with( RootFoldersHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::RootFolders, None).handle();
&DELETE_KEY,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
)
.handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::DeleteRootFolderPrompt.into() ActiveRadarrBlock::DeleteRootFolderPrompt.into()
); );
} }
@@ -208,17 +105,11 @@ mod tests {
.root_folders .root_folders
.set_items(vec![RootFolder::default()]); .set_items(vec![RootFolder::default()]);
RootFoldersHandler::with( RootFoldersHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::RootFolders, None).handle();
&DELETE_KEY,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
)
.handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
} }
} }
@@ -238,21 +129,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(4); app.data.radarr_data.main_tabs.set_index(4);
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::RootFolders,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Blocklist.into() ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
); );
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
} }
#[rstest] #[rstest]
@@ -262,18 +150,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(4); app.data.radarr_data.main_tabs.set_index(4);
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::RootFolders,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Indexers.into() ActiveRadarrBlock::Indexers.into()
); );
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
} }
#[rstest] #[rstest]
@@ -283,20 +171,20 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
RootFoldersHandler::with( RootFoldersHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt, ActiveRadarrBlock::DeleteRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
assert!(app.data.radarr_data.prompt_confirm); assert!(app.data.radarr_data.prompt_confirm);
RootFoldersHandler::with( RootFoldersHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt, ActiveRadarrBlock::DeleteRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -309,10 +197,10 @@ mod tests {
app.data.radarr_data.edit_root_folder = Some("Test".into()); app.data.radarr_data.edit_root_folder = Some("Test".into());
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::AddRootFolderPrompt, ActiveRadarrBlock::AddRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -329,10 +217,10 @@ mod tests {
); );
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::AddRootFolderPrompt, ActiveRadarrBlock::AddRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -374,10 +262,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into());
RootFoldersHandler::with( RootFoldersHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AddRootFolderPrompt, ActiveRadarrBlock::AddRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -389,7 +277,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
} }
@@ -403,10 +291,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into());
RootFoldersHandler::with( RootFoldersHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AddRootFolderPrompt, ActiveRadarrBlock::AddRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -415,7 +303,7 @@ mod tests {
assert!(app.data.radarr_data.prompt_confirm_action.is_none()); assert!(app.data.radarr_data.prompt_confirm_action.is_none());
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AddRootFolderPrompt.into() ActiveRadarrBlock::AddRootFolderPrompt.into()
); );
} }
@@ -432,10 +320,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into());
RootFoldersHandler::with( RootFoldersHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt, ActiveRadarrBlock::DeleteRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -446,7 +334,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
} }
@@ -462,10 +350,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into());
RootFoldersHandler::with( RootFoldersHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt, ActiveRadarrBlock::DeleteRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -473,7 +361,7 @@ mod tests {
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
} }
} }
@@ -493,16 +381,16 @@ mod tests {
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
RootFoldersHandler::with( RootFoldersHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt, ActiveRadarrBlock::DeleteRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -516,16 +404,16 @@ mod tests {
app.should_ignore_quit_key = true; app.should_ignore_quit_key = true;
RootFoldersHandler::with( RootFoldersHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::AddRootFolderPrompt, ActiveRadarrBlock::AddRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
assert!(app.data.radarr_data.edit_root_folder.is_none()); assert!(app.data.radarr_data.edit_root_folder.is_none());
@@ -541,11 +429,11 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
RootFoldersHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::RootFolders, &None).handle(); RootFoldersHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::RootFolders, None).handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
assert!(app.error.text.is_empty()); assert!(app.error.text.is_empty());
} }
@@ -568,16 +456,16 @@ mod tests {
.set_items(vec![RootFolder::default()]); .set_items(vec![RootFolder::default()]);
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.add.key, DEFAULT_KEYBINDINGS.add.key,
&mut app, &mut app,
&ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::RootFolders,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AddRootFolderPrompt.into() ActiveRadarrBlock::AddRootFolderPrompt.into()
); );
assert!(app.should_ignore_quit_key); assert!(app.should_ignore_quit_key);
assert!(app.data.radarr_data.edit_root_folder.is_some()); assert!(app.data.radarr_data.edit_root_folder.is_some());
@@ -595,16 +483,16 @@ mod tests {
.set_items(vec![RootFolder::default()]); .set_items(vec![RootFolder::default()]);
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.add.key, DEFAULT_KEYBINDINGS.add.key,
&mut app, &mut app,
&ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::RootFolders,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
assert!(!app.should_ignore_quit_key); assert!(!app.should_ignore_quit_key);
assert!(app.data.radarr_data.edit_root_folder.is_none()); assert!(app.data.radarr_data.edit_root_folder.is_none());
@@ -621,16 +509,16 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::RootFolders,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
assert!(app.should_refresh); assert!(app.should_refresh);
} }
@@ -647,16 +535,16 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::RootFolders,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
assert!(!app.should_refresh); assert!(!app.should_refresh);
} }
@@ -672,10 +560,10 @@ mod tests {
app.data.radarr_data.edit_root_folder = Some("/nfs/test".into()); app.data.radarr_data.edit_root_folder = Some("/nfs/test".into());
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key, DEFAULT_KEYBINDINGS.backspace.key,
&mut app, &mut app,
&ActiveRadarrBlock::AddRootFolderPrompt, ActiveRadarrBlock::AddRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -696,10 +584,10 @@ mod tests {
app.data.radarr_data.edit_root_folder = Some(HorizontallyScrollableText::default()); app.data.radarr_data.edit_root_folder = Some(HorizontallyScrollableText::default());
RootFoldersHandler::with( RootFoldersHandler::with(
&Key::Char('h'), Key::Char('h'),
&mut app, &mut app,
&ActiveRadarrBlock::AddRootFolderPrompt, ActiveRadarrBlock::AddRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -721,10 +609,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into());
RootFoldersHandler::with( RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key, DEFAULT_KEYBINDINGS.confirm.key,
&mut app, &mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt, ActiveRadarrBlock::DeleteRootFolderPrompt,
&None, None,
) )
.handle(); .handle();
@@ -735,7 +623,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into() ActiveRadarrBlock::RootFolders.into()
); );
} }
} }
@@ -744,9 +632,9 @@ mod tests {
fn test_root_folders_handler_accepts() { fn test_root_folders_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if ROOT_FOLDERS_BLOCKS.contains(&active_radarr_block) { if ROOT_FOLDERS_BLOCKS.contains(&active_radarr_block) {
assert!(RootFoldersHandler::accepts(&active_radarr_block)); assert!(RootFoldersHandler::accepts(active_radarr_block));
} else { } else {
assert!(!RootFoldersHandler::accepts(&active_radarr_block)); assert!(!RootFoldersHandler::accepts(active_radarr_block));
} }
}) })
} }
@@ -757,10 +645,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = RootFoldersHandler::with( let handler = RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::RootFolders,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -772,10 +660,10 @@ mod tests {
app.is_loading = false; app.is_loading = false;
let handler = RootFoldersHandler::with( let handler = RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::RootFolders,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -792,10 +680,10 @@ mod tests {
.root_folders .root_folders
.set_items(vec![RootFolder::default()]); .set_items(vec![RootFolder::default()]);
let handler = RootFoldersHandler::with( let handler = RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::RootFolders,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
+16 -16
View File
@@ -14,10 +14,10 @@ mod system_details_handler;
mod system_handler_tests; mod system_handler_tests;
pub(super) struct SystemHandler<'a, 'b> { pub(super) struct SystemHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b> {
@@ -31,15 +31,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
} }
} }
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
SystemDetailsHandler::accepts(active_block) || active_block == &ActiveRadarrBlock::System SystemDetailsHandler::accepts(active_block) || active_block == ActiveRadarrBlock::System
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> SystemHandler<'a, 'b> { ) -> SystemHandler<'a, 'b> {
SystemHandler { SystemHandler {
key, key,
@@ -49,7 +49,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
@@ -71,7 +71,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
fn handle_delete(&mut self) {} fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) { fn handle_left_right_action(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::System { if self.active_radarr_block == ActiveRadarrBlock::System {
handle_change_tab_left_right_keys(self.app, self.key); handle_change_tab_left_right_keys(self.app, self.key);
} }
} }
@@ -83,18 +83,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
} }
fn handle_char_key_event(&mut self) { fn handle_char_key_event(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::System { if self.active_radarr_block == ActiveRadarrBlock::System {
let key = self.key; let key = self.key;
match self.key { match self.key {
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true; self.app.should_refresh = true;
} }
_ if *key == DEFAULT_KEYBINDINGS.events.key => { _ if key == DEFAULT_KEYBINDINGS.events.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::SystemQueuedEvents.into()); .push_navigation_stack(ActiveRadarrBlock::SystemQueuedEvents.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.logs.key => { _ if key == DEFAULT_KEYBINDINGS.logs.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::SystemLogs.into()); .push_navigation_stack(ActiveRadarrBlock::SystemLogs.into());
@@ -106,12 +106,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
.set_items(self.app.data.radarr_data.logs.items.to_vec()); .set_items(self.app.data.radarr_data.logs.items.to_vec());
self.app.data.radarr_data.log_details.scroll_to_bottom(); self.app.data.radarr_data.log_details.scroll_to_bottom();
} }
_ if *key == DEFAULT_KEYBINDINGS.tasks.key => { _ if key == DEFAULT_KEYBINDINGS.tasks.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into()); .push_navigation_stack(ActiveRadarrBlock::SystemTasks.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.update.key => { _ if key == DEFAULT_KEYBINDINGS.update.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::SystemUpdates.into()); .push_navigation_stack(ActiveRadarrBlock::SystemUpdates.into());
@@ -12,22 +12,22 @@ use crate::network::radarr_network::RadarrEvent;
mod system_details_handler_tests; mod system_details_handler_tests;
pub(super) struct SystemDetailsHandler<'a, 'b> { pub(super) struct SystemDetailsHandler<'a, 'b> {
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>, _context: Option<ActiveRadarrBlock>,
} }
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler<'a, 'b> { impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool { fn accepts(active_block: ActiveRadarrBlock) -> bool {
SYSTEM_DETAILS_BLOCKS.contains(active_block) SYSTEM_DETAILS_BLOCKS.contains(&active_block)
} }
fn with( fn with(
key: &'a Key, key: Key,
app: &'a mut App<'b>, app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock, active_block: ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>, context: Option<ActiveRadarrBlock>,
) -> SystemDetailsHandler<'a, 'b> { ) -> SystemDetailsHandler<'a, 'b> {
SystemDetailsHandler { SystemDetailsHandler {
key, key,
@@ -37,13 +37,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
} }
} }
fn get_key(&self) -> &Key { fn get_key(&self) -> Key {
self.key self.key
} }
fn is_ready(&self) -> bool { fn is_ready(&self) -> bool {
!self.app.is_loading !self.app.is_loading
&& (!self.app.data.radarr_data.log_details.is_empty() && (!self.app.data.radarr_data.log_details.is_empty()
|| !self.app.data.radarr_data.tasks.is_empty()
|| !self.app.data.radarr_data.updates.is_empty()) || !self.app.data.radarr_data.updates.is_empty())
} }
@@ -100,7 +101,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::SystemLogs => match self.key { ActiveRadarrBlock::SystemLogs => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.left.key => { _ if key == DEFAULT_KEYBINDINGS.left.key => {
self self
.app .app
.data .data
@@ -110,7 +111,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
.iter() .iter()
.for_each(|log| log.scroll_right()); .for_each(|log| log.scroll_right());
} }
_ if *key == DEFAULT_KEYBINDINGS.right.key => { _ if key == DEFAULT_KEYBINDINGS.right.key => {
self self
.app .app
.data .data
@@ -163,14 +164,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
} }
fn handle_char_key_event(&mut self) { fn handle_char_key_event(&mut self) {
if SYSTEM_DETAILS_BLOCKS.contains(self.active_radarr_block) if SYSTEM_DETAILS_BLOCKS.contains(&self.active_radarr_block)
&& self.key == &DEFAULT_KEYBINDINGS.refresh.key && self.key == DEFAULT_KEYBINDINGS.refresh.key
{ {
self.app.should_refresh = true; self.app.should_refresh = true;
} }
if self.active_radarr_block == &ActiveRadarrBlock::SystemTaskStartConfirmPrompt if self.active_radarr_block == ActiveRadarrBlock::SystemTaskStartConfirmPrompt
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key && self.key == DEFAULT_KEYBINDINGS.confirm.key
{ {
self.app.data.radarr_data.prompt_confirm = true; self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::StartTask(None)); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::StartTask(None));
@@ -26,6 +26,7 @@ mod tests {
test_iterable_scroll!( test_iterable_scroll!(
test_log_details_scroll, test_log_details_scroll,
SystemDetailsHandler, SystemDetailsHandler,
radarr_data,
log_details, log_details,
simple_stateful_iterable_vec!(HorizontallyScrollableText, String, text), simple_stateful_iterable_vec!(HorizontallyScrollableText, String, text),
ActiveRadarrBlock::SystemLogs, ActiveRadarrBlock::SystemLogs,
@@ -49,14 +50,14 @@ mod tests {
text text
)); ));
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemLogs, &None).handle(); SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemLogs, None).handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.log_details.current_selection().text, app.data.radarr_data.log_details.current_selection().text,
"Test 1" "Test 1"
); );
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemLogs, &None).handle(); SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemLogs, None).handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.log_details.current_selection().text, app.data.radarr_data.log_details.current_selection().text,
@@ -76,14 +77,14 @@ mod tests {
.tasks .tasks
.set_items(simple_stateful_iterable_vec!(RadarrTask, String, name)); .set_items(simple_stateful_iterable_vec!(RadarrTask, String, name));
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle(); SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.tasks.current_selection().name, app.data.radarr_data.tasks.current_selection().name,
"Test 2" "Test 2"
); );
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle(); SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.tasks.current_selection().name, app.data.radarr_data.tasks.current_selection().name,
@@ -104,14 +105,14 @@ mod tests {
.tasks .tasks
.set_items(simple_stateful_iterable_vec!(RadarrTask, String, name)); .set_items(simple_stateful_iterable_vec!(RadarrTask, String, name));
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle(); SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.tasks.current_selection().name, app.data.radarr_data.tasks.current_selection().name,
"Test 1" "Test 1"
); );
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle(); SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.tasks.current_selection().name, app.data.radarr_data.tasks.current_selection().name,
@@ -131,26 +132,16 @@ mod tests {
.queued_events .queued_events
.set_items(simple_stateful_iterable_vec!(QueueEvent, String, name)); .set_items(simple_stateful_iterable_vec!(QueueEvent, String, name));
SystemDetailsHandler::with( SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemQueuedEvents, None)
&key, .handle();
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
)
.handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.queued_events.current_selection().name, app.data.radarr_data.queued_events.current_selection().name,
"Test 2" "Test 2"
); );
SystemDetailsHandler::with( SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemQueuedEvents, None)
&key, .handle();
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
)
.handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.queued_events.current_selection().name, app.data.radarr_data.queued_events.current_selection().name,
@@ -171,26 +162,16 @@ mod tests {
.queued_events .queued_events
.set_items(simple_stateful_iterable_vec!(QueueEvent, String, name)); .set_items(simple_stateful_iterable_vec!(QueueEvent, String, name));
SystemDetailsHandler::with( SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemQueuedEvents, None)
&key, .handle();
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
)
.handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.queued_events.current_selection().name, app.data.radarr_data.queued_events.current_selection().name,
"Test 1" "Test 1"
); );
SystemDetailsHandler::with( SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemQueuedEvents, None)
&key, .handle();
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
)
.handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.queued_events.current_selection().name, app.data.radarr_data.queued_events.current_selection().name,
@@ -204,20 +185,20 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned());
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.up.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.data.radarr_data.updates.offset, 0); assert_eq!(app.data.radarr_data.updates.offset, 0);
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.down.key, DEFAULT_KEYBINDINGS.down.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
) )
.handle(); .handle();
@@ -231,20 +212,20 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned());
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.up.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.data.radarr_data.updates.offset, 0); assert_eq!(app.data.radarr_data.updates.offset, 0);
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.down.key, DEFAULT_KEYBINDINGS.down.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
) )
.handle(); .handle();
@@ -261,6 +242,7 @@ mod tests {
test_iterable_home_and_end!( test_iterable_home_and_end!(
test_log_details_home_end, test_log_details_home_end,
SystemDetailsHandler, SystemDetailsHandler,
radarr_data,
log_details, log_details,
extended_stateful_iterable_vec!(HorizontallyScrollableText, String, text), extended_stateful_iterable_vec!(HorizontallyScrollableText, String, text),
ActiveRadarrBlock::SystemLogs, ActiveRadarrBlock::SystemLogs,
@@ -283,10 +265,10 @@ mod tests {
)); ));
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemLogs, ActiveRadarrBlock::SystemLogs,
&None, None,
) )
.handle(); .handle();
@@ -296,10 +278,10 @@ mod tests {
); );
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemLogs, ActiveRadarrBlock::SystemLogs,
&None, None,
) )
.handle(); .handle();
@@ -321,10 +303,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(RadarrTask, String, name)); .set_items(extended_stateful_iterable_vec!(RadarrTask, String, name));
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTasks, ActiveRadarrBlock::SystemTasks,
&None, None,
) )
.handle(); .handle();
@@ -334,10 +316,10 @@ mod tests {
); );
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTasks, ActiveRadarrBlock::SystemTasks,
&None, None,
) )
.handle(); .handle();
@@ -360,10 +342,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(RadarrTask, String, name)); .set_items(extended_stateful_iterable_vec!(RadarrTask, String, name));
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTasks, ActiveRadarrBlock::SystemTasks,
&None, None,
) )
.handle(); .handle();
@@ -373,10 +355,10 @@ mod tests {
); );
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTasks, ActiveRadarrBlock::SystemTasks,
&None, None,
) )
.handle(); .handle();
@@ -398,10 +380,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(QueueEvent, String, name)); .set_items(extended_stateful_iterable_vec!(QueueEvent, String, name));
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemQueuedEvents, ActiveRadarrBlock::SystemQueuedEvents,
&None, None,
) )
.handle(); .handle();
@@ -411,10 +393,10 @@ mod tests {
); );
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemQueuedEvents, ActiveRadarrBlock::SystemQueuedEvents,
&None, None,
) )
.handle(); .handle();
@@ -437,10 +419,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(QueueEvent, String, name)); .set_items(extended_stateful_iterable_vec!(QueueEvent, String, name));
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemQueuedEvents, ActiveRadarrBlock::SystemQueuedEvents,
&None, None,
) )
.handle(); .handle();
@@ -450,10 +432,10 @@ mod tests {
); );
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemQueuedEvents, ActiveRadarrBlock::SystemQueuedEvents,
&None, None,
) )
.handle(); .handle();
@@ -469,20 +451,20 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned());
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.data.radarr_data.updates.offset, 1); assert_eq!(app.data.radarr_data.updates.offset, 1);
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
) )
.handle(); .handle();
@@ -496,20 +478,20 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned());
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key, DEFAULT_KEYBINDINGS.end.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.data.radarr_data.updates.offset, 0); assert_eq!(app.data.radarr_data.updates.offset, 0);
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key, DEFAULT_KEYBINDINGS.home.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
) )
.handle(); .handle();
@@ -534,10 +516,10 @@ mod tests {
.set_items(vec!["t1".into(), "t22".into()]); .set_items(vec!["t1".into(), "t22".into()]);
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
@@ -545,10 +527,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "t22"); assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "t22");
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
@@ -556,10 +538,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "22"); assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "22");
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
@@ -567,10 +549,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "2"); assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "2");
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
@@ -578,10 +560,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), ""); assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "");
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
@@ -589,10 +571,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), ""); assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "");
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
@@ -600,10 +582,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "2"); assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "2");
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
@@ -611,10 +593,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "22"); assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "22");
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
@@ -629,20 +611,20 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
SystemDetailsHandler::with( SystemDetailsHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt, ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None, None,
) )
.handle(); .handle();
assert!(app.data.radarr_data.prompt_confirm); assert!(app.data.radarr_data.prompt_confirm);
SystemDetailsHandler::with( SystemDetailsHandler::with(
&key, key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt, ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None, None,
) )
.handle(); .handle();
@@ -664,17 +646,12 @@ mod tests {
let mut app = App::default(); let mut app = App::default();
app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned());
SystemDetailsHandler::with( SystemDetailsHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::SystemTasks, None)
&SUBMIT_KEY, .handle();
&mut app,
&ActiveRadarrBlock::SystemTasks,
&None,
)
.handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into() ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into()
); );
} }
@@ -685,17 +662,12 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into());
app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned());
SystemDetailsHandler::with( SystemDetailsHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::SystemTasks, None)
&SUBMIT_KEY, .handle();
&mut app,
&ActiveRadarrBlock::SystemTasks,
&None,
)
.handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into() ActiveRadarrBlock::SystemTasks.into()
); );
} }
@@ -708,10 +680,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into());
SystemDetailsHandler::with( SystemDetailsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt, ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None, None,
) )
.handle(); .handle();
@@ -722,7 +694,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into() ActiveRadarrBlock::SystemTasks.into()
); );
} }
@@ -734,10 +706,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into());
SystemDetailsHandler::with( SystemDetailsHandler::with(
&SUBMIT_KEY, SUBMIT_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt, ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None, None,
) )
.handle(); .handle();
@@ -745,7 +717,7 @@ mod tests {
assert_eq!(app.data.radarr_data.prompt_confirm_action, None); assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into() ActiveRadarrBlock::SystemTasks.into()
); );
} }
} }
@@ -776,10 +748,9 @@ mod tests {
.log_details .log_details
.set_items(vec![HorizontallyScrollableText::default()]); .set_items(vec![HorizontallyScrollableText::default()]);
SystemDetailsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::SystemLogs, &None) SystemDetailsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::SystemLogs, None).handle();
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(app.data.radarr_data.log_details.items.is_empty()); assert!(app.data.radarr_data.log_details.items.is_empty());
} }
@@ -795,10 +766,9 @@ mod tests {
.tasks .tasks
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
SystemDetailsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::SystemTasks, &None) SystemDetailsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
} }
#[rstest] #[rstest]
@@ -814,14 +784,14 @@ mod tests {
.set_items(vec![QueueEvent::default()]); .set_items(vec![QueueEvent::default()]);
SystemDetailsHandler::with( SystemDetailsHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::SystemQueuedEvents, ActiveRadarrBlock::SystemQueuedEvents,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
} }
#[rstest] #[rstest]
@@ -831,10 +801,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::System.into()); app.push_navigation_stack(ActiveRadarrBlock::System.into());
app.push_navigation_stack(ActiveRadarrBlock::SystemUpdates.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemUpdates.into());
SystemDetailsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::SystemUpdates, &None) SystemDetailsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::SystemUpdates, None)
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
} }
#[test] #[test]
@@ -845,16 +815,16 @@ mod tests {
app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.prompt_confirm = true;
SystemDetailsHandler::with( SystemDetailsHandler::with(
&ESC_KEY, ESC_KEY,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt, ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into() ActiveRadarrBlock::SystemTasks.into()
); );
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
} }
@@ -882,14 +852,14 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into()); app.push_navigation_stack(active_radarr_block.into());
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &active_radarr_block.into()); assert_eq!(app.get_current_route(), active_radarr_block.into());
assert!(app.should_refresh); assert!(app.should_refresh);
} }
@@ -909,14 +879,14 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into()); app.push_navigation_stack(active_radarr_block.into());
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&active_radarr_block, active_radarr_block,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &active_radarr_block.into()); assert_eq!(app.get_current_route(), active_radarr_block.into());
assert!(!app.should_refresh); assert!(!app.should_refresh);
} }
@@ -928,10 +898,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into());
SystemDetailsHandler::with( SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key, DEFAULT_KEYBINDINGS.confirm.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt, ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None, None,
) )
.handle(); .handle();
@@ -942,7 +912,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into() ActiveRadarrBlock::SystemTasks.into()
); );
} }
} }
@@ -951,9 +921,9 @@ mod tests {
fn test_system_details_handler_accepts() { fn test_system_details_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if SYSTEM_DETAILS_BLOCKS.contains(&active_radarr_block) { if SYSTEM_DETAILS_BLOCKS.contains(&active_radarr_block) {
assert!(SystemDetailsHandler::accepts(&active_radarr_block)); assert!(SystemDetailsHandler::accepts(active_radarr_block));
} else { } else {
assert!(!SystemDetailsHandler::accepts(&active_radarr_block)); assert!(!SystemDetailsHandler::accepts(active_radarr_block));
} }
}) })
} }
@@ -964,25 +934,25 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let handler = SystemDetailsHandler::with( let handler = SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
} }
#[test] #[test]
fn test_system_details_handler_not_ready_when_both_log_details_and_updates_are_empty() { fn test_system_details_handler_not_ready_when_log_details_and_updates_and_tasks_are_empty() {
let mut app = App::default(); let mut app = App::default();
app.is_loading = false; app.is_loading = false;
let handler = SystemDetailsHandler::with( let handler = SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
); );
assert!(!handler.is_ready()); assert!(!handler.is_ready());
@@ -999,10 +969,30 @@ mod tests {
.set_items(vec![HorizontallyScrollableText::default()]); .set_items(vec![HorizontallyScrollableText::default()]);
let handler = SystemDetailsHandler::with( let handler = SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
);
assert!(handler.is_ready());
}
#[test]
fn test_system_details_handler_ready_when_not_loading_and_tasks_is_not_empty() {
let mut app = App::default();
app.is_loading = false;
app
.data
.radarr_data
.tasks
.set_items(vec![RadarrTask::default()]);
let handler = SystemDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::SystemTasks,
None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
@@ -1015,10 +1005,10 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned());
let handler = SystemDetailsHandler::with( let handler = SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key, DEFAULT_KEYBINDINGS.esc.key,
&mut app, &mut app,
&ActiveRadarrBlock::SystemUpdates, ActiveRadarrBlock::SystemUpdates,
&None, None,
); );
assert!(handler.is_ready()); assert!(handler.is_ready());
@@ -1,6 +1,5 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_eq;
use rstest::rstest; use rstest::rstest;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@@ -28,18 +27,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(6); app.data.radarr_data.main_tabs.set_index(6);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.left.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Indexers.into() ActiveRadarrBlock::Indexers.into()
); );
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
} }
#[rstest] #[rstest]
@@ -49,18 +48,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(6); app.data.radarr_data.main_tabs.set_index(6);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.right.key, DEFAULT_KEYBINDINGS.right.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(), app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Movies.into() ActiveRadarrBlock::Movies.into()
); );
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
} }
} }
@@ -79,9 +78,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::System.into()); app.push_navigation_stack(ActiveRadarrBlock::System.into());
app.push_navigation_stack(ActiveRadarrBlock::System.into()); app.push_navigation_stack(ActiveRadarrBlock::System.into());
SystemHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::System, &None).handle(); SystemHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::System, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(app.error.text.is_empty()); assert!(app.error.text.is_empty());
} }
} }
@@ -112,16 +111,16 @@ mod tests {
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key, DEFAULT_KEYBINDINGS.update.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemUpdates.into() ActiveRadarrBlock::SystemUpdates.into()
); );
} }
@@ -146,14 +145,14 @@ mod tests {
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key, DEFAULT_KEYBINDINGS.update.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
} }
#[test] #[test]
@@ -175,16 +174,16 @@ mod tests {
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.events.key, DEFAULT_KEYBINDINGS.events.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemQueuedEvents.into() ActiveRadarrBlock::SystemQueuedEvents.into()
); );
} }
@@ -209,14 +208,14 @@ mod tests {
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.events.key, DEFAULT_KEYBINDINGS.events.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
} }
#[test] #[test]
@@ -239,14 +238,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::System.into()); app.push_navigation_stack(ActiveRadarrBlock::System.into());
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(app.should_refresh); assert!(app.should_refresh);
} }
@@ -272,14 +271,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::System.into()); app.push_navigation_stack(ActiveRadarrBlock::System.into());
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(!app.should_refresh); assert!(!app.should_refresh);
} }
@@ -302,16 +301,16 @@ mod tests {
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.logs.key, DEFAULT_KEYBINDINGS.logs.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemLogs.into() ActiveRadarrBlock::SystemLogs.into()
); );
assert_eq!( assert_eq!(
app.data.radarr_data.log_details.items, app.data.radarr_data.log_details.items,
@@ -344,14 +343,14 @@ mod tests {
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.logs.key, DEFAULT_KEYBINDINGS.logs.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(app.data.radarr_data.log_details.is_empty()); assert!(app.data.radarr_data.log_details.is_empty());
} }
@@ -374,16 +373,16 @@ mod tests {
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.tasks.key, DEFAULT_KEYBINDINGS.tasks.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!( assert_eq!(
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into() ActiveRadarrBlock::SystemTasks.into()
); );
} }
@@ -408,14 +407,14 @@ mod tests {
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
SystemHandler::with( SystemHandler::with(
&DEFAULT_KEYBINDINGS.tasks.key, DEFAULT_KEYBINDINGS.tasks.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
} }
} }
@@ -444,9 +443,9 @@ mod tests {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| { ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if system_blocks.contains(&active_radarr_block) { if system_blocks.contains(&active_radarr_block) {
assert!(SystemHandler::accepts(&active_radarr_block)); assert!(SystemHandler::accepts(active_radarr_block));
} else { } else {
assert!(!SystemHandler::accepts(&active_radarr_block)); assert!(!SystemHandler::accepts(active_radarr_block));
} }
}) })
} }
@@ -457,10 +456,10 @@ mod tests {
app.is_loading = true; app.is_loading = true;
let system_handler = SystemHandler::with( let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key, DEFAULT_KEYBINDINGS.update.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
); );
assert!(!system_handler.is_ready()); assert!(!system_handler.is_ready());
@@ -482,10 +481,10 @@ mod tests {
.set_items(vec![QueueEvent::default()]); .set_items(vec![QueueEvent::default()]);
let system_handler = SystemHandler::with( let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key, DEFAULT_KEYBINDINGS.update.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
); );
assert!(!system_handler.is_ready()); assert!(!system_handler.is_ready());
@@ -503,10 +502,10 @@ mod tests {
.set_items(vec![QueueEvent::default()]); .set_items(vec![QueueEvent::default()]);
let system_handler = SystemHandler::with( let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key, DEFAULT_KEYBINDINGS.update.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
); );
assert!(!system_handler.is_ready()); assert!(!system_handler.is_ready());
@@ -524,10 +523,10 @@ mod tests {
.set_items(vec![RadarrTask::default()]); .set_items(vec![RadarrTask::default()]);
let system_handler = SystemHandler::with( let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key, DEFAULT_KEYBINDINGS.update.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
); );
assert!(!system_handler.is_ready()); assert!(!system_handler.is_ready());
@@ -550,10 +549,10 @@ mod tests {
.set_items(vec![QueueEvent::default()]); .set_items(vec![QueueEvent::default()]);
let system_handler = SystemHandler::with( let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key, DEFAULT_KEYBINDINGS.update.key,
&mut app, &mut app,
&ActiveRadarrBlock::System, ActiveRadarrBlock::System,
&None, None,
); );
assert!(system_handler.is_ready()); assert!(system_handler.is_ready());
@@ -0,0 +1,621 @@
#[cfg(test)]
mod tests {
use std::cmp::Ordering;
use chrono::DateTime;
use pretty_assertions::{assert_eq, assert_str_eq};
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::blocklist::{blocklist_sorting_options, BlocklistHandler};
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, BLOCKLIST_BLOCKS};
use crate::models::servarr_models::{Language, Quality, QualityWrapper};
use crate::models::sonarr_models::BlocklistItem;
mod test_handle_delete {
use pretty_assertions::assert_eq;
use super::*;
const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key;
#[test]
fn test_delete_blocklist_item_prompt() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::DeleteBlocklistItemPrompt.into()
);
}
#[test]
fn test_delete_blocklist_item_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
}
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
#[rstest]
fn test_blocklist_tab_left(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(2);
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Downloads.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
}
#[rstest]
fn test_blocklist_tab_right(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(2);
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::History.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::History.into());
}
#[rstest]
fn test_blocklist_left_right_prompt_toggle(
#[values(
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt
)]
active_sonarr_block: ActiveSonarrBlock,
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(key, &mut app, active_sonarr_block, None).handle();
assert!(app.data.sonarr_data.prompt_confirm);
BlocklistHandler::with(key, &mut app, active_sonarr_block, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[test]
fn test_blocklist_submit() {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::BlocklistItemDetails.into()
);
}
#[test]
fn test_blocklist_submit_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[rstest]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
SonarrEvent::DeleteBlocklistItem(None)
)]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt,
SonarrEvent::ClearBlocklist
)]
fn test_blocklist_prompt_confirm_submit(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
#[case] expected_action: SonarrEvent,
) {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.data.sonarr_data.prompt_confirm = true;
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), base_route.into());
}
#[rstest]
fn test_blocklist_prompt_decline_submit(
#[values(
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt
)]
prompt_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::DeleteBlocklistItemPrompt
)]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt
)]
fn test_blocklist_prompt_blocks_esc(
#[case] base_block: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(base_block.into());
app.push_navigation_stack(prompt_block.into());
app.data.sonarr_data.prompt_confirm = true;
BlocklistHandler::with(ESC_KEY, &mut app, prompt_block, None).handle();
assert_eq!(app.get_current_route(), base_block.into());
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[test]
fn test_esc_blocklist_item_details() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.push_navigation_stack(ActiveSonarrBlock::BlocklistItemDetails.into());
BlocklistHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::BlocklistItemDetails,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[rstest]
fn test_default_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.error = "test error".to_owned().into();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
assert!(app.error.text.is_empty());
}
}
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
#[test]
fn test_refresh_blocklist_key() {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
assert!(app.should_refresh);
}
#[test]
fn test_refresh_blocklist_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
assert!(!app.should_refresh);
}
#[test]
fn test_clear_blocklist_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.clear.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::BlocklistClearAllItemsPrompt.into()
);
}
#[test]
fn test_clear_blocklist_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.clear.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[rstest]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
SonarrEvent::DeleteBlocklistItem(None)
)]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt,
SonarrEvent::ClearBlocklist
)]
fn test_blocklist_prompt_confirm(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
#[case] expected_action: SonarrEvent,
) {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
prompt_block,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), base_route.into());
}
}
#[test]
fn test_blocklist_sorting_options_series_title() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering = |a, b| {
a.series_title
.as_ref()
.unwrap_or(&String::new())
.to_lowercase()
.cmp(
&b.series_title
.as_ref()
.unwrap_or(&String::new())
.to_lowercase(),
)
};
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[0].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Series Title");
}
#[test]
fn test_blocklist_sorting_options_source_title() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering = |a, b| {
a.source_title
.to_lowercase()
.cmp(&b.source_title.to_lowercase())
};
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[1].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Source Title");
}
#[test]
fn test_blocklist_sorting_options_language() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering = |a, b| {
let a_languages = a
.languages
.iter()
.map(|lang| lang.name.to_lowercase())
.collect::<Vec<String>>()
.join(", ");
let b_languages = b
.languages
.iter()
.map(|lang| lang.name.to_lowercase())
.collect::<Vec<String>>()
.join(", ");
a_languages.cmp(&b_languages)
};
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[2].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Language");
}
#[test]
fn test_blocklist_sorting_options_quality() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering = |a, b| {
a.quality
.quality
.name
.to_lowercase()
.cmp(&b.quality.quality.name.to_lowercase())
};
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[3].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Quality");
}
#[test]
fn test_blocklist_sorting_options_date() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering =
|a, b| a.date.cmp(&b.date);
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[4].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Date");
}
#[test]
fn test_blocklist_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if BLOCKLIST_BLOCKS.contains(&active_sonarr_block) {
assert!(BlocklistHandler::accepts(active_sonarr_block));
} else {
assert!(!BlocklistHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_blocklist_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = true;
let handler = BlocklistHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_blocklist_handler_not_ready_when_blocklist_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = false;
let handler = BlocklistHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_blocklist_handler_ready_when_not_loading_and_blocklist_is_not_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = false;
app
.data
.sonarr_data
.blocklist
.set_items(vec![BlocklistItem::default()]);
let handler = BlocklistHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
);
assert!(handler.is_ready());
}
fn blocklist_vec() -> Vec<BlocklistItem> {
vec![
BlocklistItem {
id: 3,
source_title: "test 1".to_owned(),
languages: vec![Language {
id: 1,
name: "telgu".to_owned(),
}],
quality: QualityWrapper {
quality: Quality {
name: "HD - 1080p".to_owned(),
},
},
date: DateTime::from(DateTime::parse_from_rfc3339("2024-01-10T07:28:45Z").unwrap()),
series_title: Some("test 3".into()),
..BlocklistItem::default()
},
BlocklistItem {
id: 2,
source_title: "test 2".to_owned(),
languages: vec![Language {
id: 3,
name: "chinese".to_owned(),
}],
quality: QualityWrapper {
quality: Quality {
name: "SD - 720p".to_owned(),
},
},
date: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
series_title: Some("test 2".into()),
..BlocklistItem::default()
},
BlocklistItem {
id: 1,
source_title: "test 3".to_owned(),
languages: vec![Language {
id: 1,
name: "english".to_owned(),
}],
quality: QualityWrapper {
quality: Quality {
name: "HD - 1080p".to_owned(),
},
},
date: DateTime::from(DateTime::parse_from_rfc3339("2024-03-10T07:28:45Z").unwrap()),
series_title: None,
..BlocklistItem::default()
},
]
}
}
@@ -0,0 +1,232 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::sonarr_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::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, BLOCKLIST_BLOCKS};
use crate::models::sonarr_models::BlocklistItem;
use crate::models::stateful_table::SortOption;
use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)]
#[path = "blocklist_handler_tests.rs"]
mod blocklist_handler_tests;
pub(super) struct BlocklistHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> BlocklistHandler<'a, 'b> {
handle_table_events!(
self,
blocklist,
self.app.data.sonarr_data.blocklist,
BlocklistItem
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for BlocklistHandler<'a, 'b> {
fn handle(&mut self) {
let blocklist_table_handling_config =
TableHandlingConfig::new(ActiveSonarrBlock::Blocklist.into())
.sorting_block(ActiveSonarrBlock::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: ActiveSonarrBlock) -> bool {
BLOCKLIST_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> Self {
BlocklistHandler {
key,
app,
active_sonarr_block: active_block,
_context: context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.blocklist.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::Blocklist {
self
.app
.push_navigation_stack(ActiveSonarrBlock::DeleteBlocklistItemPrompt.into());
}
}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Blocklist => handle_change_tab_left_right_keys(self.app, self.key),
ActiveSonarrBlock::DeleteBlocklistItemPrompt
| ActiveSonarrBlock::BlocklistClearAllItemsPrompt => handle_prompt_toggle(self.app, self.key),
_ => {}
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteBlocklistItemPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteBlocklistItem(None));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::BlocklistClearAllItemsPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::ClearBlocklist);
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::Blocklist => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::BlocklistItemDetails.into());
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteBlocklistItemPrompt
| ActiveSonarrBlock::BlocklistClearAllItemsPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
ActiveSonarrBlock::BlocklistItemDetails | ActiveSonarrBlock::BlocklistSortPrompt => {
self.app.pop_navigation_stack();
}
_ => handle_clear_errors(self.app),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::Blocklist => match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if key == DEFAULT_KEYBINDINGS.clear.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::BlocklistClearAllItemsPrompt.into());
}
_ => (),
},
ActiveSonarrBlock::DeleteBlocklistItemPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteBlocklistItem(None));
self.app.pop_navigation_stack();
}
}
ActiveSonarrBlock::BlocklistClearAllItemsPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::ClearBlocklist);
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
fn blocklist_sorting_options() -> Vec<SortOption<BlocklistItem>> {
vec![
SortOption {
name: "Series Title",
cmp_fn: Some(|a, b| {
a.series_title
.as_ref()
.unwrap_or(&String::new())
.to_lowercase()
.cmp(
&b.series_title
.as_ref()
.unwrap_or(&String::new())
.to_lowercase(),
)
}),
},
SortOption {
name: "Source Title",
cmp_fn: Some(|a, b| {
a.source_title
.to_lowercase()
.cmp(&b.source_title.to_lowercase())
}),
},
SortOption {
name: "Language",
cmp_fn: Some(|a, b| {
let a_languages = a
.languages
.iter()
.map(|lang| lang.name.to_lowercase())
.collect::<Vec<String>>()
.join(", ");
let b_languages = b
.languages
.iter()
.map(|lang| lang.name.to_lowercase())
.collect::<Vec<String>>()
.join(", ");
a_languages.cmp(&b_languages)
}),
},
SortOption {
name: "Quality",
cmp_fn: Some(|a, b| {
a.quality
.quality
.name
.to_lowercase()
.cmp(&b.quality.quality.name.to_lowercase())
}),
},
SortOption {
name: "Date",
cmp_fn: Some(|a, b| a.date.cmp(&b.date)),
},
]
}
@@ -0,0 +1,442 @@
#[cfg(test)]
mod tests {
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::downloads::DownloadsHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DOWNLOADS_BLOCKS};
use crate::models::sonarr_models::DownloadRecord;
mod test_handle_delete {
use pretty_assertions::assert_eq;
use super::*;
const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key;
#[test]
fn test_delete_download_prompt() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Downloads, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::DeleteDownloadPrompt.into()
);
}
#[test]
fn test_delete_download_prompt_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Downloads, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
}
}
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
#[rstest]
fn test_downloads_tab_left(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(1);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Series.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
}
#[rstest]
fn test_downloads_tab_right(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(1);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[rstest]
fn test_downloads_left_right_prompt_toggle(
#[values(
ActiveSonarrBlock::DeleteDownloadPrompt,
ActiveSonarrBlock::UpdateDownloadsPrompt
)]
active_sonarr_block: ActiveSonarrBlock,
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
DownloadsHandler::with(key, &mut app, active_sonarr_block, None).handle();
assert!(app.data.sonarr_data.prompt_confirm);
DownloadsHandler::with(key, &mut app, active_sonarr_block, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[rstest]
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::DeleteDownloadPrompt,
SonarrEvent::DeleteDownload(None)
)]
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::UpdateDownloadsPrompt,
SonarrEvent::UpdateDownloads
)]
fn test_downloads_prompt_confirm_submit(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
#[case] expected_action: SonarrEvent,
) {
let mut app = App::default();
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
app.data.sonarr_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();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), base_route.into());
}
#[rstest]
#[case(ActiveSonarrBlock::Downloads, ActiveSonarrBlock::DeleteDownloadPrompt)]
#[case(ActiveSonarrBlock::Downloads, ActiveSonarrBlock::UpdateDownloadsPrompt)]
fn test_downloads_prompt_decline_submit(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
DownloadsHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert_eq!(app.get_current_route(), base_route.into());
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
#[case(ActiveSonarrBlock::Downloads, ActiveSonarrBlock::DeleteDownloadPrompt)]
#[case(ActiveSonarrBlock::Downloads, ActiveSonarrBlock::UpdateDownloadsPrompt)]
fn test_downloads_prompt_blocks_esc(
#[case] base_block: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(base_block.into());
app.push_navigation_stack(prompt_block.into());
app.data.sonarr_data.prompt_confirm = true;
DownloadsHandler::with(ESC_KEY, &mut app, prompt_block, None).handle();
assert_eq!(app.get_current_route(), base_block.into());
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[rstest]
fn test_default_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.error = "test error".to_owned().into();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
DownloadsHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::Downloads, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
assert!(app.error.text.is_empty());
}
}
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
#[test]
fn test_update_downloads_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::UpdateDownloadsPrompt.into()
);
}
#[test]
fn test_update_downloads_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
}
#[test]
fn test_refresh_downloads_key() {
let mut app = App::default();
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
assert!(app.should_refresh);
}
#[test]
fn test_refresh_downloads_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
assert!(!app.should_refresh);
}
#[rstest]
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::DeleteDownloadPrompt,
SonarrEvent::DeleteDownload(None)
)]
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::UpdateDownloadsPrompt,
SonarrEvent::UpdateDownloads
)]
fn test_downloads_prompt_confirm_submit(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
#[case] expected_action: SonarrEvent,
) {
let mut app = App::default();
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
prompt_block,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), base_route.into());
}
}
#[test]
fn test_downloads_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if DOWNLOADS_BLOCKS.contains(&active_sonarr_block) {
assert!(DownloadsHandler::accepts(active_sonarr_block));
} else {
assert!(!DownloadsHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_downloads_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = true;
let handler = DownloadsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_downloads_handler_not_ready_when_downloads_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = false;
let handler = DownloadsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_downloads_handler_ready_when_not_loading_and_downloads_is_not_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = false;
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
let handler = DownloadsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,157 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::sonarr_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::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DOWNLOADS_BLOCKS};
use crate::models::sonarr_models::DownloadRecord;
use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)]
#[path = "downloads_handler_tests.rs"]
mod downloads_handler_tests;
pub(super) struct DownloadsHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> DownloadsHandler<'a, 'b> {
handle_table_events!(
self,
downloads,
self.app.data.sonarr_data.downloads,
DownloadRecord
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DownloadsHandler<'a, 'b> {
fn handle(&mut self) {
let download_table_handling_config =
TableHandlingConfig::new(ActiveSonarrBlock::Downloads.into());
if !self.handle_downloads_table_events(download_table_handling_config) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
DOWNLOADS_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> DownloadsHandler<'a, 'b> {
DownloadsHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.downloads.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::Downloads {
self
.app
.push_navigation_stack(ActiveSonarrBlock::DeleteDownloadPrompt.into())
}
}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Downloads => handle_change_tab_left_right_keys(self.app, self.key),
ActiveSonarrBlock::DeleteDownloadPrompt | ActiveSonarrBlock::UpdateDownloadsPrompt => {
handle_prompt_toggle(self.app, self.key)
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteDownloadPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteDownload(None));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::UpdateDownloadsPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::UpdateDownloads);
}
self.app.pop_navigation_stack();
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteDownloadPrompt | ActiveSonarrBlock::UpdateDownloadsPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
_ => handle_clear_errors(self.app),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::Downloads => match self.key {
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::UpdateDownloadsPrompt.into());
}
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ => (),
},
ActiveSonarrBlock::DeleteDownloadPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteDownload(None));
self.app.pop_navigation_stack();
}
}
ActiveSonarrBlock::UpdateDownloadsPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::UpdateDownloads);
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
@@ -0,0 +1,414 @@
#[cfg(test)]
mod tests {
use std::cmp::Ordering;
use chrono::DateTime;
use pretty_assertions::{assert_eq, assert_str_eq};
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::history::{history_sorting_options, HistoryHandler};
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, HISTORY_BLOCKS};
use crate::models::servarr_models::{Language, Quality, QualityWrapper};
use crate::models::sonarr_models::{SonarrHistoryEventType, SonarrHistoryItem};
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
#[rstest]
fn test_history_tab_left(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::History.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(3);
HistoryHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::History,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[rstest]
fn test_history_tab_right(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::History.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(3);
HistoryHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::History,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[test]
fn test_history_submit() {
let mut app = App::default();
app.data.sonarr_data.history.set_items(history_vec());
app.push_navigation_stack(ActiveSonarrBlock::History.into());
HistoryHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::History, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::HistoryItemDetails.into()
);
}
#[test]
fn test_history_submit_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.data.sonarr_data.history.set_items(history_vec());
app.push_navigation_stack(ActiveSonarrBlock::History.into());
HistoryHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::History, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::History.into());
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::models::servarr_data::sonarr::sonarr_data::sonarr_test_utils::utils::create_test_sonarr_data;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[test]
fn test_esc_history_item_details() {
let mut app = App::default();
app
.data
.sonarr_data
.history
.set_items(vec![SonarrHistoryItem::default()]);
app.push_navigation_stack(ActiveSonarrBlock::History.into());
app.push_navigation_stack(ActiveSonarrBlock::HistoryItemDetails.into());
HistoryHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::HistoryItemDetails,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::History.into());
}
#[rstest]
fn test_default_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.error = "test error".to_owned().into();
app.push_navigation_stack(ActiveSonarrBlock::History.into());
app.push_navigation_stack(ActiveSonarrBlock::History.into());
app.data.sonarr_data = create_test_sonarr_data();
app
.data
.sonarr_data
.history
.set_items(vec![SonarrHistoryItem::default()]);
HistoryHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::History, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::History.into());
assert!(app.error.text.is_empty());
}
}
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn test_refresh_history_key() {
let mut app = App::default();
app.data.sonarr_data.history.set_items(history_vec());
app.push_navigation_stack(ActiveSonarrBlock::History.into());
HistoryHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::History,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::History.into());
assert!(app.should_refresh);
}
#[test]
fn test_refresh_history_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.data.sonarr_data.history.set_items(history_vec());
app.push_navigation_stack(ActiveSonarrBlock::History.into());
HistoryHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::History,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::History.into());
assert!(!app.should_refresh);
}
}
#[test]
fn test_history_sorting_options_source_title() {
let expected_cmp_fn: fn(&SonarrHistoryItem, &SonarrHistoryItem) -> Ordering = |a, b| {
a.source_title
.text
.to_lowercase()
.cmp(&b.source_title.text.to_lowercase())
};
let mut expected_history_vec = history_vec();
expected_history_vec.sort_by(expected_cmp_fn);
let sort_option = history_sorting_options()[0].clone();
let mut sorted_history_vec = history_vec();
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_history_vec, expected_history_vec);
assert_str_eq!(sort_option.name, "Source Title");
}
#[test]
fn test_history_sorting_options_event_type() {
let expected_cmp_fn: fn(&SonarrHistoryItem, &SonarrHistoryItem) -> Ordering = |a, b| {
a.event_type
.to_string()
.to_lowercase()
.cmp(&b.event_type.to_string().to_lowercase())
};
let mut expected_history_vec = history_vec();
expected_history_vec.sort_by(expected_cmp_fn);
let sort_option = history_sorting_options()[1].clone();
let mut sorted_history_vec = history_vec();
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_history_vec, expected_history_vec);
assert_str_eq!(sort_option.name, "Event Type");
}
#[test]
fn test_history_sorting_options_language() {
let expected_cmp_fn: fn(&SonarrHistoryItem, &SonarrHistoryItem) -> Ordering = |a, b| {
let default_language = Language {
id: 1,
name: "_".to_owned(),
};
let language_a = &a.languages.first().unwrap_or(&default_language);
let language_b = &b.languages.first().unwrap_or(&default_language);
language_a.cmp(language_b)
};
let mut expected_history_vec = history_vec();
expected_history_vec.sort_by(expected_cmp_fn);
let sort_option = history_sorting_options()[2].clone();
let mut sorted_history_vec = history_vec();
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_history_vec, expected_history_vec);
assert_str_eq!(sort_option.name, "Language");
}
#[test]
fn test_history_sorting_options_quality() {
let expected_cmp_fn: fn(&SonarrHistoryItem, &SonarrHistoryItem) -> Ordering = |a, b| {
a.quality
.quality
.name
.to_lowercase()
.cmp(&b.quality.quality.name.to_lowercase())
};
let mut expected_history_vec = history_vec();
expected_history_vec.sort_by(expected_cmp_fn);
let sort_option = history_sorting_options()[3].clone();
let mut sorted_history_vec = history_vec();
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_history_vec, expected_history_vec);
assert_str_eq!(sort_option.name, "Quality");
}
#[test]
fn test_history_sorting_options_date() {
let expected_cmp_fn: fn(&SonarrHistoryItem, &SonarrHistoryItem) -> Ordering =
|a, b| a.date.cmp(&b.date);
let mut expected_history_vec = history_vec();
expected_history_vec.sort_by(expected_cmp_fn);
let sort_option = history_sorting_options()[4].clone();
let mut sorted_history_vec = history_vec();
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_history_vec, expected_history_vec);
assert_str_eq!(sort_option.name, "Date");
}
#[test]
fn test_history_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if HISTORY_BLOCKS.contains(&active_sonarr_block) {
assert!(HistoryHandler::accepts(active_sonarr_block));
} else {
assert!(!HistoryHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_history_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::History.into());
app.is_loading = true;
let handler = HistoryHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::History,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_history_handler_not_ready_when_history_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::History.into());
app.is_loading = false;
let handler = HistoryHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::History,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_history_handler_ready_when_not_loading_and_history_is_not_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::History.into());
app.is_loading = false;
app
.data
.sonarr_data
.history
.set_items(vec![SonarrHistoryItem::default()]);
let handler = HistoryHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::History,
None,
);
assert!(handler.is_ready());
}
fn history_vec() -> Vec<SonarrHistoryItem> {
vec![
SonarrHistoryItem {
id: 3,
source_title: "test 1".into(),
event_type: SonarrHistoryEventType::Grabbed,
languages: vec![Language {
id: 1,
name: "telgu".to_owned(),
}],
quality: QualityWrapper {
quality: Quality {
name: "HD - 1080p".to_owned(),
},
},
date: DateTime::from(DateTime::parse_from_rfc3339("2024-01-10T07:28:45Z").unwrap()),
..SonarrHistoryItem::default()
},
SonarrHistoryItem {
id: 2,
source_title: "test 2".into(),
event_type: SonarrHistoryEventType::DownloadFolderImported,
languages: vec![Language {
id: 3,
name: "chinese".to_owned(),
}],
quality: QualityWrapper {
quality: Quality {
name: "SD - 720p".to_owned(),
},
},
date: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
..SonarrHistoryItem::default()
},
SonarrHistoryItem {
id: 1,
source_title: "test 3".into(),
event_type: SonarrHistoryEventType::EpisodeFileDeleted,
languages: vec![Language {
id: 1,
name: "english".to_owned(),
}],
quality: QualityWrapper {
quality: Quality {
name: "HD - 1080p".to_owned(),
},
},
date: DateTime::from(DateTime::parse_from_rfc3339("2024-03-10T07:28:45Z").unwrap()),
..SonarrHistoryItem::default()
},
]
}
}
+171
View File
@@ -0,0 +1,171 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_clear_errors, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, HISTORY_BLOCKS};
use crate::models::servarr_models::Language;
use crate::models::sonarr_models::SonarrHistoryItem;
use crate::models::stateful_table::SortOption;
#[cfg(test)]
#[path = "history_handler_tests.rs"]
mod history_handler_tests;
pub(super) struct HistoryHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> HistoryHandler<'a, 'b> {
handle_table_events!(
self,
history,
self.app.data.sonarr_data.history,
SonarrHistoryItem
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for HistoryHandler<'a, 'b> {
fn handle(&mut self) {
let history_table_handling_config = TableHandlingConfig::new(ActiveSonarrBlock::History.into())
.sorting_block(ActiveSonarrBlock::HistorySortPrompt.into())
.sort_by_fn(|a: &SonarrHistoryItem, b: &SonarrHistoryItem| a.id.cmp(&b.id))
.sort_options(history_sorting_options())
.searching_block(ActiveSonarrBlock::SearchHistory.into())
.search_error_block(ActiveSonarrBlock::SearchHistoryError.into())
.search_field_fn(|history| &history.source_title.text)
.filtering_block(ActiveSonarrBlock::FilterHistory.into())
.filter_error_block(ActiveSonarrBlock::FilterHistoryError.into())
.filter_field_fn(|history| &history.source_title.text);
if !self.handle_history_table_events(history_table_handling_config) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
HISTORY_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> Self {
HistoryHandler {
key,
app,
active_sonarr_block: active_block,
_context: context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.history.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::History {
handle_change_tab_left_right_keys(self.app, self.key)
}
}
fn handle_submit(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::History {
self
.app
.push_navigation_stack(ActiveSonarrBlock::HistoryItemDetails.into());
}
}
fn handle_esc(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::HistoryItemDetails {
self.app.pop_navigation_stack();
} else {
handle_clear_errors(self.app);
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
if self.active_sonarr_block == ActiveSonarrBlock::History {
match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ => (),
}
}
}
}
pub(in crate::handlers::sonarr_handlers) fn history_sorting_options(
) -> Vec<SortOption<SonarrHistoryItem>> {
vec![
SortOption {
name: "Source Title",
cmp_fn: Some(|a, b| {
a.source_title
.text
.to_lowercase()
.cmp(&b.source_title.text.to_lowercase())
}),
},
SortOption {
name: "Event Type",
cmp_fn: Some(|a, b| {
a.event_type
.to_string()
.to_lowercase()
.cmp(&b.event_type.to_string().to_lowercase())
}),
},
SortOption {
name: "Language",
cmp_fn: Some(|a, b| {
let default_language = Language {
id: 1,
name: "_".to_owned(),
};
let language_a = &a.languages.first().unwrap_or(&default_language);
let language_b = &b.languages.first().unwrap_or(&default_language);
language_a.cmp(language_b)
}),
},
SortOption {
name: "Quality",
cmp_fn: Some(|a, b| {
a.quality
.quality
.name
.to_lowercase()
.cmp(&b.quality.quality.name.to_lowercase())
}),
},
SortOption {
name: "Date",
cmp_fn: Some(|a, b| a.date.cmp(&b.date)),
},
]
}
@@ -0,0 +1,476 @@
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::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS};
use crate::network::sonarr_network::SonarrEvent;
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: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditIndexerHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
EDIT_INDEXER_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> EditIndexerHandler<'a, 'b> {
EditIndexerHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && self.app.data.sonarr_data.edit_indexer_modal.is_some()
}
fn handle_scroll_up(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
self.app.data.sonarr_data.selected_block.up();
}
ActiveSonarrBlock::EditIndexerPriorityInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.priority += 1;
}
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
self.app.data.sonarr_data.selected_block.down();
}
ActiveSonarrBlock::EditIndexerPriorityInput => {
let edit_indexer_modal = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap();
if edit_indexer_modal.priority > 0 {
edit_indexer_modal.priority -= 1;
}
}
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerNameInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.name
.scroll_home();
}
ActiveSonarrBlock::EditIndexerUrlInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.url
.scroll_home();
}
ActiveSonarrBlock::EditIndexerApiKeyInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.api_key
.scroll_home();
}
ActiveSonarrBlock::EditIndexerSeedRatioInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.seed_ratio
.scroll_home();
}
ActiveSonarrBlock::EditIndexerTagsInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.tags
.scroll_home();
}
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerNameInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.name
.reset_offset();
}
ActiveSonarrBlock::EditIndexerUrlInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.url
.reset_offset();
}
ActiveSonarrBlock::EditIndexerApiKeyInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.api_key
.reset_offset();
}
ActiveSonarrBlock::EditIndexerSeedRatioInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.seed_ratio
.reset_offset();
}
ActiveSonarrBlock::EditIndexerTagsInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.tags
.reset_offset();
}
_ => (),
}
}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
handle_prompt_left_right_keys!(
self,
ActiveSonarrBlock::EditIndexerConfirmPrompt,
sonarr_data
);
}
ActiveSonarrBlock::EditIndexerNameInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.name
);
}
ActiveSonarrBlock::EditIndexerUrlInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.url
);
}
ActiveSonarrBlock::EditIndexerApiKeyInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.api_key
);
}
ActiveSonarrBlock::EditIndexerSeedRatioInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.seed_ratio
);
}
ActiveSonarrBlock::EditIndexerTagsInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.tags
);
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
let selected_block = self.app.data.sonarr_data.selected_block.get_active_block();
match selected_block {
ActiveSonarrBlock::EditIndexerConfirmPrompt => {
let sonarr_data = &mut self.app.data.sonarr_data;
if sonarr_data.prompt_confirm {
sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditIndexer(None));
self.app.should_refresh = true;
} else {
sonarr_data.edit_indexer_modal = None;
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::EditIndexerNameInput
| ActiveSonarrBlock::EditIndexerUrlInput
| ActiveSonarrBlock::EditIndexerApiKeyInput
| ActiveSonarrBlock::EditIndexerSeedRatioInput
| ActiveSonarrBlock::EditIndexerTagsInput => {
self.app.push_navigation_stack(selected_block.into());
self.app.should_ignore_quit_key = true;
}
ActiveSonarrBlock::EditIndexerPriorityInput => self
.app
.push_navigation_stack(ActiveSonarrBlock::EditIndexerPriorityInput.into()),
ActiveSonarrBlock::EditIndexerToggleEnableRss => {
let indexer = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap();
indexer.enable_rss = Some(!indexer.enable_rss.unwrap_or_default());
}
ActiveSonarrBlock::EditIndexerToggleEnableAutomaticSearch => {
let indexer = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap();
indexer.enable_automatic_search =
Some(!indexer.enable_automatic_search.unwrap_or_default());
}
ActiveSonarrBlock::EditIndexerToggleEnableInteractiveSearch => {
let indexer = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap();
indexer.enable_interactive_search =
Some(!indexer.enable_interactive_search.unwrap_or_default());
}
_ => (),
}
}
ActiveSonarrBlock::EditIndexerNameInput
| ActiveSonarrBlock::EditIndexerUrlInput
| ActiveSonarrBlock::EditIndexerApiKeyInput
| ActiveSonarrBlock::EditIndexerSeedRatioInput
| ActiveSonarrBlock::EditIndexerTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
ActiveSonarrBlock::EditIndexerPriorityInput => self.app.pop_navigation_stack(),
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
self.app.data.sonarr_data.edit_indexer_modal = None;
}
ActiveSonarrBlock::EditIndexerNameInput
| ActiveSonarrBlock::EditIndexerUrlInput
| ActiveSonarrBlock::EditIndexerApiKeyInput
| ActiveSonarrBlock::EditIndexerSeedRatioInput
| ActiveSonarrBlock::EditIndexerPriorityInput
| ActiveSonarrBlock::EditIndexerTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
_ => self.app.pop_navigation_stack(),
}
}
fn handle_char_key_event(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerNameInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.name
);
}
ActiveSonarrBlock::EditIndexerUrlInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.url
);
}
ActiveSonarrBlock::EditIndexerApiKeyInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.api_key
);
}
ActiveSonarrBlock::EditIndexerSeedRatioInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.seed_ratio
);
}
ActiveSonarrBlock::EditIndexerTagsInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.tags
);
}
ActiveSonarrBlock::EditIndexerPrompt => {
if self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::EditIndexerConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditIndexer(None));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,182 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_prompt_left_right_keys;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
};
use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)]
#[path = "edit_indexer_settings_handler_tests.rs"]
mod edit_indexer_settings_handler_tests;
pub(super) struct IndexerSettingsHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexerSettingsHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
INDEXER_SETTINGS_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> IndexerSettingsHandler<'a, 'b> {
IndexerSettingsHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && self.app.data.sonarr_data.indexer_settings.is_some()
}
fn handle_scroll_up(&mut self) {
let indexer_settings = self.app.data.sonarr_data.indexer_settings.as_mut().unwrap();
match self.active_sonarr_block {
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
self.app.data.sonarr_data.selected_block.up();
}
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput => {
indexer_settings.minimum_age += 1;
}
ActiveSonarrBlock::IndexerSettingsRetentionInput => {
indexer_settings.retention += 1;
}
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput => {
indexer_settings.maximum_size += 1;
}
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput => {
indexer_settings.rss_sync_interval += 1;
}
_ => (),
}
}
fn handle_scroll_down(&mut self) {
let indexer_settings = self.app.data.sonarr_data.indexer_settings.as_mut().unwrap();
match self.active_sonarr_block {
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
self.app.data.sonarr_data.selected_block.down()
}
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput => {
if indexer_settings.minimum_age > 0 {
indexer_settings.minimum_age -= 1;
}
}
ActiveSonarrBlock::IndexerSettingsRetentionInput => {
if indexer_settings.retention > 0 {
indexer_settings.retention -= 1;
}
}
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput => {
if indexer_settings.maximum_size > 0 {
indexer_settings.maximum_size -= 1;
}
}
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput => {
if indexer_settings.rss_sync_interval > 0 {
indexer_settings.rss_sync_interval -= 1;
}
}
_ => (),
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::AllIndexerSettingsPrompt {
handle_prompt_left_right_keys!(
self,
ActiveSonarrBlock::IndexerSettingsConfirmPrompt,
sonarr_data
);
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::IndexerSettingsConfirmPrompt => {
let sonarr_data = &mut self.app.data.sonarr_data;
if sonarr_data.prompt_confirm {
sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditAllIndexerSettings(None));
self.app.should_refresh = true;
} else {
sonarr_data.indexer_settings = None;
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput
| ActiveSonarrBlock::IndexerSettingsRetentionInput
| ActiveSonarrBlock::IndexerSettingsMaximumSizeInput
| ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput => {
self.app.push_navigation_stack(
(
self.app.data.sonarr_data.selected_block.get_active_block(),
None,
)
.into(),
)
}
_ => (),
}
}
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput
| ActiveSonarrBlock::IndexerSettingsRetentionInput
| ActiveSonarrBlock::IndexerSettingsMaximumSizeInput
| ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput => self.app.pop_navigation_stack(),
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
self.app.data.sonarr_data.indexer_settings = None;
}
_ => self.app.pop_navigation_stack(),
}
}
fn handle_char_key_event(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::AllIndexerSettingsPrompt
&& self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::IndexerSettingsConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::EditAllIndexerSettings(None));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
}
}
}
@@ -0,0 +1,570 @@
#[cfg(test)]
mod tests {
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
};
use crate::models::sonarr_models::IndexerSettings;
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::models::servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS;
use crate::models::sonarr_models::IndexerSettings;
use crate::models::BlockSelectionState;
use super::*;
macro_rules! test_i64_counter_scroll_value {
($block:expr, $key:expr, $data_ref:ident, $negatives:literal) => {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with($key, &mut app, $block, None).handle();
if $key == Key::Up {
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
1
);
} else {
if $negatives {
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
-1
);
} else {
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
0
);
IndexerSettingsHandler::with(Key::Up, &mut app, $block, None).handle();
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
1
);
IndexerSettingsHandler::with($key, &mut app, $block, None).handle();
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
0
);
}
}
};
}
#[rstest]
fn test_edit_indexer_settings_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.down();
IndexerSettingsHandler::with(
key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
if key == Key::Up {
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput
);
} else {
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput
);
}
}
#[rstest]
fn test_edit_indexer_settings_prompt_scroll_no_op_when_not_ready(
#[values(Key::Up, Key::Down)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.down();
IndexerSettingsHandler::with(
key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::IndexerSettingsRetentionInput
);
}
#[rstest]
fn test_edit_indexer_settings_minimum_age_scroll(#[values(Key::Up, Key::Down)] key: Key) {
test_i64_counter_scroll_value!(
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
key,
minimum_age,
false
);
}
#[rstest]
fn test_edit_indexer_settings_retention_scroll(#[values(Key::Up, Key::Down)] key: Key) {
test_i64_counter_scroll_value!(
ActiveSonarrBlock::IndexerSettingsRetentionInput,
key,
retention,
false
);
}
#[rstest]
fn test_edit_indexer_settings_maximum_size_scroll(#[values(Key::Up, Key::Down)] key: Key) {
test_i64_counter_scroll_value!(
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
key,
maximum_size,
false
);
}
#[rstest]
fn test_edit_indexer_settings_rss_sync_interval_scroll(#[values(Key::Up, Key::Down)] key: Key) {
test_i64_counter_scroll_value!(
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput,
key,
rss_sync_interval,
false
);
}
}
mod test_handle_left_right_action {
use crate::models::servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS;
use crate::models::BlockSelectionState;
use rstest::rstest;
use super::*;
#[rstest]
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.y = INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1;
IndexerSettingsHandler::with(
key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
IndexerSettingsHandler::with(
key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::{
models::{
servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS,
sonarr_models::IndexerSettings, BlockSelectionState,
},
network::sonarr_network::SonarrEvent,
};
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[test]
fn test_edit_indexer_settings_prompt_prompt_decline_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh);
assert_eq!(app.data.sonarr_data.indexer_settings, None);
}
#[test]
fn test_edit_indexer_settings_prompt_prompt_confirmation_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.prompt_confirm = true;
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditAllIndexerSettings(None))
);
assert!(app.data.sonarr_data.indexer_settings.is_some());
assert!(app.should_refresh);
}
#[test]
fn test_edit_indexer_settings_prompt_prompt_confirmation_submit_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.prompt_confirm = true;
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AllIndexerSettingsPrompt.into()
);
assert!(!app.should_refresh);
}
#[rstest]
#[case(ActiveSonarrBlock::IndexerSettingsMinimumAgeInput, 0)]
#[case(ActiveSonarrBlock::IndexerSettingsRetentionInput, 1)]
#[case(ActiveSonarrBlock::IndexerSettingsMaximumSizeInput, 2)]
#[case(ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput, 3)]
fn test_edit_indexer_settings_prompt_submit_selected_block(
#[case] selected_block: ActiveSonarrBlock,
#[case] y_index: usize,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.set_index(0, y_index);
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
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, 3, 4)] y_index: usize,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.set_index(0, y_index);
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AllIndexerSettingsPrompt.into()
);
}
#[rstest]
fn test_edit_indexer_settings_selected_block_submit(
#[values(
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
ActiveSonarrBlock::IndexerSettingsRetentionInput,
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput
)]
active_sonarr_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.push_navigation_stack(active_sonarr_block.into());
IndexerSettingsHandler::with(SUBMIT_KEY, &mut app, active_sonarr_block, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AllIndexerSettingsPrompt.into()
);
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::models::sonarr_models::IndexerSettings;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
fn test_edit_indexer_settings_prompt_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.data.sonarr_data.indexer_settings, None);
}
#[rstest]
fn test_edit_indexer_settings_selected_blocks_esc(
#[values(
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
ActiveSonarrBlock::IndexerSettingsRetentionInput,
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput
)]
active_sonarr_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(active_sonarr_block.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(ESC_KEY, &mut app, active_sonarr_block, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(
app.data.sonarr_data.indexer_settings,
Some(IndexerSettings::default())
);
}
}
mod test_handle_key_char {
use crate::{
models::{
servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS,
sonarr_models::IndexerSettings, BlockSelectionState,
},
network::sonarr_network::SonarrEvent,
};
use super::*;
#[test]
fn test_edit_indexer_settings_prompt_prompt_confirmation_confirm() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditAllIndexerSettings(None))
);
assert!(app.data.sonarr_data.indexer_settings.is_some());
assert!(app.should_refresh);
}
}
#[test]
fn test_indexer_settings_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if INDEXER_SETTINGS_BLOCKS.contains(&active_sonarr_block) {
assert!(IndexerSettingsHandler::accepts(active_sonarr_block));
} else {
assert!(!IndexerSettingsHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_edit_indexer_settings_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
let handler = IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_edit_indexer_settings_handler_not_ready_when_indexer_settings_is_none() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
let handler = IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_edit_indexer_settings_handler_ready_when_not_loading_and_indexer_settings_is_some() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
let handler = IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,704 @@
#[cfg(test)]
mod tests {
use rstest::rstest;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::indexers::IndexersHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, EDIT_INDEXER_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
};
use crate::models::servarr_models::Indexer;
use crate::test_handler_delegation;
mod test_handle_delete {
use pretty_assertions::assert_eq;
use super::*;
const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key;
#[test]
fn test_delete_indexer_prompt() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::DeleteIndexerPrompt.into()
);
}
#[test]
fn test_delete_indexer_prompt_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
}
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
#[rstest]
fn test_indexers_tab_left(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(5);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
}
#[rstest]
fn test_indexers_tab_right(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(5);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::System.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::System.into());
}
#[rstest]
fn test_left_right_delete_indexer_prompt_toggle(
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
IndexersHandler::with(key, &mut app, ActiveSonarrBlock::DeleteIndexerPrompt, None).handle();
assert!(app.data.sonarr_data.prompt_confirm);
IndexersHandler::with(key, &mut app, ActiveSonarrBlock::DeleteIndexerPrompt, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use crate::models::servarr_data::modals::EditIndexerModal;
use crate::models::servarr_data::sonarr::sonarr_data::{
SonarrData, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
};
use crate::models::servarr_models::{Indexer, IndexerField};
use bimap::BiMap;
use pretty_assertions::assert_eq;
use serde_json::{Number, Value};
use crate::network::sonarr_network::SonarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[rstest]
fn test_edit_indexer_submit(#[values(true, false)] torrent_protocol: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
let protocol = if torrent_protocol {
"torrent".to_owned()
} else {
"usenet".to_owned()
};
let mut expected_edit_indexer_modal = EditIndexerModal {
name: "Test".into(),
enable_rss: Some(true),
enable_automatic_search: Some(true),
enable_interactive_search: Some(true),
url: "https://test.com".into(),
api_key: "1234".into(),
tags: "usenet, test".into(),
..EditIndexerModal::default()
};
let mut sonarr_data = SonarrData {
tags_map: BiMap::from_iter([(1, "usenet".to_owned()), (2, "test".to_owned())]),
..SonarrData::default()
};
let mut fields = vec![
IndexerField {
name: Some("baseUrl".to_owned()),
value: Some(Value::String("https://test.com".to_owned())),
},
IndexerField {
name: Some("apiKey".to_owned()),
value: Some(Value::String("1234".to_owned())),
},
];
if torrent_protocol {
fields.push(IndexerField {
name: Some("seedCriteria.seedRatio".to_owned()),
value: Some(Value::from(1.2f64)),
});
expected_edit_indexer_modal.seed_ratio = "1.2".into();
}
let indexer = Indexer {
name: Some("Test".to_owned()),
enable_rss: true,
enable_automatic_search: true,
enable_interactive_search: true,
protocol,
tags: vec![Number::from(1), Number::from(2)],
fields: Some(fields),
..Indexer::default()
};
sonarr_data.indexers.set_items(vec![indexer]);
app.data.sonarr_data = sonarr_data;
IndexersHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::EditIndexerPrompt.into()
);
assert_eq!(
app.data.sonarr_data.edit_indexer_modal,
Some((&app.data.sonarr_data).into())
);
assert_eq!(
app.data.sonarr_data.edit_indexer_modal,
Some(expected_edit_indexer_modal)
);
if torrent_protocol {
assert_eq!(
app.data.sonarr_data.selected_block.blocks,
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS
);
} else {
assert_eq!(
app.data.sonarr_data.selected_block.blocks,
EDIT_INDEXER_NZB_SELECTION_BLOCKS
);
}
}
#[test]
fn test_edit_indexer_submit_no_op_when_not_ready() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(app.data.sonarr_data.edit_indexer_modal, None);
}
#[test]
fn test_delete_indexer_prompt_confirm_submit() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.data.sonarr_data.prompt_confirm = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteIndexer(None))
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[test]
fn test_prompt_decline_submit() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
fn test_delete_indexer_prompt_block_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
IndexersHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[rstest]
fn test_test_indexer_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.data.sonarr_data.indexer_test_errors = Some("test result".to_owned());
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::TestIndexer.into());
IndexersHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::TestIndexer, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(app.data.sonarr_data.indexer_test_errors, None);
}
#[rstest]
fn test_default_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.error = "test error".to_owned().into();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
IndexersHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(app.error.text.is_empty());
}
}
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use crate::{
models::servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS,
network::sonarr_network::SonarrEvent,
};
use super::*;
#[test]
fn test_refresh_indexers_key() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
IndexersHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(app.should_refresh);
}
#[test]
fn test_refresh_indexers_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
IndexersHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(!app.should_refresh);
}
#[test]
fn test_indexer_settings_key() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.settings.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AllIndexerSettingsPrompt.into()
);
assert_eq!(
app.data.sonarr_data.selected_block.blocks,
INDEXER_SETTINGS_SELECTION_BLOCKS
);
}
#[test]
fn test_indexer_settings_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.settings.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[test]
fn test_test_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.test.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::TestIndexer.into()
);
}
#[test]
fn test_test_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.test.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[test]
fn test_test_all_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.test_all.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::TestAllIndexers.into()
);
}
#[test]
fn test_test_all_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.test_all.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[test]
fn test_delete_indexer_prompt_confirm() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveSonarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteIndexer(None))
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
}
#[rstest]
fn test_delegates_edit_indexer_blocks_to_edit_indexer_handler(
#[values(
ActiveSonarrBlock::EditIndexerPrompt,
ActiveSonarrBlock::EditIndexerConfirmPrompt,
ActiveSonarrBlock::EditIndexerApiKeyInput,
ActiveSonarrBlock::EditIndexerNameInput,
ActiveSonarrBlock::EditIndexerSeedRatioInput,
ActiveSonarrBlock::EditIndexerToggleEnableRss,
ActiveSonarrBlock::EditIndexerToggleEnableAutomaticSearch,
ActiveSonarrBlock::EditIndexerToggleEnableInteractiveSearch,
ActiveSonarrBlock::EditIndexerUrlInput,
ActiveSonarrBlock::EditIndexerTagsInput
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
IndexersHandler,
ActiveSonarrBlock::Indexers,
active_sonarr_block
);
}
#[rstest]
fn test_delegates_indexer_settings_blocks_to_indexer_settings_handler(
#[values(
ActiveSonarrBlock::AllIndexerSettingsPrompt,
ActiveSonarrBlock::IndexerSettingsConfirmPrompt,
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
ActiveSonarrBlock::IndexerSettingsRetentionInput,
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
IndexersHandler,
ActiveSonarrBlock::Indexers,
active_sonarr_block
);
}
#[test]
fn test_delegates_test_all_indexers_block_to_test_all_indexers_handler() {
test_handler_delegation!(
IndexersHandler,
ActiveSonarrBlock::Indexers,
ActiveSonarrBlock::TestAllIndexers
);
}
#[test]
fn test_indexers_handler_accepts() {
let mut indexers_blocks = Vec::new();
indexers_blocks.extend(INDEXERS_BLOCKS);
indexers_blocks.extend(INDEXER_SETTINGS_BLOCKS);
indexers_blocks.extend(EDIT_INDEXER_BLOCKS);
indexers_blocks.push(ActiveSonarrBlock::TestAllIndexers);
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if indexers_blocks.contains(&active_sonarr_block) {
assert!(IndexersHandler::accepts(active_sonarr_block));
} else {
assert!(!IndexersHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_indexers_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
let handler = IndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_indexers_handler_not_ready_when_indexers_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
let handler = IndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_indexers_handler_ready_when_not_loading_and_indexers_is_not_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
let handler = IndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,200 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::sonarr_handlers::indexers::edit_indexer_handler::EditIndexerHandler;
use crate::handlers::sonarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
use crate::handlers::sonarr_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::sonarr::sonarr_data::{
ActiveSonarrBlock, 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::network::sonarr_network::SonarrEvent;
mod edit_indexer_handler;
mod edit_indexer_settings_handler;
mod test_all_indexers_handler;
#[cfg(test)]
#[path = "indexers_handler_tests.rs"]
mod indexers_handler_tests;
pub(super) struct IndexersHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> IndexersHandler<'a, 'b> {
handle_table_events!(self, indexers, self.app.data.sonarr_data.indexers, Indexer);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexersHandler<'a, 'b> {
fn handle(&mut self) {
let indexers_table_handling_config =
TableHandlingConfig::new(ActiveSonarrBlock::Indexers.into());
if !self.handle_indexers_table_events(indexers_table_handling_config) {
match self.active_sonarr_block {
_ if EditIndexerHandler::accepts(self.active_sonarr_block) => {
EditIndexerHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle()
}
_ if IndexerSettingsHandler::accepts(self.active_sonarr_block) => {
IndexerSettingsHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle()
}
_ if TestAllIndexersHandler::accepts(self.active_sonarr_block) => {
TestAllIndexersHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle()
}
_ => self.handle_key_event(),
}
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
EditIndexerHandler::accepts(active_block)
|| IndexerSettingsHandler::accepts(active_block)
|| TestAllIndexersHandler::accepts(active_block)
|| INDEXERS_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> IndexersHandler<'a, 'b> {
IndexersHandler {
key,
app,
active_sonarr_block: active_block,
context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.indexers.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::Indexers {
self
.app
.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
}
}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Indexers => handle_change_tab_left_right_keys(self.app, self.key),
ActiveSonarrBlock::DeleteIndexerPrompt => handle_prompt_toggle(self.app, self.key),
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteIndexerPrompt => {
let sonarr_data = &mut self.app.data.sonarr_data;
if sonarr_data.prompt_confirm {
sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteIndexer(None));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::Indexers => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::EditIndexerPrompt.into());
self.app.data.sonarr_data.edit_indexer_modal = Some((&self.app.data.sonarr_data).into());
let protocol = &self
.app
.data
.sonarr_data
.indexers
.current_selection()
.protocol;
if protocol == "torrent" {
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(EDIT_INDEXER_TORRENT_SELECTION_BLOCKS);
} else {
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(EDIT_INDEXER_NZB_SELECTION_BLOCKS);
}
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteIndexerPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
ActiveSonarrBlock::TestIndexer => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.indexer_test_errors = None;
}
_ => handle_clear_errors(self.app),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::Indexers => match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if key == DEFAULT_KEYBINDINGS.test.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::TestIndexer.into());
}
_ if key == DEFAULT_KEYBINDINGS.test_all.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::TestAllIndexers.into());
}
_ if key == DEFAULT_KEYBINDINGS.settings.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
}
_ => (),
},
ActiveSonarrBlock::DeleteIndexerPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteIndexer(None));
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
@@ -0,0 +1,101 @@
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::sonarr::sonarr_data::ActiveSonarrBlock;
#[cfg(test)]
#[path = "test_all_indexers_handler_tests.rs"]
mod test_all_indexers_handler_tests;
pub(super) struct TestAllIndexersHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> TestAllIndexersHandler<'a, 'b> {
handle_table_events!(
self,
indexer_test_all_results,
self
.app
.data
.sonarr_data
.indexer_test_all_results
.as_mut()
.unwrap(),
IndexerTestResultModalItem
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for TestAllIndexersHandler<'a, 'b> {
fn handle(&mut self) {
let indexer_test_all_results_table_handling_config =
TableHandlingConfig::new(ActiveSonarrBlock::TestAllIndexers.into());
if !self
.handle_indexer_test_all_results_table_events(indexer_test_all_results_table_handling_config)
{
self.handle_key_event();
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
active_block == ActiveSonarrBlock::TestAllIndexers
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> TestAllIndexersHandler<'a, 'b> {
TestAllIndexersHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
let table_is_ready = if let Some(table) = &self.app.data.sonarr_data.indexer_test_all_results {
!table.is_empty()
} else {
false
};
!self.app.is_loading && table_is_ready
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {}
fn handle_submit(&mut self) {}
fn handle_esc(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::TestAllIndexers {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.indexer_test_all_results = None;
}
}
fn handle_char_key_event(&mut self) {}
}
@@ -0,0 +1,118 @@
#[cfg(test)]
mod tests {
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::handlers::sonarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::stateful_table::StatefulTable;
use strum::IntoEnumIterator;
mod test_handle_esc {
use super::*;
use crate::models::stateful_table::StatefulTable;
use pretty_assertions::assert_eq;
use rstest::rstest;
#[rstest]
fn test_test_all_indexers_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::TestAllIndexers.into());
app.data.sonarr_data.indexer_test_all_results = Some(StatefulTable::default());
TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.indexer_test_all_results.is_none());
}
}
#[test]
fn test_test_all_indexers_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if active_sonarr_block == ActiveSonarrBlock::TestAllIndexers {
assert!(TestAllIndexersHandler::accepts(active_sonarr_block));
} else {
assert!(!TestAllIndexersHandler::accepts(active_sonarr_block));
}
});
}
#[test]
fn test_test_all_indexers_handler_is_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
let handler = TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_test_all_indexers_handler_is_not_ready_when_results_is_none() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
let handler = TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_test_all_indexers_handler_is_not_ready_when_results_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
app.data.sonarr_data.indexer_test_all_results = Some(StatefulTable::default());
let handler = TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_test_all_indexers_handler_is_ready_when_results_is_not_empty_and_is_loaded() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
let mut indexer_test_results = StatefulTable::default();
indexer_test_results.set_items(vec![IndexerTestResultModalItem::default()]);
app.data.sonarr_data.indexer_test_all_results = Some(indexer_test_results);
let handler = TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,544 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, ADD_SERIES_BLOCKS, ADD_SERIES_SELECTION_BLOCKS,
};
use crate::models::sonarr_models::AddSeriesSearchResult;
use crate::models::{BlockSelectionState, Scrollable};
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_table_events, handle_text_box_keys, handle_text_box_left_right_keys, App, Key};
#[cfg(test)]
#[path = "add_series_handler_tests.rs"]
mod add_series_handler_tests;
pub(super) struct AddSeriesHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> AddSeriesHandler<'a, 'b> {
handle_table_events!(
self,
add_searched_series,
self
.app
.data
.sonarr_data
.add_searched_series
.as_mut()
.unwrap(),
AddSeriesSearchResult
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for AddSeriesHandler<'a, 'b> {
fn handle(&mut self) {
let add_series_table_handling_config =
TableHandlingConfig::new(ActiveSonarrBlock::AddSeriesSearchResults.into());
if !self.handle_add_searched_series_table_events(add_series_table_handling_config) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
ADD_SERIES_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> AddSeriesHandler<'a, 'b> {
AddSeriesHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading
}
fn handle_scroll_up(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSelectMonitor => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.monitor_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesSelectRootFolder => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.root_folder_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesPrompt => self.app.data.sonarr_data.selected_block.up(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSelectMonitor => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.monitor_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesSelectRootFolder => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.root_folder_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesPrompt => self.app.data.sonarr_data.selected_block.down(),
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSelectMonitor => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.monitor_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSelectRootFolder => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.root_folder_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSearchInput => self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
.scroll_home(),
ActiveSonarrBlock::AddSeriesTagsInput => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.tags
.scroll_home(),
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSelectMonitor => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.monitor_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSelectRootFolder => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.root_folder_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSearchInput => self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
.reset_offset(),
ActiveSonarrBlock::AddSeriesTagsInput => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.tags
.reset_offset(),
_ => (),
}
}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesPrompt => handle_prompt_toggle(self.app, self.key),
ActiveSonarrBlock::AddSeriesSearchInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
)
}
ActiveSonarrBlock::AddSeriesTagsInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.tags
)
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
_ if self.active_sonarr_block == ActiveSonarrBlock::AddSeriesSearchInput
&& !self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
.text
.is_empty() =>
{
self
.app
.push_navigation_stack(ActiveSonarrBlock::AddSeriesSearchResults.into());
self.app.should_ignore_quit_key = false;
}
_ if self.active_sonarr_block == ActiveSonarrBlock::AddSeriesSearchResults
&& self.app.data.sonarr_data.add_searched_series.is_some() =>
{
let tvdb_id = self
.app
.data
.sonarr_data
.add_searched_series
.as_ref()
.unwrap()
.current_selection()
.tvdb_id;
if self
.app
.data
.sonarr_data
.series
.items
.iter()
.any(|series| series.tvdb_id == tvdb_id)
{
self
.app
.push_navigation_stack(ActiveSonarrBlock::AddSeriesAlreadyInLibrary.into());
} else {
self
.app
.push_navigation_stack(ActiveSonarrBlock::AddSeriesPrompt.into());
self.app.data.sonarr_data.add_series_modal = Some((&self.app.data.sonarr_data).into());
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(ADD_SERIES_SELECTION_BLOCKS);
}
}
ActiveSonarrBlock::AddSeriesPrompt => {
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::AddSeriesConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::AddSeries(None));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::AddSeriesSelectMonitor
| ActiveSonarrBlock::AddSeriesSelectSeriesType
| ActiveSonarrBlock::AddSeriesSelectQualityProfile
| ActiveSonarrBlock::AddSeriesSelectLanguageProfile
| ActiveSonarrBlock::AddSeriesSelectRootFolder => self.app.push_navigation_stack(
self
.app
.data
.sonarr_data
.selected_block
.get_active_block()
.into(),
),
ActiveSonarrBlock::AddSeriesTagsInput => {
self.app.push_navigation_stack(
self
.app
.data
.sonarr_data
.selected_block
.get_active_block()
.into(),
);
self.app.should_ignore_quit_key = true;
}
ActiveSonarrBlock::AddSeriesToggleUseSeasonFolder => {
self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.use_season_folder = !self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.use_season_folder;
}
_ => (),
}
}
ActiveSonarrBlock::AddSeriesSelectMonitor
| ActiveSonarrBlock::AddSeriesSelectSeriesType
| ActiveSonarrBlock::AddSeriesSelectQualityProfile
| ActiveSonarrBlock::AddSeriesSelectLanguageProfile
| ActiveSonarrBlock::AddSeriesSelectRootFolder => self.app.pop_navigation_stack(),
ActiveSonarrBlock::AddSeriesTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSearchInput => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.add_series_search = None;
self.app.should_ignore_quit_key = false;
}
ActiveSonarrBlock::AddSeriesSearchResults
| ActiveSonarrBlock::AddSeriesEmptySearchResults => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.add_searched_series = None;
self.app.should_ignore_quit_key = true;
}
ActiveSonarrBlock::AddSeriesPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.add_series_modal = None;
self.app.data.sonarr_data.prompt_confirm = false;
}
ActiveSonarrBlock::AddSeriesSelectMonitor
| ActiveSonarrBlock::AddSeriesSelectSeriesType
| ActiveSonarrBlock::AddSeriesSelectQualityProfile
| ActiveSonarrBlock::AddSeriesSelectLanguageProfile
| ActiveSonarrBlock::AddSeriesAlreadyInLibrary
| ActiveSonarrBlock::AddSeriesSelectRootFolder => self.app.pop_navigation_stack(),
ActiveSonarrBlock::AddSeriesTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
_ => (),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSearchInput => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
)
}
ActiveSonarrBlock::AddSeriesTagsInput => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.tags
)
}
ActiveSonarrBlock::AddSeriesPrompt => {
if self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::AddSeriesConfirmPrompt
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::AddSeries(None));
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
use crate::{
app::{key_binding::DEFAULT_KEYBINDINGS, App},
event::Key,
handlers::{handle_prompt_toggle, KeyEventHandler},
models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS},
network::sonarr_network::SonarrEvent,
};
#[cfg(test)]
#[path = "delete_series_handler_tests.rs"]
mod delete_series_handler_tests;
pub(super) struct DeleteSeriesHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DeleteSeriesHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
DELETE_SERIES_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> Self {
DeleteSeriesHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading
}
fn handle_scroll_up(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
self.app.data.sonarr_data.selected_block.up();
}
}
fn handle_scroll_down(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
self.app.data.sonarr_data.selected_block.down();
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
handle_prompt_toggle(self.app, self.key);
}
}
fn handle_submit(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::DeleteSeriesConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteSeries(None));
self.app.should_refresh = true;
} else {
self.app.data.sonarr_data.reset_delete_series_preferences();
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::DeleteSeriesToggleDeleteFile => {
self.app.data.sonarr_data.delete_series_files =
!self.app.data.sonarr_data.delete_series_files;
}
ActiveSonarrBlock::DeleteSeriesToggleAddListExclusion => {
self.app.data.sonarr_data.add_list_exclusion =
!self.app.data.sonarr_data.add_list_exclusion;
}
_ => (),
}
}
}
fn handle_esc(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.reset_delete_series_preferences();
self.app.data.sonarr_data.prompt_confirm = false;
}
}
fn handle_char_key_event(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt
&& self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::DeleteSeriesConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteSeries(None));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
}
}
}
@@ -0,0 +1,339 @@
#[cfg(test)]
mod tests {
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::library::delete_series_handler::DeleteSeriesHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS};
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::models::servarr_data::sonarr::sonarr_data::DELETE_SERIES_SELECTION_BLOCKS;
use crate::models::BlockSelectionState;
use super::*;
#[rstest]
fn test_delete_series_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default();
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.down();
DeleteSeriesHandler::with(key, &mut app, ActiveSonarrBlock::DeleteSeriesPrompt, None)
.handle();
if key == Key::Up {
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::DeleteSeriesToggleDeleteFile
);
} else {
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::DeleteSeriesConfirmPrompt
);
}
}
#[rstest]
fn test_delete_series_prompt_scroll_no_op_when_not_ready(
#[values(Key::Up, Key::Down)] key: Key,
) {
let mut app = App::default();
app.is_loading = true;
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.down();
DeleteSeriesHandler::with(key, &mut app, ActiveSonarrBlock::DeleteSeriesPrompt, None)
.handle();
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::DeleteSeriesToggleAddListExclusion
);
}
}
mod test_handle_left_right_action {
use rstest::rstest;
use super::*;
#[rstest]
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
DeleteSeriesHandler::with(key, &mut app, ActiveSonarrBlock::DeleteSeriesPrompt, None)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
DeleteSeriesHandler::with(key, &mut app, ActiveSonarrBlock::DeleteSeriesPrompt, None)
.handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use crate::models::servarr_data::sonarr::sonarr_data::DELETE_SERIES_SELECTION_BLOCKS;
use crate::models::BlockSelectionState;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[test]
fn test_delete_series_prompt_prompt_decline_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, DELETE_SERIES_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(!app.data.sonarr_data.delete_series_files);
assert!(!app.data.sonarr_data.add_list_exclusion);
}
#[test]
fn test_delete_series_confirm_prompt_prompt_confirmation_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, DELETE_SERIES_SELECTION_BLOCKS.len() - 1);
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteSeries(None))
);
assert!(app.should_refresh);
assert!(app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.delete_series_files);
assert!(app.data.sonarr_data.add_list_exclusion);
}
#[test]
fn test_delete_series_confirm_prompt_prompt_confirmation_submit_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::DeleteSeriesPrompt.into()
);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh);
assert!(app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.delete_series_files);
assert!(app.data.sonarr_data.add_list_exclusion);
}
#[test]
fn test_delete_series_toggle_delete_files_submit() {
let current_route = ActiveSonarrBlock::DeleteSeriesPrompt.into();
let mut app = App::default();
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), current_route);
assert_eq!(app.data.sonarr_data.delete_series_files, true);
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), current_route);
assert_eq!(app.data.sonarr_data.delete_series_files, false);
}
}
mod test_handle_esc {
use super::*;
use rstest::rstest;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
fn test_delete_series_prompt_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
DeleteSeriesHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(!app.data.sonarr_data.delete_series_files);
assert!(!app.data.sonarr_data.add_list_exclusion);
}
}
mod test_handle_key_char {
use crate::{
models::{
servarr_data::sonarr::sonarr_data::DELETE_SERIES_SELECTION_BLOCKS, BlockSelectionState,
},
network::sonarr_network::SonarrEvent,
};
use super::*;
#[test]
fn test_delete_series_confirm_prompt_prompt_confirm() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, DELETE_SERIES_SELECTION_BLOCKS.len() - 1);
DeleteSeriesHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteSeries(None))
);
assert!(app.should_refresh);
assert!(app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.delete_series_files);
assert!(app.data.sonarr_data.add_list_exclusion);
}
}
#[test]
fn test_delete_series_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if DELETE_SERIES_BLOCKS.contains(&active_sonarr_block) {
assert!(DeleteSeriesHandler::accepts(active_sonarr_block));
} else {
assert!(!DeleteSeriesHandler::accepts(active_sonarr_block));
}
});
}
#[test]
fn test_delete_series_handler_not_ready_when_loading() {
let mut app = App::default();
app.is_loading = true;
let handler = DeleteSeriesHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_delete_series_handler_ready_when_not_loading() {
let mut app = App::default();
app.is_loading = false;
let handler = DeleteSeriesHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,404 @@
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::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_SERIES_BLOCKS};
use crate::models::Scrollable;
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
#[cfg(test)]
#[path = "edit_series_handler_tests.rs"]
mod edit_series_handler_tests;
pub(super) struct EditSeriesHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditSeriesHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
EDIT_SERIES_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> EditSeriesHandler<'a, 'b> {
EditSeriesHandler {
key,
app,
active_sonarr_block: active_block,
context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && self.app.data.sonarr_data.edit_series_modal.is_some()
}
fn handle_scroll_up(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_up(),
ActiveSonarrBlock::EditSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_up(),
ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_up(),
ActiveSonarrBlock::EditSeriesPrompt => self.app.data.sonarr_data.selected_block.up(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_down(),
ActiveSonarrBlock::EditSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_down(),
ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_down(),
ActiveSonarrBlock::EditSeriesPrompt => self.app.data.sonarr_data.selected_block.down(),
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_to_top(),
ActiveSonarrBlock::EditSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_to_top(),
ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_to_top(),
ActiveSonarrBlock::EditSeriesPathInput => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.path
.scroll_home(),
ActiveSonarrBlock::EditSeriesTagsInput => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.tags
.scroll_home(),
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_to_bottom(),
ActiveSonarrBlock::EditSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_to_bottom(),
ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_to_bottom(),
ActiveSonarrBlock::EditSeriesPathInput => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.path
.reset_offset(),
ActiveSonarrBlock::EditSeriesTagsInput => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.tags
.reset_offset(),
_ => (),
}
}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesPrompt => handle_prompt_toggle(self.app, self.key),
ActiveSonarrBlock::EditSeriesPathInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.path
)
}
ActiveSonarrBlock::EditSeriesTagsInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.tags
)
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesPrompt => {
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::EditSeriesConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditSeries(None));
self.app.should_refresh = true;
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::EditSeriesSelectSeriesType
| ActiveSonarrBlock::EditSeriesSelectQualityProfile
| ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self.app.push_navigation_stack(
(
self.app.data.sonarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
),
ActiveSonarrBlock::EditSeriesPathInput | ActiveSonarrBlock::EditSeriesTagsInput => {
self.app.push_navigation_stack(
(
self.app.data.sonarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
);
self.app.should_ignore_quit_key = true;
}
ActiveSonarrBlock::EditSeriesToggleMonitored => {
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.monitored = Some(
!self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.monitored
.unwrap_or_default(),
)
}
ActiveSonarrBlock::EditSeriesToggleSeasonFolder => {
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.use_season_folders = Some(
!self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.use_season_folders
.unwrap_or_default(),
)
}
_ => (),
}
}
ActiveSonarrBlock::EditSeriesSelectSeriesType
| ActiveSonarrBlock::EditSeriesSelectQualityProfile
| ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self.app.pop_navigation_stack(),
ActiveSonarrBlock::EditSeriesPathInput | ActiveSonarrBlock::EditSeriesTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesTagsInput | ActiveSonarrBlock::EditSeriesPathInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
ActiveSonarrBlock::EditSeriesPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.edit_series_modal = None;
self.app.data.sonarr_data.prompt_confirm = false;
}
ActiveSonarrBlock::EditSeriesSelectSeriesType
| ActiveSonarrBlock::EditSeriesSelectQualityProfile
| ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self.app.pop_navigation_stack(),
_ => (),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesPathInput => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.path
)
}
ActiveSonarrBlock::EditSeriesTagsInput => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.tags
)
}
ActiveSonarrBlock::EditSeriesPrompt => {
if self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::EditSeriesConfirmPrompt
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditSeries(None));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}

Some files were not shown because too many files have changed in this diff Show More