Compare commits
No commits in common. "d5716b79e27d99edac912729699de4f9d57a2b01" and "8b008292cb143858649797e9689a9547f0d428e6" have entirely different histories.
d5716b79e2
...
8b008292cb
128
Cargo.lock
generated
128
Cargo.lock
generated
@ -65,7 +65,7 @@ version = "0.2.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.1.19",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
@ -129,9 +129,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "castaway"
|
name = "castaway"
|
||||||
version = "0.2.3"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustversion",
|
"rustversion",
|
||||||
]
|
]
|
||||||
@ -168,14 +168,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compact_str"
|
name = "compact_str"
|
||||||
version = "0.8.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644"
|
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"castaway",
|
"castaway",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"itoa",
|
"itoa",
|
||||||
"rustversion",
|
|
||||||
"ryu",
|
"ryu",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
@ -198,15 +197,15 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
version = "0.28.1"
|
version = "0.27.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"mio 1.0.2",
|
"libc",
|
||||||
|
"mio",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"rustix",
|
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"signal-hook-mio",
|
"signal-hook-mio",
|
||||||
"winapi",
|
"winapi",
|
||||||
@ -394,9 +393,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
@ -407,12 +406,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
@ -505,14 +498,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instability"
|
name = "indoc"
|
||||||
version = "0.3.2"
|
version = "2.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c"
|
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
@ -522,9 +511,9 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.13.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
@ -552,15 +541,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.158"
|
version = "0.2.153"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.14"
|
version = "0.4.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
@ -614,22 +603,10 @@ version = "0.8.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi 0.3.9",
|
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -676,7 +653,6 @@ dependencies = [
|
|||||||
"structopt",
|
"structopt",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tui-input",
|
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"version_check",
|
"version_check",
|
||||||
@ -935,22 +911,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ratatui"
|
name = "ratatui"
|
||||||
version = "0.28.1"
|
version = "0.26.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d"
|
checksum = "154b85ef15a5d1719bcaa193c3c81fe645cd120c156874cd660fe49fd21d1373"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
"cassowary",
|
"cassowary",
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"instability",
|
"indoc",
|
||||||
"itertools",
|
"itertools",
|
||||||
"lru",
|
"lru",
|
||||||
"paste",
|
"paste",
|
||||||
|
"stability",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-truncate",
|
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1011,9 +986,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.37"
|
version = "0.38.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
"errno",
|
"errno",
|
||||||
@ -1152,12 +1127,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-mio"
|
name = "signal-hook-mio"
|
||||||
version = "0.2.4"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mio 1.0.2",
|
"mio",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1214,6 +1189,16 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stability"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -1261,11 +1246,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum_macros"
|
name = "strum_macros"
|
||||||
version = "0.26.4"
|
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 = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
@ -1392,7 +1377,7 @@ dependencies = [
|
|||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio 0.8.10",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
@ -1485,16 +1470,6 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tui-input"
|
|
||||||
version = "0.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bd137780d743c103a391e06fe952487f914b299a4fe2c3626677f6a6339a7c6b"
|
|
||||||
dependencies = [
|
|
||||||
"ratatui",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typed-builder"
|
name = "typed-builder"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
@ -1542,22 +1517,11 @@ version = "1.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-truncate"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
|
||||||
dependencies = [
|
|
||||||
"itertools",
|
|
||||||
"unicode-segmentation",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.13"
|
version = "0.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
|
@ -7,18 +7,16 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aho-corasick = { version = "1.1.2", optional = true }
|
aho-corasick = { version = "1.1.2", optional = true }
|
||||||
crossterm = { version = "0.28.1", optional = true}
|
crossterm = { version = "0.27.0", optional = true}
|
||||||
once_cell = { version = "1.19.0", optional = true}
|
once_cell = { version = "1.19.0", optional = true}
|
||||||
openssh = { version = "0.10.3", features = ["native-mux"], default-features = false, optional = true}
|
openssh = { version = "0.10.3", features = ["native-mux"], default-features = false, optional = true}
|
||||||
paste = { version = "1.0.15", optional = true }
|
paste = { version = "1.0.15", optional = true }
|
||||||
ratatui = { version = "0.28.1", optional = true}
|
ratatui = { version = "0.26.0", optional = true}
|
||||||
reqwest = { version = "0.11.25", features = ["blocking", "json"], optional = true }
|
reqwest = { version = "0.11.25", features = ["blocking", "json"], optional = true }
|
||||||
serde = { version = "1.0.196", features = ["derive"], optional = true }
|
serde = { version = "1.0.196", features = ["derive"], optional = true }
|
||||||
serde_json = { version = "1.0.113", optional = true}
|
serde_json = { version = "1.0.113", optional = true}
|
||||||
structopt = { version = "0.3.26", optional = true}
|
structopt = { version = "0.3.26", optional = true}
|
||||||
tokio = { version = "1.36.0", features = ["rt"], optional = true}
|
tokio = { version = "1.36.0", features = ["rt"], optional = true}
|
||||||
# ratatui/crossterm dependency version must match with musichhoard's ratatui/crossterm
|
|
||||||
tui-input = { version = "0.10.1", optional = true }
|
|
||||||
url = { version = "2.5.0" }
|
url = { version = "2.5.0" }
|
||||||
uuid = { version = "1.7.0" }
|
uuid = { version = "1.7.0" }
|
||||||
|
|
||||||
@ -37,7 +35,7 @@ database-json = ["serde", "serde_json"]
|
|||||||
library-beets = []
|
library-beets = []
|
||||||
library-beets-ssh = ["openssh", "tokio"]
|
library-beets-ssh = ["openssh", "tokio"]
|
||||||
musicbrainz = ["paste", "reqwest", "serde", "serde_json"]
|
musicbrainz = ["paste", "reqwest", "serde", "serde_json"]
|
||||||
tui = ["aho-corasick", "crossterm", "once_cell", "ratatui", "tui-input"]
|
tui = ["aho-corasick", "crossterm", "once_cell", "ratatui"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "musichoard"
|
name = "musichoard"
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
use tui_input::backend::crossterm::EventHandler;
|
|
||||||
|
|
||||||
use crate::tui::app::{machine::App, AppState, IAppInput, InputEvent, InputPublic};
|
|
||||||
|
|
||||||
use super::AppInputMode;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Input(tui_input::Input);
|
|
||||||
|
|
||||||
impl<'app> From<&'app Input> for InputPublic<'app> {
|
|
||||||
fn from(value: &'app Input) -> Self {
|
|
||||||
&value.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IAppInput for AppInputMode {
|
|
||||||
type APP = App;
|
|
||||||
|
|
||||||
fn input(mut self, input: InputEvent) -> Self::APP {
|
|
||||||
self.input
|
|
||||||
.0
|
|
||||||
.handle_event(&crossterm::event::Event::Key(input));
|
|
||||||
self.app.inner_mut().input.replace(self.input);
|
|
||||||
self.app
|
|
||||||
}
|
|
||||||
|
|
||||||
fn confirm(self) -> Self::APP {
|
|
||||||
self.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cancel(mut self) -> Self::APP {
|
|
||||||
if let AppState::Match(state) = &mut self.app {
|
|
||||||
state.submit_input(self.input)
|
|
||||||
};
|
|
||||||
self.app
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ use crate::tui::app::{
|
|||||||
MatchStateInfo, MatchStatePublic, WidgetState,
|
MatchStateInfo, MatchStatePublic, WidgetState,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{fetch_state::FetchState, input::Input};
|
use super::fetch_state::FetchState;
|
||||||
|
|
||||||
impl ArtistMatches {
|
impl ArtistMatches {
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
@ -16,14 +16,6 @@ impl ArtistMatches {
|
|||||||
fn push_cannot_have_mbid(&mut self) {
|
fn push_cannot_have_mbid(&mut self) {
|
||||||
self.list.push(MatchOption::CannotHaveMbid)
|
self.list.push(MatchOption::CannotHaveMbid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_manual_input_mbid(&mut self) {
|
|
||||||
self.list.push(MatchOption::ManualInputMbid)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_manual_input_mbid(&self, index: usize) -> bool {
|
|
||||||
self.list.get(index) == Some(&MatchOption::ManualInputMbid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlbumMatches {
|
impl AlbumMatches {
|
||||||
@ -34,14 +26,6 @@ impl AlbumMatches {
|
|||||||
fn push_cannot_have_mbid(&mut self) {
|
fn push_cannot_have_mbid(&mut self) {
|
||||||
self.list.push(MatchOption::CannotHaveMbid)
|
self.list.push(MatchOption::CannotHaveMbid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_manual_input_mbid(&mut self) {
|
|
||||||
self.list.push(MatchOption::ManualInputMbid)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_manual_input_mbid(&self, index: usize) -> bool {
|
|
||||||
self.list.get(index) == Some(&MatchOption::ManualInputMbid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchStateInfo {
|
impl MatchStateInfo {
|
||||||
@ -52,26 +36,12 @@ impl MatchStateInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_cannot_have_mbid(&mut self) {
|
pub fn push_cannot_have_mbid(&mut self) {
|
||||||
match self {
|
match self {
|
||||||
Self::Artist(a) => a.push_cannot_have_mbid(),
|
Self::Artist(a) => a.push_cannot_have_mbid(),
|
||||||
Self::Album(a) => a.push_cannot_have_mbid(),
|
Self::Album(a) => a.push_cannot_have_mbid(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_manual_input_mbid(&mut self) {
|
|
||||||
match self {
|
|
||||||
Self::Artist(a) => a.push_manual_input_mbid(),
|
|
||||||
Self::Album(a) => a.push_manual_input_mbid(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_manual_input_mbid(&self, index: usize) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Artist(a) => a.is_manual_input_mbid(index),
|
|
||||||
Self::Album(a) => a.is_manual_input_mbid(index),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MatchState {
|
pub struct MatchState {
|
||||||
@ -86,7 +56,6 @@ impl MatchState {
|
|||||||
if let Some(ref mut current) = current {
|
if let Some(ref mut current) = current {
|
||||||
state.list.select(Some(0));
|
state.list.select(Some(0));
|
||||||
current.push_cannot_have_mbid();
|
current.push_cannot_have_mbid();
|
||||||
current.push_manual_input_mbid();
|
|
||||||
}
|
}
|
||||||
MatchState {
|
MatchState {
|
||||||
current,
|
current,
|
||||||
@ -100,8 +69,6 @@ impl AppMachine<MatchState> {
|
|||||||
pub fn match_state(inner: AppInner, state: MatchState) -> Self {
|
pub fn match_state(inner: AppInner, state: MatchState) -> Self {
|
||||||
AppMachine { inner, state }
|
AppMachine { inner, state }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn submit_input(&mut self, _input: Input) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AppMachine<MatchState>> for App {
|
impl From<AppMachine<MatchState>> for App {
|
||||||
@ -110,20 +77,14 @@ impl From<AppMachine<MatchState>> for App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a mut MatchState> for MatchStatePublic<'a> {
|
|
||||||
fn from(state: &'a mut MatchState) -> Self {
|
|
||||||
MatchStatePublic {
|
|
||||||
info: state.current.as_ref().map(Into::into),
|
|
||||||
state: &mut state.state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a mut AppMachine<MatchState>> for AppPublic<'a> {
|
impl<'a> From<&'a mut AppMachine<MatchState>> for AppPublic<'a> {
|
||||||
fn from(machine: &'a mut AppMachine<MatchState>) -> Self {
|
fn from(machine: &'a mut AppMachine<MatchState>) -> Self {
|
||||||
AppPublic {
|
AppPublic {
|
||||||
inner: (&mut machine.inner).into(),
|
inner: (&mut machine.inner).into(),
|
||||||
state: AppState::Match((&mut machine.state).into()),
|
state: AppState::Match(MatchStatePublic {
|
||||||
|
info: machine.state.current.as_ref().map(Into::into),
|
||||||
|
state: &mut machine.state.state,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,20 +115,7 @@ impl IAppInteractMatch for AppMachine<MatchState> {
|
|||||||
self.into()
|
self.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select(mut self) -> Self::APP {
|
fn select(self) -> Self::APP {
|
||||||
if let Some(index) = self.state.state.list.selected() {
|
|
||||||
// selected() implies current exists
|
|
||||||
if self
|
|
||||||
.state
|
|
||||||
.current
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.is_manual_input_mbid(index)
|
|
||||||
{
|
|
||||||
self.inner.input = Some(Input::default());
|
|
||||||
return self.into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AppMachine::app_fetch_next(self.inner, self.state.fetch)
|
AppMachine::app_fetch_next(self.inner, self.state.fetch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ mod critical_state;
|
|||||||
mod error_state;
|
mod error_state;
|
||||||
mod fetch_state;
|
mod fetch_state;
|
||||||
mod info_state;
|
mod info_state;
|
||||||
mod input;
|
|
||||||
mod match_state;
|
mod match_state;
|
||||||
mod reload_state;
|
mod reload_state;
|
||||||
mod search_state;
|
mod search_state;
|
||||||
@ -21,12 +20,11 @@ use critical_state::CriticalState;
|
|||||||
use error_state::ErrorState;
|
use error_state::ErrorState;
|
||||||
use fetch_state::FetchState;
|
use fetch_state::FetchState;
|
||||||
use info_state::InfoState;
|
use info_state::InfoState;
|
||||||
use input::Input;
|
|
||||||
use match_state::MatchState;
|
use match_state::MatchState;
|
||||||
use reload_state::ReloadState;
|
use reload_state::ReloadState;
|
||||||
use search_state::SearchState;
|
use search_state::SearchState;
|
||||||
|
|
||||||
use super::{AppMode, IAppBase};
|
use super::IAppBase;
|
||||||
|
|
||||||
pub type App = AppState<
|
pub type App = AppState<
|
||||||
AppMachine<BrowseState>,
|
AppMachine<BrowseState>,
|
||||||
@ -50,18 +48,6 @@ pub struct AppInner {
|
|||||||
musicbrainz: Arc<Mutex<dyn IMusicBrainz + Send>>,
|
musicbrainz: Arc<Mutex<dyn IMusicBrainz + Send>>,
|
||||||
selection: Selection,
|
selection: Selection,
|
||||||
events: EventSender,
|
events: EventSender,
|
||||||
input: Option<Input>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppInputMode {
|
|
||||||
input: Input,
|
|
||||||
app: App,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppInputMode {
|
|
||||||
fn new(input: Input, app: App) -> Self {
|
|
||||||
AppInputMode { input, app }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
@ -119,7 +105,6 @@ impl IApp for App {
|
|||||||
type MatchState = AppMachine<MatchState>;
|
type MatchState = AppMachine<MatchState>;
|
||||||
type ErrorState = AppMachine<ErrorState>;
|
type ErrorState = AppMachine<ErrorState>;
|
||||||
type CriticalState = AppMachine<CriticalState>;
|
type CriticalState = AppMachine<CriticalState>;
|
||||||
type InputMode = AppInputMode;
|
|
||||||
|
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.inner_ref().running
|
self.inner_ref().running
|
||||||
@ -144,28 +129,6 @@ impl IApp for App {
|
|||||||
> {
|
> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mode(
|
|
||||||
mut self,
|
|
||||||
) -> super::AppMode<
|
|
||||||
AppState<
|
|
||||||
Self::BrowseState,
|
|
||||||
Self::InfoState,
|
|
||||||
Self::ReloadState,
|
|
||||||
Self::SearchState,
|
|
||||||
Self::FetchState,
|
|
||||||
Self::MatchState,
|
|
||||||
Self::ErrorState,
|
|
||||||
Self::CriticalState,
|
|
||||||
>,
|
|
||||||
Self::InputMode,
|
|
||||||
> {
|
|
||||||
if let Some(input) = self.inner_mut().input.take() {
|
|
||||||
AppMode::Input(AppInputMode::new(input, self.state()))
|
|
||||||
} else {
|
|
||||||
AppMode::State(self.state())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<App>> IAppBase for T {
|
impl<T: Into<App>> IAppBase for T {
|
||||||
@ -179,14 +142,14 @@ impl<T: Into<App>> IAppBase for T {
|
|||||||
impl IAppAccess for App {
|
impl IAppAccess for App {
|
||||||
fn get(&mut self) -> AppPublic {
|
fn get(&mut self) -> AppPublic {
|
||||||
match self {
|
match self {
|
||||||
AppState::Browse(state) => state.into(),
|
AppState::Browse(browse) => browse.into(),
|
||||||
AppState::Info(state) => state.into(),
|
AppState::Info(info) => info.into(),
|
||||||
AppState::Reload(state) => state.into(),
|
AppState::Reload(reload) => reload.into(),
|
||||||
AppState::Search(state) => state.into(),
|
AppState::Search(search) => search.into(),
|
||||||
AppState::Fetch(state) => state.into(),
|
AppState::Fetch(fetch) => fetch.into(),
|
||||||
AppState::Match(state) => state.into(),
|
AppState::Match(matches) => matches.into(),
|
||||||
AppState::Error(state) => state.into(),
|
AppState::Error(error) => error.into(),
|
||||||
AppState::Critical(state) => state.into(),
|
AppState::Critical(critical) => critical.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,7 +167,6 @@ impl AppInner {
|
|||||||
musicbrainz: Arc::new(Mutex::new(musicbrainz)),
|
musicbrainz: Arc::new(Mutex::new(musicbrainz)),
|
||||||
selection,
|
selection,
|
||||||
events,
|
events,
|
||||||
input: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,7 +176,6 @@ impl<'a> From<&'a mut AppInner> for AppPublicInner<'a> {
|
|||||||
AppPublicInner {
|
AppPublicInner {
|
||||||
collection: inner.music_hoard.get_collection(),
|
collection: inner.music_hoard.get_collection(),
|
||||||
selection: &mut inner.selection,
|
selection: &mut inner.selection,
|
||||||
input: inner.input.as_ref().map(Into::into),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,77 +194,57 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl<
|
impl<BS, IS, RS, SS, FS, MS, ES, CS> AppState<BS, IS, RS, SS, FS, MS, ES, CS> {
|
||||||
BrowseState,
|
pub fn unwrap_browse(self) -> BS {
|
||||||
InfoState,
|
|
||||||
ReloadState,
|
|
||||||
SearchState,
|
|
||||||
FetchState,
|
|
||||||
MatchState,
|
|
||||||
ErrorState,
|
|
||||||
CriticalState,
|
|
||||||
>
|
|
||||||
AppState<
|
|
||||||
BrowseState,
|
|
||||||
InfoState,
|
|
||||||
ReloadState,
|
|
||||||
SearchState,
|
|
||||||
FetchState,
|
|
||||||
MatchState,
|
|
||||||
ErrorState,
|
|
||||||
CriticalState,
|
|
||||||
>
|
|
||||||
{
|
|
||||||
pub fn unwrap_browse(self) -> BrowseState {
|
|
||||||
match self {
|
match self {
|
||||||
AppState::Browse(browse) => browse,
|
AppState::Browse(browse) => browse,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_info(self) -> InfoState {
|
pub fn unwrap_info(self) -> IS {
|
||||||
match self {
|
match self {
|
||||||
AppState::Info(info) => info,
|
AppState::Info(info) => info,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_reload(self) -> ReloadState {
|
pub fn unwrap_reload(self) -> RS {
|
||||||
match self {
|
match self {
|
||||||
AppState::Reload(reload) => reload,
|
AppState::Reload(reload) => reload,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_search(self) -> SearchState {
|
pub fn unwrap_search(self) -> SS {
|
||||||
match self {
|
match self {
|
||||||
AppState::Search(search) => search,
|
AppState::Search(search) => search,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_fetch(self) -> FetchState {
|
pub fn unwrap_fetch(self) -> FS {
|
||||||
match self {
|
match self {
|
||||||
AppState::Fetch(fetch) => fetch,
|
AppState::Fetch(fetch) => fetch,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_match(self) -> MatchState {
|
pub fn unwrap_match(self) -> MS {
|
||||||
match self {
|
match self {
|
||||||
AppState::Match(matches) => matches,
|
AppState::Match(matches) => matches,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_error(self) -> ErrorState {
|
pub fn unwrap_error(self) -> ES {
|
||||||
match self {
|
match self {
|
||||||
AppState::Error(error) => error,
|
AppState::Error(error) => error,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_critical(self) -> CriticalState {
|
pub fn unwrap_critical(self) -> CS {
|
||||||
match self {
|
match self {
|
||||||
AppState::Critical(critical) => critical,
|
AppState::Critical(critical) => critical,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
@ -462,7 +403,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn state_match() {
|
fn state_matches() {
|
||||||
let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
|
let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
|
||||||
assert!(app.is_running());
|
assert!(app.is_running());
|
||||||
|
|
||||||
|
@ -8,27 +8,24 @@ use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, Collection};
|
|||||||
|
|
||||||
use crate::tui::lib::interface::musicbrainz::Match;
|
use crate::tui::lib::interface::musicbrainz::Match;
|
||||||
|
|
||||||
pub enum AppState<B, I, R, S, F, M, E, C> {
|
pub enum AppState<
|
||||||
Browse(B),
|
BrowseState,
|
||||||
Info(I),
|
InfoState,
|
||||||
Reload(R),
|
ReloadState,
|
||||||
Search(S),
|
SearchState,
|
||||||
Fetch(F),
|
FetchState,
|
||||||
Match(M),
|
MatchState,
|
||||||
Error(E),
|
ErrorState,
|
||||||
Critical(C),
|
CriticalState,
|
||||||
}
|
> {
|
||||||
|
Browse(BrowseState),
|
||||||
pub enum AppMode<StateMode, InputMode> {
|
Info(InfoState),
|
||||||
State(StateMode),
|
Reload(ReloadState),
|
||||||
Input(InputMode),
|
Search(SearchState),
|
||||||
}
|
Fetch(FetchState),
|
||||||
|
Match(MatchState),
|
||||||
macro_rules! IAppState {
|
Error(ErrorState),
|
||||||
() => {
|
Critical(CriticalState),
|
||||||
AppState<Self::BrowseState, Self::InfoState, Self::ReloadState, Self::SearchState,
|
|
||||||
Self::FetchState, Self::MatchState, Self::ErrorState, Self::CriticalState>
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait IApp {
|
pub trait IApp {
|
||||||
@ -42,15 +39,23 @@ pub trait IApp {
|
|||||||
type MatchState: IAppBase<APP = Self> + IAppInteractMatch<APP = Self>;
|
type MatchState: IAppBase<APP = Self> + IAppInteractMatch<APP = Self>;
|
||||||
type ErrorState: IAppBase<APP = Self> + IAppInteractError<APP = Self>;
|
type ErrorState: IAppBase<APP = Self> + IAppInteractError<APP = Self>;
|
||||||
type CriticalState: IAppBase<APP = Self>;
|
type CriticalState: IAppBase<APP = Self>;
|
||||||
type InputMode: IAppInput<APP = Self>;
|
|
||||||
|
|
||||||
fn is_running(&self) -> bool;
|
fn is_running(&self) -> bool;
|
||||||
fn force_quit(self) -> Self;
|
fn force_quit(self) -> Self;
|
||||||
|
|
||||||
fn state(self) -> IAppState!();
|
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn mode(self) -> AppMode<IAppState!(), Self::InputMode>;
|
fn state(
|
||||||
|
self,
|
||||||
|
) -> AppState<
|
||||||
|
Self::BrowseState,
|
||||||
|
Self::InfoState,
|
||||||
|
Self::ReloadState,
|
||||||
|
Self::SearchState,
|
||||||
|
Self::FetchState,
|
||||||
|
Self::MatchState,
|
||||||
|
Self::ErrorState,
|
||||||
|
Self::CriticalState,
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait IAppBase {
|
pub trait IAppBase {
|
||||||
@ -124,15 +129,6 @@ pub trait IAppInteractMatch {
|
|||||||
fn abort(self) -> Self::APP;
|
fn abort(self) -> Self::APP;
|
||||||
}
|
}
|
||||||
|
|
||||||
type InputEvent = crossterm::event::KeyEvent;
|
|
||||||
pub trait IAppInput {
|
|
||||||
type APP: IApp;
|
|
||||||
|
|
||||||
fn input(self, input: InputEvent) -> Self::APP;
|
|
||||||
fn confirm(self) -> Self::APP;
|
|
||||||
fn cancel(self) -> Self::APP;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait IAppInteractError {
|
pub trait IAppInteractError {
|
||||||
type APP: IApp;
|
type APP: IApp;
|
||||||
|
|
||||||
@ -155,16 +151,12 @@ pub struct AppPublic<'app> {
|
|||||||
pub struct AppPublicInner<'app> {
|
pub struct AppPublicInner<'app> {
|
||||||
pub collection: &'app Collection,
|
pub collection: &'app Collection,
|
||||||
pub selection: &'app mut Selection,
|
pub selection: &'app mut Selection,
|
||||||
pub input: Option<InputPublic<'app>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type InputPublic<'app> = &'app tui_input::Input;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum MatchOption<T> {
|
pub enum MatchOption<T> {
|
||||||
Match(Match<T>),
|
Match(Match<T>),
|
||||||
CannotHaveMbid,
|
CannotHaveMbid,
|
||||||
ManualInputMbid,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<Match<T>> for MatchOption<T> {
|
impl<T> From<Match<T>> for MatchOption<T> {
|
||||||
@ -211,7 +203,7 @@ pub struct MatchStatePublic<'app> {
|
|||||||
pub type AppPublicState<'app> =
|
pub type AppPublicState<'app> =
|
||||||
AppState<(), (), (), &'app str, (), MatchStatePublic<'app>, &'app str, &'app str>;
|
AppState<(), (), (), &'app str, (), MatchStatePublic<'app>, &'app str, &'app str>;
|
||||||
|
|
||||||
impl<B, I, R, S, F, M, E, C> AppState<B, I, R, S, F, M, E, C> {
|
impl<BS, IS, RS, SS, FS, MS, ES, CS> AppState<BS, IS, RS, SS, FS, MS, ES, CS> {
|
||||||
pub fn is_search(&self) -> bool {
|
pub fn is_search(&self) -> bool {
|
||||||
matches!(self, AppState::Search(_))
|
matches!(self, AppState::Search(_))
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use crate::tui::{
|
|||||||
event::{Event, EventError, EventReceiver},
|
event::{Event, EventError, EventReceiver},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::app::{AppMode, IAppBase, IAppEventFetch, IAppInput};
|
use super::app::{IAppBase, IAppEventFetch};
|
||||||
|
|
||||||
#[cfg_attr(test, automock)]
|
#[cfg_attr(test, automock)]
|
||||||
pub trait IEventHandler<APP: IApp> {
|
pub trait IEventHandler<APP: IApp> {
|
||||||
@ -30,8 +30,6 @@ trait IEventHandlerPrivate<APP: IApp> {
|
|||||||
fn handle_critical_key_event(app: <APP as IApp>::CriticalState, key_event: KeyEvent) -> APP;
|
fn handle_critical_key_event(app: <APP as IApp>::CriticalState, key_event: KeyEvent) -> APP;
|
||||||
|
|
||||||
fn handle_fetch_result_ready_event(app: APP) -> APP;
|
fn handle_fetch_result_ready_event(app: APP) -> APP;
|
||||||
|
|
||||||
fn handle_input_key_event<Input: IAppInput<APP = APP>>(app: Input, key_event: KeyEvent) -> APP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventHandler {
|
pub struct EventHandler {
|
||||||
@ -64,45 +62,36 @@ impl<APP: IApp> IEventHandlerPrivate<APP> for EventHandler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match app.mode() {
|
match app.state() {
|
||||||
AppMode::Input(input_mode) => Self::handle_input_key_event(input_mode, key_event),
|
AppState::Browse(browse_state) => {
|
||||||
AppMode::State(state_mode) => match state_mode {
|
Self::handle_browse_key_event(browse_state, key_event)
|
||||||
AppState::Browse(browse_state) => {
|
}
|
||||||
Self::handle_browse_key_event(browse_state, key_event)
|
AppState::Info(info_state) => Self::handle_info_key_event(info_state, key_event),
|
||||||
}
|
AppState::Reload(reload_state) => {
|
||||||
AppState::Info(info_state) => Self::handle_info_key_event(info_state, key_event),
|
Self::handle_reload_key_event(reload_state, key_event)
|
||||||
AppState::Reload(reload_state) => {
|
}
|
||||||
Self::handle_reload_key_event(reload_state, key_event)
|
AppState::Search(search_state) => {
|
||||||
}
|
Self::handle_search_key_event(search_state, key_event)
|
||||||
AppState::Search(search_state) => {
|
}
|
||||||
Self::handle_search_key_event(search_state, key_event)
|
AppState::Fetch(fetch_state) => Self::handle_fetch_key_event(fetch_state, key_event),
|
||||||
}
|
AppState::Match(match_state) => Self::handle_match_key_event(match_state, key_event),
|
||||||
AppState::Fetch(fetch_state) => {
|
AppState::Error(error_state) => Self::handle_error_key_event(error_state, key_event),
|
||||||
Self::handle_fetch_key_event(fetch_state, key_event)
|
AppState::Critical(critical_state) => {
|
||||||
}
|
Self::handle_critical_key_event(critical_state, key_event)
|
||||||
AppState::Match(match_state) => {
|
}
|
||||||
Self::handle_match_key_event(match_state, key_event)
|
|
||||||
}
|
|
||||||
AppState::Error(error_state) => {
|
|
||||||
Self::handle_error_key_event(error_state, key_event)
|
|
||||||
}
|
|
||||||
AppState::Critical(critical_state) => {
|
|
||||||
Self::handle_critical_key_event(critical_state, key_event)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_fetch_result_ready_event(app: APP) -> APP {
|
fn handle_fetch_result_ready_event(app: APP) -> APP {
|
||||||
match app.state() {
|
match app.state() {
|
||||||
AppState::Browse(state) => state.no_op(),
|
AppState::Browse(browse_state) => browse_state.no_op(),
|
||||||
AppState::Info(state) => state.no_op(),
|
AppState::Info(info_state) => info_state.no_op(),
|
||||||
AppState::Reload(state) => state.no_op(),
|
AppState::Reload(reload_state) => reload_state.no_op(),
|
||||||
AppState::Search(state) => state.no_op(),
|
AppState::Search(search_state) => search_state.no_op(),
|
||||||
AppState::Fetch(fetch_state) => fetch_state.fetch_result_ready(),
|
AppState::Fetch(fetch_state) => fetch_state.fetch_result_ready(),
|
||||||
AppState::Match(state) => state.no_op(),
|
AppState::Match(match_state) => match_state.no_op(),
|
||||||
AppState::Error(state) => state.no_op(),
|
AppState::Error(error_state) => error_state.no_op(),
|
||||||
AppState::Critical(state) => state.no_op(),
|
AppState::Critical(critical_state) => critical_state.no_op(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,13 +175,6 @@ impl<APP: IApp> IEventHandlerPrivate<APP> for EventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_fetch_key_event(app: <APP as IApp>::FetchState, key_event: KeyEvent) -> APP {
|
fn handle_fetch_key_event(app: <APP as IApp>::FetchState, key_event: KeyEvent) -> APP {
|
||||||
if key_event.modifiers == KeyModifiers::CONTROL {
|
|
||||||
return match key_event.code {
|
|
||||||
KeyCode::Char('g') | KeyCode::Char('G') => app.abort(),
|
|
||||||
_ => app.no_op(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
// Abort.
|
// Abort.
|
||||||
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(),
|
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(),
|
||||||
@ -202,13 +184,6 @@ impl<APP: IApp> IEventHandlerPrivate<APP> for EventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_match_key_event(app: <APP as IApp>::MatchState, key_event: KeyEvent) -> APP {
|
fn handle_match_key_event(app: <APP as IApp>::MatchState, key_event: KeyEvent) -> APP {
|
||||||
if key_event.modifiers == KeyModifiers::CONTROL {
|
|
||||||
return match key_event.code {
|
|
||||||
KeyCode::Char('g') | KeyCode::Char('G') => app.abort(),
|
|
||||||
_ => app.no_op(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
// Abort.
|
// Abort.
|
||||||
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(),
|
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(),
|
||||||
@ -230,22 +205,5 @@ impl<APP: IApp> IEventHandlerPrivate<APP> for EventHandler {
|
|||||||
// No action is allowed.
|
// No action is allowed.
|
||||||
app.no_op()
|
app.no_op()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input_key_event<Input: IAppInput<APP = APP>>(app: Input, key_event: KeyEvent) -> APP {
|
|
||||||
if key_event.modifiers == KeyModifiers::CONTROL {
|
|
||||||
match key_event.code {
|
|
||||||
KeyCode::Char('g') | KeyCode::Char('G') => return app.cancel(),
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match key_event.code {
|
|
||||||
// Return.
|
|
||||||
KeyCode::Esc => app.cancel(),
|
|
||||||
KeyCode::Enter => app.confirm(),
|
|
||||||
// Othey keys.
|
|
||||||
_ => app.input(key_event),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// GRCOV_EXCL_STOP
|
// GRCOV_EXCL_STOP
|
||||||
|
@ -28,14 +28,10 @@ pub struct TrackArea {
|
|||||||
pub info: Rect,
|
pub info: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BrowseArea {
|
pub struct FrameArea {
|
||||||
pub artist: ArtistArea,
|
pub artist: ArtistArea,
|
||||||
pub album: AlbumArea,
|
pub album: AlbumArea,
|
||||||
pub track: TrackArea,
|
pub track: TrackArea,
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FrameArea {
|
|
||||||
pub browse: BrowseArea,
|
|
||||||
pub minibuffer: Rect,
|
pub minibuffer: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,16 +91,14 @@ impl FrameArea {
|
|||||||
};
|
};
|
||||||
|
|
||||||
FrameArea {
|
FrameArea {
|
||||||
browse: BrowseArea {
|
artist: ArtistArea { list: artist_list },
|
||||||
artist: ArtistArea { list: artist_list },
|
album: AlbumArea {
|
||||||
album: AlbumArea {
|
list: album_list,
|
||||||
list: album_list,
|
info: album_info,
|
||||||
info: album_info,
|
},
|
||||||
},
|
track: TrackArea {
|
||||||
track: TrackArea {
|
list: track_list,
|
||||||
list: track_list,
|
info: track_info,
|
||||||
info: track_info,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
minibuffer,
|
minibuffer,
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,6 @@ impl UiDisplay {
|
|||||||
match_artist.score,
|
match_artist.score,
|
||||||
),
|
),
|
||||||
MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(),
|
MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(),
|
||||||
MatchOption::ManualInputMbid => Self::display_manual_input_mbid().to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,17 +153,12 @@ impl UiDisplay {
|
|||||||
match_album.score,
|
match_album.score,
|
||||||
),
|
),
|
||||||
MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(),
|
MatchOption::CannotHaveMbid => Self::display_cannot_have_mbid().to_string(),
|
||||||
MatchOption::ManualInputMbid => Self::display_manual_input_mbid().to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_cannot_have_mbid() -> &'static str {
|
fn display_cannot_have_mbid() -> &'static str {
|
||||||
"-- Cannot have a MusicBrainz Identifier --"
|
"-- Cannot have a MusicBrainz Identifier --"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_manual_input_mbid() -> &'static str {
|
|
||||||
"-- Manually enter a MusicBrainz Identifier --"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -57,16 +57,13 @@ impl Minibuffer<'_> {
|
|||||||
columns,
|
columns,
|
||||||
},
|
},
|
||||||
AppState::Fetch(()) => Minibuffer {
|
AppState::Fetch(()) => Minibuffer {
|
||||||
paragraphs: vec![
|
paragraphs: vec![Paragraph::new("fetching..."), Paragraph::new("q: abort")],
|
||||||
Paragraph::new("fetching..."),
|
|
||||||
Paragraph::new("ctrl+g: abort"),
|
|
||||||
],
|
|
||||||
columns: 2,
|
columns: 2,
|
||||||
},
|
},
|
||||||
AppState::Match(public) => Minibuffer {
|
AppState::Match(public) => Minibuffer {
|
||||||
paragraphs: vec![
|
paragraphs: vec![
|
||||||
Paragraph::new(UiDisplay::display_matching_info(public.info)),
|
Paragraph::new(UiDisplay::display_matching_info(public.info)),
|
||||||
Paragraph::new("ctrl+g: abort"),
|
Paragraph::new("q: abort"),
|
||||||
],
|
],
|
||||||
columns: 2,
|
columns: 2,
|
||||||
},
|
},
|
||||||
|
@ -10,7 +10,6 @@ mod reload_state;
|
|||||||
mod style;
|
mod style;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
use browse_state::BrowseArea;
|
|
||||||
use ratatui::{layout::Rect, widgets::Paragraph, Frame};
|
use ratatui::{layout::Rect, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
use musichoard::collection::{album::Album, Collection};
|
use musichoard::collection::{album::Album, Collection};
|
||||||
@ -33,8 +32,6 @@ use crate::tui::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::app::InputPublic;
|
|
||||||
|
|
||||||
pub trait IUi {
|
pub trait IUi {
|
||||||
fn render<APP: IAppAccess>(app: &mut APP, frame: &mut Frame);
|
fn render<APP: IAppAccess>(app: &mut APP, frame: &mut Frame);
|
||||||
}
|
}
|
||||||
@ -67,10 +64,11 @@ impl Ui {
|
|||||||
fn render_browse_frame(
|
fn render_browse_frame(
|
||||||
artists: &Collection,
|
artists: &Collection,
|
||||||
selection: &mut Selection,
|
selection: &mut Selection,
|
||||||
areas: BrowseArea,
|
state: &AppPublicState,
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
) {
|
) {
|
||||||
let active = selection.category();
|
let active = selection.category();
|
||||||
|
let areas = FrameArea::new(frame.size());
|
||||||
|
|
||||||
let artist_state = ArtistState::new(
|
let artist_state = ArtistState::new(
|
||||||
active == Category::Artist,
|
active == Category::Artist,
|
||||||
@ -103,10 +101,12 @@ impl Ui {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Self::render_track_column(track_state, areas.track, frame);
|
Self::render_track_column(track_state, areas.track, frame);
|
||||||
|
|
||||||
|
Self::render_minibuffer(state, areas.minibuffer, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_info_overlay(artists: &Collection, selection: &mut Selection, frame: &mut Frame) {
|
fn render_info_overlay(artists: &Collection, selection: &mut Selection, frame: &mut Frame) {
|
||||||
let area = OverlayBuilder::default().build(frame.area());
|
let area = OverlayBuilder::default().build(frame.size());
|
||||||
|
|
||||||
if selection.category() == Category::Artist {
|
if selection.category() == Category::Artist {
|
||||||
let overlay = ArtistOverlay::new(artists, &selection.widget_state_artist().list);
|
let overlay = ArtistOverlay::new(artists, &selection.widget_state_artist().list);
|
||||||
@ -126,13 +126,13 @@ impl Ui {
|
|||||||
let area = OverlayBuilder::default()
|
let area = OverlayBuilder::default()
|
||||||
.with_width(OverlaySize::Value(39))
|
.with_width(OverlaySize::Value(39))
|
||||||
.with_height(OverlaySize::Value(4))
|
.with_height(OverlaySize::Value(4))
|
||||||
.build(frame.area());
|
.build(frame.size());
|
||||||
let reload_text = ReloadOverlay::paragraph();
|
let reload_text = ReloadOverlay::paragraph();
|
||||||
UiWidget::render_overlay_widget("Reload", reload_text, area, false, frame);
|
UiWidget::render_overlay_widget("Reload", reload_text, area, false, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_fetch_overlay(frame: &mut Frame) {
|
fn render_fetch_overlay(frame: &mut Frame) {
|
||||||
let area = OverlayBuilder::default().build(frame.area());
|
let area = OverlayBuilder::default().build(frame.size());
|
||||||
let fetch_text = FetchOverlay::paragraph();
|
let fetch_text = FetchOverlay::paragraph();
|
||||||
UiWidget::render_overlay_widget("Fetching", fetch_text, area, false, frame)
|
UiWidget::render_overlay_widget("Fetching", fetch_text, area, false, frame)
|
||||||
}
|
}
|
||||||
@ -142,31 +142,15 @@ impl Ui {
|
|||||||
state: &mut WidgetState,
|
state: &mut WidgetState,
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
) {
|
) {
|
||||||
let area = OverlayBuilder::default().build(frame.area());
|
let area = OverlayBuilder::default().build(frame.size());
|
||||||
let st = MatchOverlay::new(info, state);
|
let st = MatchOverlay::new(info, state);
|
||||||
UiWidget::render_overlay_list_widget(&st.matching, st.list, st.state, true, area, frame)
|
UiWidget::render_overlay_list_widget(&st.matching, st.list, st.state, true, area, frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_input_overlay(input: InputPublic, frame: &mut Frame) {
|
|
||||||
let area = OverlayBuilder::default()
|
|
||||||
.with_width(OverlaySize::MarginFactor(4))
|
|
||||||
.with_height(OverlaySize::Value(3))
|
|
||||||
.build(frame.area());
|
|
||||||
let text_area = format!(" {}", input.value());
|
|
||||||
UiWidget::render_overlay_widget("Input", Paragraph::new(text_area), area, false, frame);
|
|
||||||
|
|
||||||
let width = area.width.max(4) - 4; // keep 2 for borders, 1 for left-pad, and 1 for cursor
|
|
||||||
let scroll = input.visual_scroll(width as usize);
|
|
||||||
frame.set_cursor_position((
|
|
||||||
area.x + ((input.visual_cursor()).max(scroll) - scroll) as u16 + 2,
|
|
||||||
area.y + 1,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_error_overlay<S: AsRef<str>>(title: S, msg: S, frame: &mut Frame) {
|
fn render_error_overlay<S: AsRef<str>>(title: S, msg: S, frame: &mut Frame) {
|
||||||
let area = OverlayBuilder::default()
|
let area = OverlayBuilder::default()
|
||||||
.with_height(OverlaySize::Value(4))
|
.with_height(OverlaySize::Value(4))
|
||||||
.build(frame.area());
|
.build(frame.size());
|
||||||
let error_text = ErrorOverlay::paragraph(msg.as_ref());
|
let error_text = ErrorOverlay::paragraph(msg.as_ref());
|
||||||
UiWidget::render_overlay_widget(title.as_ref(), error_text, area, true, frame);
|
UiWidget::render_overlay_widget(title.as_ref(), error_text, area, true, frame);
|
||||||
}
|
}
|
||||||
@ -180,11 +164,7 @@ impl IUi for Ui {
|
|||||||
let selection = app.inner.selection;
|
let selection = app.inner.selection;
|
||||||
let state = app.state;
|
let state = app.state;
|
||||||
|
|
||||||
let areas = FrameArea::new(frame.area());
|
Self::render_browse_frame(collection, selection, &state, frame);
|
||||||
|
|
||||||
Self::render_browse_frame(collection, selection, areas.browse, frame);
|
|
||||||
Self::render_minibuffer(&state, areas.minibuffer, frame);
|
|
||||||
|
|
||||||
match state {
|
match state {
|
||||||
AppState::Info(()) => Self::render_info_overlay(collection, selection, frame),
|
AppState::Info(()) => Self::render_info_overlay(collection, selection, frame),
|
||||||
AppState::Reload(()) => Self::render_reload_overlay(frame),
|
AppState::Reload(()) => Self::render_reload_overlay(frame),
|
||||||
@ -194,10 +174,6 @@ impl IUi for Ui {
|
|||||||
AppState::Critical(msg) => Self::render_error_overlay("Critical Error", msg, frame),
|
AppState::Critical(msg) => Self::render_error_overlay("Critical Error", msg, frame),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(input) = app.inner.input {
|
|
||||||
Self::render_input_overlay(input, frame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +200,6 @@ mod tests {
|
|||||||
inner: AppPublicInner {
|
inner: AppPublicInner {
|
||||||
collection: self.inner.collection,
|
collection: self.inner.collection,
|
||||||
selection: self.inner.selection,
|
selection: self.inner.selection,
|
||||||
input: self.inner.input,
|
|
||||||
},
|
},
|
||||||
state: match self.state {
|
state: match self.state {
|
||||||
AppState::Browse(()) => AppState::Browse(()),
|
AppState::Browse(()) => AppState::Browse(()),
|
||||||
@ -250,7 +225,6 @@ mod tests {
|
|||||||
AppPublicInner {
|
AppPublicInner {
|
||||||
collection,
|
collection,
|
||||||
selection,
|
selection,
|
||||||
input: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user