Compare commits
1 commit
master
...
record_lis
Author | SHA1 | Date | |
---|---|---|---|
e30e867933 |
4 changed files with 471 additions and 141 deletions
287
Cargo.lock
generated
287
Cargo.lock
generated
|
@ -17,6 +17,18 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloc-no-stdlib"
|
||||
version = "2.0.4"
|
||||
|
@ -32,6 +44,12 @@ dependencies = [
|
|||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.11"
|
||||
|
@ -226,6 +244,21 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
|
||||
[[package]]
|
||||
name = "cassowary"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
|
@ -275,7 +308,7 @@ version = "4.5.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -293,6 +326,19 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
|
@ -302,6 +348,31 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.10.0"
|
||||
|
@ -330,8 +401,10 @@ version = "0.1.1"
|
|||
dependencies = [
|
||||
"axum",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"nix",
|
||||
"qpidfile",
|
||||
"ratatui",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -436,6 +509,22 @@ version = "0.28.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
|
@ -569,12 +658,27 @@ dependencies = [
|
|||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
|
@ -608,12 +712,31 @@ version = "0.4.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.3"
|
||||
|
@ -648,6 +771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
@ -705,6 +829,35 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
|
@ -767,6 +920,35 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"crossterm",
|
||||
"indoc",
|
||||
"itertools",
|
||||
"lru",
|
||||
"paste",
|
||||
"stability",
|
||||
"strum",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.2"
|
||||
|
@ -896,6 +1078,12 @@ version = "1.0.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.197"
|
||||
|
@ -958,6 +1146,27 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
|
@ -998,12 +1207,50 @@ version = "0.9.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "stability"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
|
@ -1257,6 +1504,18 @@ dependencies = [
|
|||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
|
@ -1292,6 +1551,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
|
@ -1574,6 +1839,26 @@ version = "0.0.19"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.7.0"
|
||||
|
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||
[features]
|
||||
default = ["server", "editor"]
|
||||
server = ["tokio", "qpidfile", "axum"]
|
||||
editor = ["reqwest", "tempfile", "which", "nix"]
|
||||
editor = ["reqwest", "tempfile", "which", "nix", "ratatui", "crossterm"]
|
||||
|
||||
[dependencies]
|
||||
qpidfile = { version = "0.9.2", optional = true }
|
||||
|
@ -26,6 +26,8 @@ tempfile = { version = "3.10.1", optional = true }
|
|||
which = { version = "6.0.1", optional = true }
|
||||
nix = { version = "0.28.0", optional = true, default-features = false, features = ["signal"] }
|
||||
urlencoding = { version = "2.1.3"}
|
||||
ratatui = { version = "0.26.2", optional = true }
|
||||
crossterm = { version = "0.27.0", optional = true }
|
||||
|
||||
[profile.release] # 💛 @Ryze@equestria.social
|
||||
strip = "symbols"
|
||||
|
|
|
@ -1,9 +1,180 @@
|
|||
use std::io;
|
||||
use std::io::{Stdout, stdout};
|
||||
use crate::schema::resource_list::ResourceList;
|
||||
use std::path::PathBuf;
|
||||
use crossterm::{event, ExecutableCommand, execute};
|
||||
use crossterm::event::{Event, KeyCode, KeyEventKind};
|
||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
|
||||
use ratatui::prelude::*;
|
||||
use ratatui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum TerminalSetupError {
|
||||
#[error("failed to enable raw mode: {0}")]
|
||||
EnableRawMode(io::Error),
|
||||
#[error("failed to enter alternate screen: {0}")]
|
||||
EnterAlternateScreen(io::Error),
|
||||
#[error("failed to create Ratatui terminal: {0}")]
|
||||
TerminalCreation(io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum TerminalRestore {
|
||||
#[error("failed to disable raw mode for terminal: {0}")]
|
||||
DisableRawMode(io::Error),
|
||||
#[error("failed to leave alternate screen: {0}")]
|
||||
LeaveAlternateScreen(io::Error),
|
||||
}
|
||||
|
||||
fn setup_terminal() -> Result<Terminal<CrosstermBackend<Stdout>>, TerminalSetupError> {
|
||||
let mut stdout = std::io::stdout();
|
||||
enable_raw_mode().map_err(TerminalSetupError::EnableRawMode)?;
|
||||
execute!(stdout, EnterAlternateScreen).map_err(TerminalSetupError::EnterAlternateScreen)?;
|
||||
Terminal::new(CrosstermBackend::new(stdout)).map_err(TerminalSetupError::TerminalCreation)
|
||||
}
|
||||
|
||||
struct ResourceListGUI {
|
||||
resources: ResourceList,
|
||||
list_state: ListState,
|
||||
}
|
||||
|
||||
impl ResourceListGUI {
|
||||
fn new(resources: ResourceList) -> Self {
|
||||
Self {
|
||||
resources,
|
||||
list_state: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&mut self, terminal: &mut Terminal<impl Backend>) {
|
||||
terminal.draw(|f| f.render_widget(self, f.size())).expect("Failed to draw self");
|
||||
}
|
||||
|
||||
fn run(mut self, terminal: &mut Terminal<impl Backend>) {
|
||||
loop {
|
||||
self.draw(terminal);
|
||||
|
||||
if let Event::Key(key) = event::read().unwrap() {
|
||||
if key.kind == KeyEventKind::Press {
|
||||
use KeyCode::*;
|
||||
match key.code {
|
||||
Char('q') | Esc => return,
|
||||
Char('j') | Down => self.next_item(),
|
||||
Char('k') | Up => self.previous_item(),
|
||||
Home => self.go_top(),
|
||||
End => self.go_bottom(),
|
||||
Char('d') => self.delete_current(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_item(&mut self) {
|
||||
if self.resources.0.is_empty() {return}
|
||||
let next_selected = if let Some(current_selected) = self.list_state.selected() {
|
||||
if current_selected >= self.resources.0.len() - 1 {
|
||||
0
|
||||
} else {
|
||||
current_selected + 1
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
self.list_state.select(Some(next_selected))
|
||||
}
|
||||
|
||||
fn previous_item(&mut self) {
|
||||
if self.resources.0.is_empty() {return}
|
||||
let next_selected = if let Some(current_selected) = self.list_state.selected() {
|
||||
if current_selected == 0 {
|
||||
self.resources.0.len()-1
|
||||
} else {
|
||||
current_selected - 1
|
||||
}
|
||||
} else {
|
||||
self.resources.0.len()-1
|
||||
};
|
||||
|
||||
self.list_state.select(Some(next_selected))
|
||||
}
|
||||
|
||||
fn go_top(&mut self) {
|
||||
if self.resources.0.is_empty() {return}
|
||||
self.list_state.select(Some(0))
|
||||
}
|
||||
|
||||
fn go_bottom(&mut self) {
|
||||
if self.resources.0.is_empty() {return}
|
||||
self.list_state.select(Some(self.resources.0.len() - 1))
|
||||
}
|
||||
|
||||
fn delete_current(&mut self) {
|
||||
if let Some(selected) = self.list_state.selected() {
|
||||
self.resources.0.remove(selected);
|
||||
|
||||
let next_selected = if self.resources.0.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(selected - 1)
|
||||
};
|
||||
|
||||
self.list_state.select(next_selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &mut ResourceListGUI {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) where Self: Sized {
|
||||
let [body, footer] = Layout::vertical([
|
||||
Constraint::Min(1),
|
||||
Constraint::Length(1),
|
||||
]).areas(area);
|
||||
|
||||
let [list_area, details] = Layout::horizontal([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Fill(2)
|
||||
]).areas(body);
|
||||
|
||||
let list_block = Block::new()
|
||||
.borders(Borders::TOP)
|
||||
.title("Resources");
|
||||
|
||||
let items = self.resources.0.iter().map(|resource| ListItem::new(resource.subject.clone()));
|
||||
let list = List::new(items)
|
||||
.block(list_block)
|
||||
.highlight_symbol(">");
|
||||
StatefulWidget::render(list, list_area, buf, &mut self.list_state);
|
||||
|
||||
if let Some(selected) = self.list_state.selected() {
|
||||
let selected = &self.resources.0.get(selected).unwrap();
|
||||
let details_block = Block::new()
|
||||
.borders(Borders::LEFT | Borders::TOP)
|
||||
.title("Resource details");
|
||||
let text = Paragraph::new(serde_json::to_string_pretty(selected).unwrap_or_default())
|
||||
.block(details_block);
|
||||
text.render(details, buf);
|
||||
}
|
||||
|
||||
let footer_text = Paragraph::new("Arrow keys navigate | d: delete")
|
||||
.centered()
|
||||
.on_dark_gray();
|
||||
footer_text.render(footer, buf);
|
||||
}
|
||||
}
|
||||
|
||||
fn restore_terminal() -> Result<(), TerminalRestore>{
|
||||
disable_raw_mode().map_err(TerminalRestore::DisableRawMode)?;
|
||||
stdout().execute(LeaveAlternateScreen).map_err(TerminalRestore::LeaveAlternateScreen)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn list(database_path: PathBuf) {
|
||||
let resources = ResourceList::load(database_path).unwrap();
|
||||
for resource in resources.0 {
|
||||
println!("{}", resource.subject)
|
||||
}
|
||||
let mut terminal = setup_terminal().unwrap();
|
||||
ResourceListGUI::new(resources).run(&mut terminal);
|
||||
restore_terminal().unwrap();
|
||||
}
|
||||
|
|
|
@ -114,7 +114,6 @@ impl Resource {
|
|||
.map(clone_hashmap_with_option_value_as_complete)
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
template: Some(link.template.clone().unwrap_or_default()),
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
@ -147,36 +146,6 @@ impl Resource {
|
|||
.links
|
||||
.filter(|links| !links.is_empty())
|
||||
.map(|mut links| {
|
||||
// Collapse default subvalues
|
||||
for link in &mut links {
|
||||
if link.media_type.as_ref().map_or(false, String::is_empty) {
|
||||
link.media_type = None;
|
||||
}
|
||||
if link.href.as_ref().map_or(false, String::is_empty) {
|
||||
link.href = None;
|
||||
}
|
||||
if let Some(titles) = &mut link.titles {
|
||||
titles.retain(|key, value| !key.is_empty() || !value.is_empty())
|
||||
}
|
||||
if link.titles.as_ref().map_or(false, HashMap::is_empty) {
|
||||
link.titles = None;
|
||||
}
|
||||
if let Some(properties) = &mut link.properties {
|
||||
for value in properties.values_mut() {
|
||||
if value.as_ref().map_or(false, String::is_empty) {
|
||||
*value = None;
|
||||
}
|
||||
}
|
||||
properties.retain(|key, value| !key.is_empty() || !value.is_none())
|
||||
}
|
||||
if link.properties.as_ref().map_or(false, HashMap::is_empty) {
|
||||
link.properties = None;
|
||||
}
|
||||
if link.template.as_ref().map_or(false, String::is_empty) {
|
||||
link.template = None;
|
||||
}
|
||||
}
|
||||
// Delete completely default links
|
||||
links.retain(|link| {
|
||||
// Empty `rel` is invalid, but short-circuiting here would delete records
|
||||
// that are only partially edited. Better to store invalid data than to delete
|
||||
|
@ -184,19 +153,22 @@ impl Resource {
|
|||
let mut is_default = link.rel.is_empty();
|
||||
is_default &= link
|
||||
.media_type
|
||||
.as_ref()
|
||||
.filter(|media_type| !media_type.is_empty())
|
||||
.is_none();
|
||||
is_default &= link.href.as_ref().filter(|href| !href.is_empty()).is_none();
|
||||
is_default &= link
|
||||
.titles
|
||||
.as_ref()
|
||||
.filter(|titles| !titles.is_empty())
|
||||
.is_none();
|
||||
is_default &= link
|
||||
.properties
|
||||
.is_none();
|
||||
is_default &= link
|
||||
.template
|
||||
.as_ref()
|
||||
.filter(|titles| !titles.is_empty())
|
||||
.is_none();
|
||||
|
||||
!is_default
|
||||
is_default
|
||||
});
|
||||
|
||||
links
|
||||
|
@ -217,15 +189,12 @@ pub struct Link {
|
|||
pub titles: Option<HashMap<String, String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub properties: Option<HashMap<String, Option<String>>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Functions to generate data for testing functions that manipulate `Resource` structs
|
||||
pub mod test_data {
|
||||
use std::collections::HashMap;
|
||||
use crate::schema::resource::{Link, Resource};
|
||||
use crate::schema::resource::Resource;
|
||||
|
||||
/// A [`Resource`] with only the `subject` field set
|
||||
pub fn barebones_user() -> Resource {
|
||||
|
@ -251,100 +220,6 @@ pub mod test_data {
|
|||
links: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn example_mastodon_user() -> Resource {
|
||||
Resource {
|
||||
subject: "acct:user@example.com".to_string(),
|
||||
aliases: Some(vec![
|
||||
"https://example.com/@user".to_string(),
|
||||
"https://example.com/users/user".to_string()
|
||||
]),
|
||||
properties: None,
|
||||
links: Some(vec![
|
||||
Link {
|
||||
rel: "http://webfinger.net/rel/profile-page".to_string(),
|
||||
media_type: Some("text/html".to_string()),
|
||||
href: Some("https://example.com/@user".to_string()),
|
||||
titles: None,
|
||||
properties: None,
|
||||
template: None,
|
||||
},
|
||||
Link {
|
||||
rel: "self".to_string(),
|
||||
media_type: Some("application/activity+json".to_string()),
|
||||
href: Some("https://example.com/users/user".to_string()),
|
||||
titles: None,
|
||||
properties: None,
|
||||
template: None,
|
||||
},
|
||||
Link {
|
||||
rel: "http://ostatus.org/schema/1.0/subscribe".to_string(),
|
||||
media_type: None,
|
||||
href: None,
|
||||
titles: None,
|
||||
properties: None,
|
||||
template: Some("https://example.com/authorize_interaction?uri={uri}".to_string()),
|
||||
},
|
||||
Link {
|
||||
rel: "http://webfinger.net/rel/avatar".to_string(),
|
||||
media_type: Some("image/png".to_string()),
|
||||
href: Some("https://example.com/system/accounts/avatars/321/423/112/234/123/527/original/1j2ioff88a9wa.png".to_string()),
|
||||
titles: None,
|
||||
properties: None,
|
||||
template: None,
|
||||
}
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn example_firefish_user() -> Resource {
|
||||
Resource {
|
||||
subject: "acct:user@example.com".to_string(),
|
||||
aliases: None,
|
||||
properties: None,
|
||||
links: Some(vec![
|
||||
Link {
|
||||
rel: "self".to_string(),
|
||||
media_type: Some("application/activity+json".to_string()),
|
||||
href: Some("https://example.com/users/8fsua89lcieaj".to_string()),
|
||||
titles: None,
|
||||
properties: None,
|
||||
template: None,
|
||||
},
|
||||
Link {
|
||||
rel: "http://webfinger.net/rel/profile-page".to_string(),
|
||||
media_type: Some("text/html".to_string()),
|
||||
href: Some("https://example.com/@user".to_string()),
|
||||
titles: None,
|
||||
properties: None,
|
||||
template: None,
|
||||
},
|
||||
Link {
|
||||
rel: "http://ostatus.org/schema/1.0/subscribe".to_string(),
|
||||
media_type: None,
|
||||
href: None,
|
||||
titles: None,
|
||||
properties: None,
|
||||
template: Some("https://example.com/authorize-follow?acct={uri}".to_string()),
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
pub fn full_resource() -> Resource {
|
||||
Resource {
|
||||
subject: "some_subject".to_string(),
|
||||
aliases: Some(vec!["ThisIsAnAlias".to_string()]),
|
||||
properties: Some(HashMap::from_iter(std::iter::once(("property_key".to_string(), Some("property_value".to_string()))))),
|
||||
links: Some(vec![Link {
|
||||
rel: "link_relation".to_string(),
|
||||
media_type: Some("media/type".to_string()),
|
||||
href: Some("https://example.com/link_href".to_string()),
|
||||
titles: Some(HashMap::from_iter(std::iter::once(("title_key".to_string(), "title_value".to_string())))),
|
||||
properties: Some(HashMap::from_iter(std::iter::once(("property_key".to_string(), Some("property_value".to_string()))))),
|
||||
template: Some("template".to_string()),
|
||||
}]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -413,9 +288,6 @@ mod tests {
|
|||
test_data::barebones_user(),
|
||||
test_data::user_with_matching_subject_and_alias(),
|
||||
test_data::user_with_single_alias(),
|
||||
test_data::example_firefish_user(),
|
||||
test_data::example_mastodon_user(),
|
||||
test_data::full_resource(),
|
||||
] {
|
||||
assert_eq!(data, data.as_completely_serializable().compress());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue