[feat] first version
This commit is contained in:
parent
a8d59e48d1
commit
fa3a2e60cd
204
Cargo.lock
generated
204
Cargo.lock
generated
@ -2,6 +2,17 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -20,6 +31,12 @@ version = "1.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.9.1"
|
version = "3.9.1"
|
||||||
@ -50,6 +67,58 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "3.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b63edc3f163b3c71ec8aa23f9bd6070f77edbf3d1d198b164afa90ff00e4ec62"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"clap_derive",
|
||||||
|
"indexmap",
|
||||||
|
"lazy_static",
|
||||||
|
"os_str_bytes",
|
||||||
|
"strsim",
|
||||||
|
"termcolor",
|
||||||
|
"textwrap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "3.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clipboard"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7"
|
||||||
|
dependencies = [
|
||||||
|
"clipboard-win",
|
||||||
|
"objc",
|
||||||
|
"objc-foundation",
|
||||||
|
"objc_id",
|
||||||
|
"x11-clipboard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clipboard-win"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
@ -213,6 +282,12 @@ version = "0.11.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
@ -380,6 +455,15 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "malloc_buf"
|
||||||
|
version = "0.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matches"
|
name = "matches"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@ -457,6 +541,35 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
||||||
|
dependencies = [
|
||||||
|
"malloc_buf",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc-foundation"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
|
||||||
|
dependencies = [
|
||||||
|
"block",
|
||||||
|
"objc",
|
||||||
|
"objc_id",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc_id"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
|
||||||
|
dependencies = [
|
||||||
|
"objc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
@ -496,6 +609,15 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_str_bytes"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@ -545,6 +667,30 @@ version = "0.3.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.36"
|
version = "1.0.36"
|
||||||
@ -576,12 +722,15 @@ dependencies = [
|
|||||||
name = "registry-cli"
|
name = "registry-cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"clipboard",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tui",
|
"tui",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -769,6 +918,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.86"
|
version = "1.0.86"
|
||||||
@ -794,6 +949,21 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.5.1"
|
version = "1.5.1"
|
||||||
@ -959,6 +1129,12 @@ version = "0.2.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -1061,6 +1237,15 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -1075,3 +1260,22 @@ checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x11-clipboard"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea"
|
||||||
|
dependencies = [
|
||||||
|
"xcb",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xcb"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e917a3f24142e9ff8be2414e36c649d47d6cc2ba81f16201cdef96e533e02de"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
@ -12,4 +12,6 @@ serde = {version = "1.0.136", features = ["derive"]}
|
|||||||
serde_json = "1.0.78"
|
serde_json = "1.0.78"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tui = "0.17.0"
|
tui = "0.17.0"
|
||||||
|
clap = { version = "3.0.14", features = ["derive"] }
|
||||||
|
clipboard = "0.5.0"
|
||||||
|
url = "2.2.2"
|
||||||
|
422
src/main.rs
422
src/main.rs
@ -1,31 +1,397 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
use clipboard::{ClipboardContext, ClipboardProvider};
|
||||||
|
use std::{
|
||||||
|
io, thread,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crossterm::{
|
||||||
|
event::{DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
||||||
|
execute,
|
||||||
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
|
};
|
||||||
|
use tui::{
|
||||||
|
backend::{Backend, CrosstermBackend},
|
||||||
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
|
style::{Color, Style},
|
||||||
|
text::{Span, Spans},
|
||||||
|
widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
|
||||||
|
Frame, Terminal,
|
||||||
|
};
|
||||||
|
|
||||||
mod registry;
|
mod registry;
|
||||||
|
|
||||||
fn main() {
|
struct StatefulList<T> {
|
||||||
println!("Hello, world!");
|
state: ListState,
|
||||||
|
items: Vec<T>,
|
||||||
let r = registry::api::Registry {
|
}
|
||||||
endpoint: "https://docker.mtfos.xyz".to_string(),
|
impl<T> StatefulList<T> {
|
||||||
user: "".to_string(),
|
fn with_items(items: Vec<T>) -> StatefulList<T> {
|
||||||
password: "".to_string(),
|
StatefulList {
|
||||||
};
|
state: ListState::default(),
|
||||||
|
items,
|
||||||
// let images = r.list_images().unwrap();
|
}
|
||||||
|
}
|
||||||
// println!("images ::: {:?}", images);
|
|
||||||
|
fn next(&mut self) {
|
||||||
// let tags = r.get_image_tags("mtfos/go-bot").unwrap();
|
let i = match self.state.selected() {
|
||||||
|
Some(i) => {
|
||||||
// println!("tags ::: {:?}", tags);
|
if i >= self.items.len() - 1 {
|
||||||
|
0
|
||||||
let digest = match r.get_image_tag_manifest("demo/node-hello", "v1") {
|
} else {
|
||||||
Ok(digest) => digest,
|
i + 1
|
||||||
Err(e) => panic!("digest err ::: {:?}", e),
|
}
|
||||||
};
|
}
|
||||||
|
None => 0,
|
||||||
println!("digest ::: {:?}", digest);
|
};
|
||||||
|
self.state.select(Some(i))
|
||||||
match r.delete_image_tag("demo/node-hello", &digest) {
|
}
|
||||||
Ok(_) => println!("deleted"),
|
|
||||||
Err(e) => println!("error ::: {:?}", e),
|
fn previous(&mut self) {
|
||||||
};
|
let i = match self.state.selected() {
|
||||||
|
Some(i) => {
|
||||||
|
if i <= 0 {
|
||||||
|
self.items.len() - 1
|
||||||
|
} else {
|
||||||
|
i - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => self.items.len() - 1,
|
||||||
|
};
|
||||||
|
self.state.select(Some(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unselect(&mut self) {
|
||||||
|
self.state.select(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected(&self) -> Option<&T> {
|
||||||
|
self.state.selected().map(|i| &self.items[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PageState {
|
||||||
|
Images,
|
||||||
|
Tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LoadingState {
|
||||||
|
Init,
|
||||||
|
Loading,
|
||||||
|
Loaded,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
registry_uri: String,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
|
||||||
|
registry: Option<registry::api::Registry>,
|
||||||
|
|
||||||
|
page_state: PageState,
|
||||||
|
loading_state: LoadingState,
|
||||||
|
|
||||||
|
selected_image: Option<String>,
|
||||||
|
images: Option<StatefulList<String>>,
|
||||||
|
tags: Option<StatefulList<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
fn new(uri: String, username: String, password: String) -> App {
|
||||||
|
let mut app = App {
|
||||||
|
registry_uri: uri,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
|
||||||
|
registry: None,
|
||||||
|
|
||||||
|
page_state: PageState::Images,
|
||||||
|
loading_state: LoadingState::Init,
|
||||||
|
|
||||||
|
selected_image: None,
|
||||||
|
images: None,
|
||||||
|
tags: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let api = registry::api::Registry {
|
||||||
|
endpoint: app.registry_uri.to_string(),
|
||||||
|
user: app.username.to_string(),
|
||||||
|
password: app.password.to_string(),
|
||||||
|
};
|
||||||
|
app.registry = Some(api);
|
||||||
|
|
||||||
|
app
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Parser)]
|
||||||
|
#[clap(version = "0.1.0")]
|
||||||
|
struct Args {
|
||||||
|
#[clap(short, long)]
|
||||||
|
registry_uri: String,
|
||||||
|
#[clap(short, long, default_value = "")]
|
||||||
|
username: String,
|
||||||
|
#[clap(short, long, default_value = "")]
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args = Args::parse();
|
||||||
|
if args.registry_uri.is_empty() {
|
||||||
|
println!("Please provide a registry URI");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_raw_mode()?;
|
||||||
|
|
||||||
|
let mut stdout = io::stdout();
|
||||||
|
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
||||||
|
|
||||||
|
let backend = CrosstermBackend::new(stdout);
|
||||||
|
|
||||||
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
|
let app = App::new(args.registry_uri, args.username, args.password);
|
||||||
|
|
||||||
|
let res = run_app(&mut terminal, app);
|
||||||
|
|
||||||
|
disable_raw_mode()?;
|
||||||
|
execute!(
|
||||||
|
terminal.backend_mut(),
|
||||||
|
LeaveAlternateScreen,
|
||||||
|
DisableMouseCapture
|
||||||
|
)?;
|
||||||
|
|
||||||
|
terminal.show_cursor()?;
|
||||||
|
|
||||||
|
if let Err(e) = res {
|
||||||
|
println!("{}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_app<B: Backend>(
|
||||||
|
terminal: &mut Terminal<B>,
|
||||||
|
mut app: App,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut last_tick = Instant::now();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
terminal.draw(|f| ui(f, &mut app))?;
|
||||||
|
|
||||||
|
match app.loading_state {
|
||||||
|
LoadingState::Init => {
|
||||||
|
app.loading_state = LoadingState::Loading;
|
||||||
|
loading(&mut app);
|
||||||
|
}
|
||||||
|
LoadingState::Loading => {}
|
||||||
|
LoadingState::Loaded => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeout = Duration::from_millis(250)
|
||||||
|
.checked_sub(last_tick.elapsed())
|
||||||
|
.unwrap_or(Duration::from_millis(0));
|
||||||
|
|
||||||
|
if crossterm::event::poll(timeout)? {
|
||||||
|
let event = crossterm::event::read()?;
|
||||||
|
if let Event::Key(key) = event {
|
||||||
|
match key.code {
|
||||||
|
KeyCode::Char('q') => return Ok(()),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
match app.page_state {
|
||||||
|
PageState::Images => match key.code {
|
||||||
|
KeyCode::Char('r') => {
|
||||||
|
// reload list of images
|
||||||
|
app.loading_state = LoadingState::Init;
|
||||||
|
}
|
||||||
|
KeyCode::Down => {
|
||||||
|
if let Some(ref mut images) = app.images {
|
||||||
|
images.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Up => {
|
||||||
|
if let Some(ref mut images) = app.images {
|
||||||
|
images.previous();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Enter => {
|
||||||
|
app.selected_image = app.images.as_ref().unwrap().selected().cloned();
|
||||||
|
app.page_state = PageState::Tags;
|
||||||
|
app.loading_state = LoadingState::Init;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
PageState::Tags => match key.code {
|
||||||
|
KeyCode::Esc => {
|
||||||
|
app.selected_image = None;
|
||||||
|
app.tags = None;
|
||||||
|
app.page_state = PageState::Images;
|
||||||
|
}
|
||||||
|
KeyCode::Down => {
|
||||||
|
if let Some(ref mut tags) = app.tags {
|
||||||
|
tags.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Up => {
|
||||||
|
if let Some(ref mut tags) = app.tags {
|
||||||
|
tags.previous();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Char('c') => {
|
||||||
|
// copy docker image uri
|
||||||
|
if let Some(ref image) = app.selected_image {
|
||||||
|
if let Some(ref tags) = app.tags {
|
||||||
|
if let Some(ref tag) = tags.selected() {
|
||||||
|
let mut u = url::Url::parse(&app.registry_uri)?;
|
||||||
|
u.set_path(image);
|
||||||
|
|
||||||
|
let uri = u
|
||||||
|
.to_string()
|
||||||
|
.replace(&format!("{}://", u.scheme()).to_string(), "");
|
||||||
|
|
||||||
|
let uri_str = format!("{}:{}", uri, tag);
|
||||||
|
let mut clipboard: ClipboardContext =
|
||||||
|
ClipboardProvider::new()?;
|
||||||
|
clipboard.set_contents(uri_str)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if last_tick.elapsed() >= Duration::from_millis(250) {
|
||||||
|
last_tick = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn loading(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
match app.page_state {
|
||||||
|
PageState::Images => {
|
||||||
|
if let Some(registry) = &app.registry {
|
||||||
|
let images = registry.list_images()?;
|
||||||
|
app.images = Some(StatefulList::with_items(images));
|
||||||
|
app.loading_state = LoadingState::Loaded;
|
||||||
|
if let Some(ref mut images) = app.images {
|
||||||
|
images.state.select(Some(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PageState::Tags => {
|
||||||
|
if let Some(ref image) = app.selected_image {
|
||||||
|
let image_tags = app.registry.as_ref().unwrap().get_image_tags(image)?;
|
||||||
|
app.tags = Some(StatefulList::with_items(image_tags.tags));
|
||||||
|
app.loading_state = LoadingState::Loaded;
|
||||||
|
if let Some(ref mut tags) = app.tags {
|
||||||
|
tags.state.select(Some(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.constraints(
|
||||||
|
[
|
||||||
|
Constraint::Percentage(10),
|
||||||
|
Constraint::Percentage(5),
|
||||||
|
Constraint::Percentage(85),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(f.size());
|
||||||
|
|
||||||
|
// chunk 0 is imfomation
|
||||||
|
show_info(f, app, chunks[0]);
|
||||||
|
|
||||||
|
// chunk 1 is the loading state
|
||||||
|
match app.loading_state {
|
||||||
|
LoadingState::Init | LoadingState::Loaded => {
|
||||||
|
f.render_widget(Block::default().borders(Borders::ALL), chunks[1]);
|
||||||
|
}
|
||||||
|
LoadingState::Loading => {
|
||||||
|
let txt = Spans::from(vec![Span::styled(
|
||||||
|
"Loading...",
|
||||||
|
Style::default().fg(Color::Yellow),
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let paragraph = Paragraph::new(txt).block(Block::default().borders(Borders::ALL));
|
||||||
|
f.render_widget(paragraph, chunks[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match app.page_state {
|
||||||
|
PageState::Images => page_images(f, app, chunks[2]),
|
||||||
|
PageState::Tags => page_tags(f, app, chunks[2]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_info<B: Backend>(f: &mut Frame<B>, app: &mut App, area: Rect) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.margin(1)
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([Constraint::Percentage(40), Constraint::Percentage(60)])
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
let mut texts = vec![Spans::from(vec![
|
||||||
|
Span::raw("Registry: "),
|
||||||
|
Span::raw(&app.registry_uri),
|
||||||
|
])];
|
||||||
|
|
||||||
|
if let Some(ref selected_image) = app.selected_image {
|
||||||
|
texts.push(Spans::from(vec![
|
||||||
|
Span::raw("Selected Image: "),
|
||||||
|
Span::raw(selected_image.to_owned()),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
let paragraph = Paragraph::new(texts).block(Block::default().borders(Borders::NONE));
|
||||||
|
f.render_widget(paragraph, chunks[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_images<B: Backend>(f: &mut Frame<B>, app: &mut App, area: Rect) {
|
||||||
|
if let Some(ref mut images) = app.images {
|
||||||
|
let items: Vec<ListItem> = images
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.map(|i| {
|
||||||
|
let line = Spans::from(vec![Span::raw(i.as_str())]);
|
||||||
|
ListItem::new(line)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let list = List::new(items)
|
||||||
|
.block(Block::default().borders(Borders::ALL))
|
||||||
|
.highlight_style(Style::default().bg(Color::LightGreen).fg(Color::Black));
|
||||||
|
|
||||||
|
f.render_stateful_widget(list, area, &mut images.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_tags<B: Backend>(f: &mut Frame<B>, app: &mut App, area: Rect) {
|
||||||
|
if let Some(ref mut tags) = app.tags {
|
||||||
|
let items: Vec<ListItem> = tags
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.map(|i| {
|
||||||
|
let line = Spans::from(vec![Span::raw(i.as_str())]);
|
||||||
|
ListItem::new(line)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let list = List::new(items)
|
||||||
|
.block(Block::default().borders(Borders::ALL))
|
||||||
|
.highlight_style(Style::default().bg(Color::LightGreen).fg(Color::Black));
|
||||||
|
|
||||||
|
f.render_stateful_widget(list, area, &mut tags.state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ impl Registry {
|
|||||||
|
|
||||||
let headers = resp.headers().to_owned();
|
let headers = resp.headers().to_owned();
|
||||||
|
|
||||||
println!("\n{:?}\n", headers);
|
// println!("\n{:?}\n", headers);
|
||||||
|
|
||||||
if !headers
|
if !headers
|
||||||
.get("Content-Type")
|
.get("Content-Type")
|
||||||
|
Loading…
Reference in New Issue
Block a user