rewrote parts to make it more async
This commit is contained in:
parent
c98558c41d
commit
87039541a5
152
Cargo.lock
generated
152
Cargo.lock
generated
@ -32,16 +32,16 @@ version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
@ -184,12 +184,6 @@ version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.16"
|
||||
@ -210,12 +204,9 @@ checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -226,7 +217,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -272,6 +263,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.4"
|
||||
@ -393,9 +393,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.99"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@ -426,24 +426,14 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.7.13"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
|
||||
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -464,15 +454,6 @@ dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
@ -494,11 +475,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.2.6",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@ -691,6 +672,7 @@ dependencies = [
|
||||
"structopt",
|
||||
"termion",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tui",
|
||||
]
|
||||
|
||||
@ -845,9 +827,9 @@ checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.1"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad"
|
||||
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
@ -956,7 +938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@ -977,9 +959,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.10.0"
|
||||
version = "1.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b"
|
||||
checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@ -988,7 +970,20 @@ dependencies = [
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"winapi",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1139,6 +1134,12 @@ version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.75"
|
||||
@ -1239,6 +1240,63 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.7.0"
|
||||
|
@ -9,7 +9,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
serde = { version = "1.0.127", features = ["derive"] }
|
||||
serde_json = "1.0.66"
|
||||
reqwest = { version = "0.11.4", features = ["blocking", "json"] }
|
||||
reqwest = { version = "0.11.4", features = ["json"] }
|
||||
chrono = "0.4.19"
|
||||
tui = "0.16"
|
||||
termion = "1.5"
|
||||
@ -18,6 +18,7 @@ lazy_static = "1.4.0"
|
||||
structopt = "0.3.23"
|
||||
thiserror = "1.0.32"
|
||||
anyhow = "1.0.59"
|
||||
tokio = { version = "1.24.2", features = ["macros", "rt-multi-thread"] }
|
||||
|
||||
[profile.release]
|
||||
lto = "yes"
|
||||
|
@ -46,17 +46,17 @@ pub struct DockerHub {
|
||||
|
||||
impl DockerHub {
|
||||
/// fetches tag information with a repository name in the form of organization/repository or library/repository in the case of official images from docker
|
||||
pub fn create_repo(repo: &str) -> Result<super::Repo, Error> {
|
||||
pub async fn create_repo(repo: &str) -> Result<super::Repo, Error> {
|
||||
let request = format!("https://hub.docker.com/v2/repositories/{}/tags", repo);
|
||||
Self::with_url(&request)
|
||||
Self::with_url(&request).await
|
||||
}
|
||||
|
||||
/// fetches tag information from a url
|
||||
pub fn with_url(url: &str) -> Result<super::Repo, Error> {
|
||||
let response = reqwest::blocking::get(url)?;
|
||||
pub async fn with_url(url: &str) -> Result<super::Repo, Error> {
|
||||
let response = reqwest::get(url).await?;
|
||||
|
||||
//convert it to json
|
||||
let tags = response.json::<Self>()?;
|
||||
let tags = response.json::<Self>().await?;
|
||||
if tags.results.is_empty() {
|
||||
return Err(Error::NoTagsFound);
|
||||
}
|
||||
|
@ -46,13 +46,14 @@ impl Tag {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Repo {
|
||||
tags: Vec<Tag>,
|
||||
next_page: Option<String>,
|
||||
}
|
||||
|
||||
impl Repo {
|
||||
pub fn new(repo: &str) -> Result<Self, Error> {
|
||||
pub async fn new(repo: &str) -> Result<Self, Error> {
|
||||
use crate::repo::Repo;
|
||||
let (registry, repo) = match crate::repo::split_repo_without_tag(repo) {
|
||||
Ok(Repo::WithServer(reg, org, pro)) => (Some(reg), format!("{}/{}", org, pro)),
|
||||
@ -62,7 +63,7 @@ impl Repo {
|
||||
};
|
||||
|
||||
if registry.unwrap_or_default().is_empty() {
|
||||
dockerhub::DockerHub::create_repo(&repo)
|
||||
dockerhub::DockerHub::create_repo(&repo).await
|
||||
} else {
|
||||
Err(Error::Converting(
|
||||
"This registry is not supported yet".into(),
|
||||
@ -70,18 +71,18 @@ impl Repo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_url(url: &str) -> Result<Self, Error> {
|
||||
pub async fn with_url(url: &str) -> Result<Self, Error> {
|
||||
//TODO fix for other registries
|
||||
dockerhub::DockerHub::with_url(url)
|
||||
dockerhub::DockerHub::with_url(url).await
|
||||
}
|
||||
|
||||
pub fn get_tags(&self) -> &Vec<Tag> {
|
||||
&self.tags
|
||||
}
|
||||
|
||||
pub fn next_page(&self) -> Option<Self> {
|
||||
pub async fn next_page(&self) -> Option<Self> {
|
||||
if let Some(url) = &self.next_page {
|
||||
match Self::with_url(url) {
|
||||
match Self::with_url(url).await {
|
||||
Ok(tags) => return Some(tags),
|
||||
Err(e) => println!("Encountered error: {e}"),
|
||||
}
|
||||
|
@ -1,225 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use termion::event::Key;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::layout::{Constraint, Direction, Layout};
|
||||
use tui::Terminal;
|
||||
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::repository;
|
||||
use crate::widget::info;
|
||||
use crate::widget::repo_entry;
|
||||
use crate::widget::service_switcher;
|
||||
use crate::widget::tag_list;
|
||||
use crate::Opt;
|
||||
|
||||
pub struct Ui {
|
||||
state: State,
|
||||
repo: crate::widget::repo_entry::RepoEntry,
|
||||
tags: crate::widget::tag_list::TagList,
|
||||
services: crate::widget::service_switcher::ServiceSwitcher,
|
||||
details: crate::widget::details::Details,
|
||||
info: crate::widget::info::Info,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum State {
|
||||
EditRepo,
|
||||
SelectTag,
|
||||
SelectService,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for State {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
State::EditRepo => write!(f, "Edit repository"),
|
||||
State::SelectTag => write!(f, "Select a tag"),
|
||||
State::SelectService => write!(f, "Select a image"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::Iterator for State {
|
||||
type Item = Self;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
State::EditRepo => *self = State::SelectTag,
|
||||
State::SelectTag => *self = State::SelectService,
|
||||
State::SelectService => *self = State::EditRepo,
|
||||
}
|
||||
Some(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
pub fn run(opt: &Opt, switcher: service_switcher::ServiceSwitcher) -> Result<()> {
|
||||
let repo_id = opt.repo.as_deref();
|
||||
|
||||
let mut ui = Ui {
|
||||
state: State::SelectService,
|
||||
repo: repo_entry::RepoEntry::new(repo_id),
|
||||
tags: tag_list::TagList::with_status("Tags are empty"),
|
||||
services: switcher,
|
||||
details: crate::widget::details::Details::new(),
|
||||
info: info::Info::new("Select image of edit Repository"),
|
||||
};
|
||||
|
||||
if opt.repo.is_none() {
|
||||
ui.tags = tag_list::TagList::with_repo_name(ui.repo.get());
|
||||
}
|
||||
|
||||
//setup tui
|
||||
let stdout = io::stdout().into_raw_mode()?;
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
//setup input thread
|
||||
let receiver = super::spawn_stdin_channel();
|
||||
|
||||
//core interaction loop
|
||||
'core: loop {
|
||||
//draw
|
||||
terminal.draw(|rect| {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(10),
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(7),
|
||||
Constraint::Length(2),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(rect.size());
|
||||
|
||||
let (list, state) = ui.services.render(ui.state == State::SelectService);
|
||||
rect.render_stateful_widget(list, chunks[0], state);
|
||||
rect.render_widget(ui.repo.render(ui.state == State::EditRepo), chunks[1]);
|
||||
let (list, state) = ui.tags.render(ui.state == State::SelectTag);
|
||||
let more_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Min(15), Constraint::Length(28)].as_ref())
|
||||
.split(chunks[2]);
|
||||
rect.render_stateful_widget(list, more_chunks[0], state);
|
||||
rect.render_widget(ui.details.render(), more_chunks[1]);
|
||||
rect.render_widget(ui.info.render(), chunks[3]);
|
||||
})?;
|
||||
|
||||
//handle input
|
||||
match receiver.try_recv() {
|
||||
Ok(Key::Ctrl('q')) => break 'core, //quit program without saving
|
||||
Ok(Key::Char('\t')) => {
|
||||
ui.state.next();
|
||||
ui.info.set_info(&ui.state);
|
||||
}
|
||||
Ok(Key::Ctrl('s')) => match ui.services.save() {
|
||||
Err(e) => {
|
||||
ui.info.set_info(&format!("{}", e));
|
||||
continue;
|
||||
}
|
||||
Ok(_) => ui.info.set_text("Saved compose file"),
|
||||
},
|
||||
Ok(Key::Ctrl('r')) => {
|
||||
ui.repo.confirm();
|
||||
ui.tags = tag_list::TagList::with_repo_name(ui.repo.get());
|
||||
}
|
||||
Ok(Key::Char('\n')) => match ui.state {
|
||||
State::EditRepo => {
|
||||
ui.repo.confirm();
|
||||
ui.tags = tag_list::TagList::with_repo_name(ui.repo.get());
|
||||
}
|
||||
State::SelectTag => {
|
||||
let mut repo = ui.repo.get();
|
||||
let tag = match ui.tags.get_selected() {
|
||||
Err(tag_list::Error::NextPageSelected) => continue,
|
||||
Err(e) => {
|
||||
ui.info.set_info(&format!("{}", e));
|
||||
continue;
|
||||
}
|
||||
Ok(tag) => tag,
|
||||
};
|
||||
repo.push(':');
|
||||
repo.push_str(&tag);
|
||||
ui.services.change_current_line(repo);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Ok(Key::Char(key)) => match ui.state {
|
||||
State::SelectService => (),
|
||||
State::EditRepo => {
|
||||
ui.info.set_text("Editing Repository");
|
||||
ui.repo.handle_input(Key::Char(key));
|
||||
}
|
||||
State::SelectTag => (),
|
||||
},
|
||||
Ok(Key::Backspace) => match ui.state {
|
||||
State::SelectService => (),
|
||||
State::EditRepo => {
|
||||
ui.info.set_text("Editing Repository");
|
||||
ui.repo.handle_input(Key::Backspace);
|
||||
}
|
||||
State::SelectTag => (),
|
||||
},
|
||||
Ok(Key::Up) => match ui.state {
|
||||
State::SelectService if ui.services.find_previous_match() => {
|
||||
match ui.services.extract_repo() {
|
||||
Err(e) => ui.info.set_info(&format!("{}", e)),
|
||||
Ok(s) => {
|
||||
let repo = match repository::check_repo(&s) {
|
||||
Err(e) => {
|
||||
ui.info.set_info(&format!("{}", e));
|
||||
continue;
|
||||
}
|
||||
Ok(s) => s,
|
||||
};
|
||||
ui.repo.set(repo.to_string());
|
||||
ui.tags = tag_list::TagList::with_repo_name(ui.repo.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
State::SelectService => (),
|
||||
State::EditRepo => (),
|
||||
State::SelectTag => {
|
||||
ui.tags.handle_input(Key::Up);
|
||||
ui.details = ui.tags.create_detail_widget();
|
||||
}
|
||||
},
|
||||
Ok(Key::Down) => match ui.state {
|
||||
State::SelectService if ui.services.find_next_match() => {
|
||||
match ui.services.extract_repo() {
|
||||
Err(e) => ui.info.set_info(&format!("{}", e)),
|
||||
Ok(s) => {
|
||||
let repo = match repository::check_repo(&s) {
|
||||
Err(e) => {
|
||||
ui.info.set_info(&format!("{}", e));
|
||||
continue;
|
||||
}
|
||||
Ok(s) => s,
|
||||
};
|
||||
ui.repo.set(repo.to_string());
|
||||
ui.tags = tag_list::TagList::with_repo_name(ui.repo.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
State::SelectService => (),
|
||||
State::EditRepo => (),
|
||||
State::SelectTag => {
|
||||
ui.tags.handle_input(Key::Down);
|
||||
ui.details = ui.tags.create_detail_widget();
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
//sleep for 32ms (30 fps)
|
||||
thread::sleep(std::time::Duration::from_millis(32));
|
||||
}
|
||||
|
||||
terminal.clear()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
mod default;
|
||||
mod no_yaml;
|
||||
mod no_yaml_found;
|
||||
mod yaml_found;
|
||||
|
||||
use anyhow::Result;
|
||||
use termion::input::TermRead;
|
||||
@ -13,8 +13,8 @@ use std::{io, thread};
|
||||
pub fn create_ui(opt: &Opt) -> Result<()> {
|
||||
let service_result = service_switcher::ServiceSwitcher::new(&opt.file);
|
||||
match service_result {
|
||||
None => no_yaml::NoYaml::run(opt),
|
||||
Some(switcher) => default::Ui::run(opt, switcher),
|
||||
Some(switcher) => yaml_found::Ui::run(opt, switcher),
|
||||
_ => no_yaml_found::Ui::run(opt),
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
|
@ -1,161 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use termion::event::Key;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::layout::{Constraint, Direction, Layout};
|
||||
use tui::Terminal;
|
||||
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::widget::details;
|
||||
use crate::widget::info;
|
||||
use crate::widget::repo_entry;
|
||||
use crate::widget::tag_list;
|
||||
use crate::Opt;
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum State {
|
||||
EditRepo,
|
||||
SelectTag,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for State {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
State::EditRepo => write!(f, "Edit repository"),
|
||||
State::SelectTag => write!(f, "Select a tag"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::Iterator for State {
|
||||
type Item = Self;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
State::EditRepo => *self = State::SelectTag,
|
||||
State::SelectTag => *self = State::EditRepo,
|
||||
}
|
||||
Some(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoYaml {
|
||||
state: State,
|
||||
repo: repo_entry::RepoEntry,
|
||||
tags: tag_list::TagList,
|
||||
details: details::Details,
|
||||
info: info::Info,
|
||||
}
|
||||
|
||||
impl NoYaml {
|
||||
pub fn run(opt: &Opt) -> Result<()> {
|
||||
let repo_id = opt.repo.as_deref();
|
||||
|
||||
let mut ui = NoYaml {
|
||||
state: State::EditRepo,
|
||||
repo: repo_entry::RepoEntry::new(repo_id),
|
||||
tags: tag_list::TagList::with_status("Tags are empty"),
|
||||
details: details::Details::new(),
|
||||
info: info::Info::new("could not find a docker-compose file"),
|
||||
};
|
||||
|
||||
// load tags if a repository was given thorugh paramter
|
||||
if opt.repo.is_none() {
|
||||
ui.tags = tag_list::TagList::with_repo_name(ui.repo.get());
|
||||
}
|
||||
|
||||
//setup tui
|
||||
let stdout = io::stdout().into_raw_mode()?;
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
//setup input thread
|
||||
let receiver = super::spawn_stdin_channel();
|
||||
|
||||
//core interaction loop
|
||||
'core: loop {
|
||||
//draw
|
||||
terminal.draw(|rect| {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(7),
|
||||
Constraint::Length(2),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(rect.size());
|
||||
|
||||
rect.render_widget(ui.repo.render(ui.state == State::EditRepo), chunks[0]);
|
||||
let (list, state) = ui.tags.render(ui.state == State::SelectTag);
|
||||
let more_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Min(15), Constraint::Length(28)].as_ref())
|
||||
.split(chunks[1]);
|
||||
rect.render_stateful_widget(list, more_chunks[0], state);
|
||||
rect.render_widget(ui.details.render(), more_chunks[1]);
|
||||
rect.render_widget(ui.info.render(), chunks[2]);
|
||||
})?;
|
||||
|
||||
//handle input
|
||||
match receiver.try_recv() {
|
||||
Ok(Key::Ctrl('q')) => break 'core,
|
||||
Ok(Key::Char('\t')) => {
|
||||
ui.state.next();
|
||||
ui.info.set_info(&ui.state);
|
||||
}
|
||||
Ok(Key::Ctrl('r')) => {
|
||||
ui.repo.confirm();
|
||||
ui.tags = tag_list::TagList::with_repo_name(ui.repo.get());
|
||||
}
|
||||
Ok(Key::Char('\n')) => match ui.state {
|
||||
State::EditRepo => {
|
||||
ui.repo.confirm();
|
||||
ui.tags = tag_list::TagList::with_repo_name(ui.repo.get());
|
||||
}
|
||||
State::SelectTag => ui.tags.handle_input(Key::Char('\n')),
|
||||
},
|
||||
Ok(Key::Char(key)) => match ui.state {
|
||||
State::EditRepo => {
|
||||
ui.info.set_text("Editing Repository");
|
||||
ui.repo.handle_input(Key::Char(key));
|
||||
}
|
||||
State::SelectTag => {
|
||||
ui.tags.handle_input(Key::Char(key));
|
||||
}
|
||||
},
|
||||
Ok(Key::Backspace) => match ui.state {
|
||||
State::EditRepo => {
|
||||
ui.info.set_text("Editing Repository");
|
||||
ui.repo.handle_input(Key::Backspace);
|
||||
}
|
||||
State::SelectTag => (),
|
||||
},
|
||||
Ok(Key::Up) => match ui.state {
|
||||
State::EditRepo => (),
|
||||
State::SelectTag => {
|
||||
ui.tags.handle_input(Key::Up);
|
||||
ui.details = ui.tags.create_detail_widget();
|
||||
}
|
||||
},
|
||||
Ok(Key::Down) => match ui.state {
|
||||
State::EditRepo => (),
|
||||
State::SelectTag => {
|
||||
ui.tags.handle_input(Key::Down);
|
||||
ui.details = ui.tags.create_detail_widget();
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
//sleep for 32ms (30 fps)
|
||||
thread::sleep(std::time::Duration::from_millis(32));
|
||||
}
|
||||
|
||||
terminal.clear()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
225
src/ui/no_yaml_found.rs
Normal file
225
src/ui/no_yaml_found.rs
Normal file
@ -0,0 +1,225 @@
|
||||
use anyhow::Result;
|
||||
use termion::event::Key;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::layout::{Constraint, Direction, Layout};
|
||||
use tui::Terminal;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::widget::async_tag_list;
|
||||
use crate::widget::info;
|
||||
use crate::widget::repo_entry;
|
||||
use crate::Opt;
|
||||
|
||||
pub struct Ui {
|
||||
state: State,
|
||||
repo: repo_entry::RepoEntry,
|
||||
tags: async_tag_list::TagList,
|
||||
details: crate::widget::details::Details,
|
||||
info: info::Info,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum State {
|
||||
EditRepo,
|
||||
SelectTag,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for State {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
State::EditRepo => write!(f, "Edit repository"),
|
||||
State::SelectTag => write!(f, "Select a tag"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::Iterator for State {
|
||||
type Item = Self;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
State::EditRepo => *self = State::SelectTag,
|
||||
State::SelectTag => *self = State::EditRepo,
|
||||
}
|
||||
Some(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub enum UiEvent {
|
||||
NewRepo(String),
|
||||
TagInput(termion::event::Key),
|
||||
Quit,
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
#[tokio::main]
|
||||
pub async fn work_requests(ui: Arc<Mutex<Ui>>, event: std::sync::mpsc::Receiver<UiEvent>) {
|
||||
loop {
|
||||
match event.recv() {
|
||||
Ok(UiEvent::Quit) => break,
|
||||
Ok(UiEvent::NewRepo(name)) => {
|
||||
let list = async_tag_list::TagList::with_repo_name(name).await;
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.tags = list;
|
||||
}
|
||||
Ok(UiEvent::TagInput(key)) => {
|
||||
let mut tags = {
|
||||
let ui_data = ui.lock().unwrap();
|
||||
ui_data.tags.clone()
|
||||
};
|
||||
tags.handle_input(key).await;
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.tags = tags;
|
||||
}
|
||||
Err(e) => {
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.info.set_info(&e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(opt: &Opt) -> Result<()> {
|
||||
let repo_id = opt.repo.as_deref();
|
||||
|
||||
let ui = Arc::new(Mutex::new(Ui {
|
||||
state: State::EditRepo,
|
||||
repo: repo_entry::RepoEntry::new(repo_id),
|
||||
tags: async_tag_list::TagList::with_status("no tags"),
|
||||
details: crate::widget::details::Details::new(),
|
||||
info: info::Info::new("Select image or edit Repository"),
|
||||
}));
|
||||
|
||||
// spawn new thread that fetches information async
|
||||
let (sender, receiver) = std::sync::mpsc::channel();
|
||||
let ui_clone = ui.clone();
|
||||
std::thread::spawn(move || {
|
||||
Self::work_requests(ui_clone, receiver);
|
||||
});
|
||||
|
||||
//setup tui
|
||||
let stdout = io::stdout().into_raw_mode()?;
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
//setup input thread
|
||||
let receiver = super::spawn_stdin_channel();
|
||||
|
||||
//core interaction loop
|
||||
'core: loop {
|
||||
let mut ui_data = ui.lock().unwrap();
|
||||
//draw
|
||||
terminal.draw(|rect| {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(7),
|
||||
Constraint::Length(2),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(rect.size());
|
||||
rect.render_widget(
|
||||
ui_data.repo.render(ui_data.state == State::EditRepo),
|
||||
chunks[0],
|
||||
);
|
||||
let render_state = ui_data.state == State::SelectTag;
|
||||
let (tags, state) = ui_data.tags.render(render_state);
|
||||
let more_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Min(15), Constraint::Length(28)].as_ref())
|
||||
.split(chunks[1]);
|
||||
rect.render_stateful_widget(tags, more_chunks[0], state);
|
||||
rect.render_widget(ui_data.details.render(), more_chunks[1]);
|
||||
rect.render_widget(ui_data.info.render(), chunks[2]);
|
||||
})?;
|
||||
|
||||
//handle input
|
||||
match receiver.try_recv() {
|
||||
Ok(Key::Ctrl('q')) => {
|
||||
sender.send(UiEvent::Quit)?;
|
||||
break 'core; //quit program without saving
|
||||
}
|
||||
Ok(Key::Char('\t')) => {
|
||||
ui_data.state.next();
|
||||
let state = ui_data.state.clone();
|
||||
ui_data.info.set_info(&state);
|
||||
}
|
||||
Ok(Key::Ctrl('r')) => {
|
||||
ui_data.repo.confirm();
|
||||
sender.send(UiEvent::NewRepo(ui_data.repo.get())).unwrap();
|
||||
}
|
||||
Ok(Key::Char('\n')) => match ui_data.state {
|
||||
State::EditRepo => {
|
||||
ui_data.repo.confirm();
|
||||
sender.send(UiEvent::NewRepo(ui_data.repo.get())).unwrap();
|
||||
}
|
||||
State::SelectTag => {} // {
|
||||
// let mut repo = ui_data.repo.get();
|
||||
// let tag = match ui_data.tags.get_selected() {
|
||||
// Err(async_tag_list::Error::NextPageSelected) => continue,
|
||||
// Err(e) => {
|
||||
// ui_data.info.set_info(&format!("{}", e));
|
||||
// continue;
|
||||
// }
|
||||
// Ok(tag) => tag,
|
||||
// };
|
||||
// repo.push(':');
|
||||
// repo.push_str(&tag);
|
||||
// ui_data.services.change_current_line(repo);
|
||||
// }
|
||||
},
|
||||
Ok(Key::Char(key)) => match ui_data.state {
|
||||
State::EditRepo => {
|
||||
ui_data.info.set_text("Editing Repository");
|
||||
ui_data.repo.handle_input(Key::Char(key));
|
||||
}
|
||||
State::SelectTag => {}
|
||||
},
|
||||
Ok(Key::Backspace) => match ui_data.state {
|
||||
State::EditRepo => {
|
||||
ui_data.info.set_text("Editing Repository");
|
||||
ui_data.repo.handle_input(Key::Backspace);
|
||||
}
|
||||
State::SelectTag => {}
|
||||
},
|
||||
Ok(Key::Up) => {
|
||||
let state = ui_data.state.clone();
|
||||
match state {
|
||||
State::EditRepo => {}
|
||||
State::SelectTag => {
|
||||
sender.send(UiEvent::TagInput(Key::Up)).unwrap();
|
||||
ui_data.details = ui_data.tags.create_detail_widget();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Key::Down) => {
|
||||
let state = ui_data.state.clone();
|
||||
match state {
|
||||
State::EditRepo => {}
|
||||
State::SelectTag => {
|
||||
sender.send(UiEvent::TagInput(Key::Down)).unwrap();
|
||||
ui_data.details = ui_data.tags.create_detail_widget();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// release lock of ui for other threads
|
||||
drop(ui_data);
|
||||
|
||||
//sleep for 32ms (30 fps)
|
||||
thread::sleep(std::time::Duration::from_millis(32));
|
||||
}
|
||||
|
||||
terminal.clear()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
285
src/ui/yaml_found.rs
Normal file
285
src/ui/yaml_found.rs
Normal file
@ -0,0 +1,285 @@
|
||||
use anyhow::Result;
|
||||
use termion::event::Key;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::layout::{Constraint, Direction, Layout};
|
||||
use tui::Terminal;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::repository;
|
||||
use crate::widget::async_tag_list;
|
||||
use crate::widget::info;
|
||||
use crate::widget::repo_entry;
|
||||
use crate::widget::service_switcher;
|
||||
use crate::Opt;
|
||||
|
||||
pub struct Ui {
|
||||
state: State,
|
||||
repo: repo_entry::RepoEntry,
|
||||
tags: async_tag_list::TagList,
|
||||
services: service_switcher::ServiceSwitcher,
|
||||
details: crate::widget::details::Details,
|
||||
info: info::Info,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum State {
|
||||
EditRepo,
|
||||
SelectTag,
|
||||
SelectService,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for State {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
State::EditRepo => write!(f, "Edit repository"),
|
||||
State::SelectTag => write!(f, "Select a tag"),
|
||||
State::SelectService => write!(f, "Select a image"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::Iterator for State {
|
||||
type Item = Self;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
State::EditRepo => *self = State::SelectTag,
|
||||
State::SelectTag => *self = State::SelectService,
|
||||
State::SelectService => *self = State::EditRepo,
|
||||
}
|
||||
Some(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub enum UiEvent {
|
||||
NewRepo(String),
|
||||
TagInput(termion::event::Key),
|
||||
Quit,
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
#[tokio::main]
|
||||
pub async fn work_requests(ui: Arc<Mutex<Ui>>, event: std::sync::mpsc::Receiver<UiEvent>) {
|
||||
loop {
|
||||
match event.recv() {
|
||||
Ok(UiEvent::Quit) => break,
|
||||
Ok(UiEvent::NewRepo(name)) => {
|
||||
let list = async_tag_list::TagList::with_repo_name(name).await;
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.tags = list;
|
||||
}
|
||||
Ok(UiEvent::TagInput(key)) => {
|
||||
let mut tags = {
|
||||
let ui_data = ui.lock().unwrap();
|
||||
ui_data.tags.clone()
|
||||
};
|
||||
tags.handle_input(key).await;
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.tags = tags;
|
||||
}
|
||||
Err(e) => {
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.info.set_info(&e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(opt: &Opt, switcher: service_switcher::ServiceSwitcher) -> Result<()> {
|
||||
let repo_id = opt.repo.as_deref();
|
||||
|
||||
let ui = Arc::new(Mutex::new(Ui {
|
||||
state: State::SelectService,
|
||||
repo: repo_entry::RepoEntry::new(repo_id),
|
||||
tags: async_tag_list::TagList::with_status("no tags"),
|
||||
services: switcher,
|
||||
details: crate::widget::details::Details::new(),
|
||||
info: info::Info::new("Select image or edit Repository"),
|
||||
}));
|
||||
|
||||
// spawn new thread that fetches information async
|
||||
let (sender, receiver) = std::sync::mpsc::channel();
|
||||
let ui_clone = ui.clone();
|
||||
std::thread::spawn(move || {
|
||||
Self::work_requests(ui_clone, receiver);
|
||||
});
|
||||
|
||||
//setup tui
|
||||
let stdout = io::stdout().into_raw_mode()?;
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
//setup input thread
|
||||
let receiver = super::spawn_stdin_channel();
|
||||
|
||||
//core interaction loop
|
||||
'core: loop {
|
||||
let mut ui_data = ui.lock().unwrap();
|
||||
//draw
|
||||
terminal.draw(|rect| {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(10),
|
||||
Constraint::Min(7),
|
||||
Constraint::Length(2),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(rect.size());
|
||||
|
||||
let render_state = ui_data.state == State::SelectService;
|
||||
let (file, state) = ui_data.services.render(render_state);
|
||||
rect.render_stateful_widget(file, chunks[0], state);
|
||||
let more_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Ratio(1, 3),
|
||||
Constraint::Ratio(1, 3),
|
||||
Constraint::Ratio(1, 3),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(chunks[1]);
|
||||
rect.render_widget(
|
||||
ui_data.repo.render(ui_data.state == State::EditRepo),
|
||||
more_chunks[0],
|
||||
);
|
||||
let render_state = ui_data.state == State::SelectTag;
|
||||
let (tags, state) = ui_data.tags.render(render_state);
|
||||
rect.render_stateful_widget(tags, more_chunks[1], state);
|
||||
rect.render_widget(ui_data.details.render(), more_chunks[2]);
|
||||
rect.render_widget(ui_data.info.render(), chunks[2]);
|
||||
})?;
|
||||
|
||||
//handle input
|
||||
match receiver.try_recv() {
|
||||
Ok(Key::Ctrl('q')) => {
|
||||
sender.send(UiEvent::Quit)?;
|
||||
break 'core; //quit program without saving
|
||||
}
|
||||
Ok(Key::Char('\t')) => {
|
||||
ui_data.state.next();
|
||||
let state = ui_data.state.clone();
|
||||
ui_data.info.set_info(&state);
|
||||
}
|
||||
Ok(Key::Ctrl('s')) => match ui_data.services.save() {
|
||||
Err(e) => {
|
||||
ui_data.info.set_info(&format!("{}", e));
|
||||
continue;
|
||||
}
|
||||
Ok(_) => ui_data.info.set_text("Saved compose file"),
|
||||
},
|
||||
Ok(Key::Ctrl('r')) => {
|
||||
ui_data.repo.confirm();
|
||||
sender.send(UiEvent::NewRepo(ui_data.repo.get())).unwrap();
|
||||
}
|
||||
Ok(Key::Char('\n')) => match ui_data.state {
|
||||
State::EditRepo => {
|
||||
ui_data.repo.confirm();
|
||||
sender.send(UiEvent::NewRepo(ui_data.repo.get())).unwrap();
|
||||
}
|
||||
State::SelectTag => {
|
||||
let mut repo = ui_data.repo.get();
|
||||
let tag = match ui_data.tags.get_selected() {
|
||||
Err(async_tag_list::Error::NextPageSelected) => continue,
|
||||
Err(e) => {
|
||||
ui_data.info.set_info(&format!("{}", e));
|
||||
continue;
|
||||
}
|
||||
Ok(tag) => tag,
|
||||
};
|
||||
repo.push(':');
|
||||
repo.push_str(&tag);
|
||||
ui_data.services.change_current_line(repo);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Ok(Key::Char(key)) => match ui_data.state {
|
||||
State::SelectService => (),
|
||||
State::EditRepo => {
|
||||
ui_data.info.set_text("Editing Repository");
|
||||
ui_data.repo.handle_input(Key::Char(key));
|
||||
}
|
||||
State::SelectTag => (),
|
||||
},
|
||||
Ok(Key::Backspace) => match ui_data.state {
|
||||
State::SelectService => (),
|
||||
State::EditRepo => {
|
||||
ui_data.info.set_text("Editing Repository");
|
||||
ui_data.repo.handle_input(Key::Backspace);
|
||||
}
|
||||
State::SelectTag => (),
|
||||
},
|
||||
Ok(Key::Up) => {
|
||||
let state = ui_data.state.clone();
|
||||
match state {
|
||||
State::SelectService if ui_data.services.find_previous_match() => {
|
||||
match ui_data.services.extract_repo() {
|
||||
Err(e) => ui_data.info.set_info(&format!("{}", e)),
|
||||
Ok(s) => {
|
||||
let repo = match repository::check_repo(&s) {
|
||||
Err(e) => {
|
||||
ui_data.info.set_info(&format!("{}", e));
|
||||
continue;
|
||||
}
|
||||
Ok(s) => s,
|
||||
};
|
||||
ui_data.repo.set(repo.to_string());
|
||||
sender.send(UiEvent::NewRepo(ui_data.repo.get())).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
State::SelectService | State::EditRepo => (),
|
||||
State::SelectTag => {
|
||||
sender.send(UiEvent::TagInput(Key::Up)).unwrap();
|
||||
ui_data.details = ui_data.tags.create_detail_widget();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Key::Down) => {
|
||||
let state = ui_data.state.clone();
|
||||
match state {
|
||||
State::SelectService if ui_data.services.find_next_match() => {
|
||||
match ui_data.services.extract_repo() {
|
||||
Err(e) => ui_data.info.set_info(&format!("{}", e)),
|
||||
Ok(s) => {
|
||||
let repo = match repository::check_repo(&s) {
|
||||
Err(e) => {
|
||||
ui_data.info.set_info(&format!("{}", e));
|
||||
continue;
|
||||
}
|
||||
Ok(s) => s,
|
||||
};
|
||||
ui_data.repo.set(repo.to_string());
|
||||
sender.send(UiEvent::NewRepo(ui_data.repo.get())).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
State::SelectService => (),
|
||||
State::EditRepo => (),
|
||||
State::SelectTag => {
|
||||
sender.send(UiEvent::TagInput(Key::Down)).unwrap();
|
||||
ui_data.details = ui_data.tags.create_detail_widget();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
drop(ui_data);
|
||||
|
||||
//sleep for 32ms (30 fps)
|
||||
thread::sleep(std::time::Duration::from_millis(32));
|
||||
}
|
||||
|
||||
terminal.clear()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Line {
|
||||
Status(String),
|
||||
Image(repository::Tag),
|
||||
@ -38,6 +39,7 @@ impl fmt::Display for Line {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TagList {
|
||||
lines: Vec<Line>,
|
||||
state: ListState,
|
||||
@ -55,22 +57,22 @@ impl TagList {
|
||||
}
|
||||
|
||||
/// list the tags of the repository if the input is valid
|
||||
pub fn with_repo_name(repo: String) -> Self {
|
||||
match repository::Repo::new(&repo) {
|
||||
Ok(tags) => Self::with_tags(tags),
|
||||
pub async fn with_repo_name(repo: String) -> Self {
|
||||
match repository::Repo::new(&repo).await {
|
||||
Ok(tags) => Self::with_tags(tags).await,
|
||||
Err(_) => Self::with_status("input repo was not found"),
|
||||
}
|
||||
}
|
||||
|
||||
/// list the tags of the input
|
||||
fn with_tags(mut tags: repository::Repo) -> Self {
|
||||
async fn with_tags(mut tags: repository::Repo) -> Self {
|
||||
let mut lines: Vec<Line> = tags
|
||||
.get_tags()
|
||||
.iter()
|
||||
.map(|r| Line::Image(r.clone()))
|
||||
.collect();
|
||||
|
||||
match tags.next_page() {
|
||||
match tags.next_page().await {
|
||||
None => (),
|
||||
Some(new_tags) => {
|
||||
lines.push(Line::NextPage(String::from("load more tags")));
|
||||
@ -128,20 +130,20 @@ impl TagList {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_input(&mut self, key: termion::event::Key) {
|
||||
pub async fn handle_input(&mut self, key: termion::event::Key) {
|
||||
match key {
|
||||
Key::Down => self.next(),
|
||||
Key::Down => self.next().await,
|
||||
Key::Up => self.previous(),
|
||||
Key::Char('\n') => self.select(),
|
||||
Key::Char('\n') => self.select().await,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// loads new tags when matching line is selected
|
||||
fn select(&mut self) {
|
||||
async fn select(&mut self) {
|
||||
if let Some(i) = self.state.selected() {
|
||||
if let Line::NextPage(_) = &self.lines[i] {
|
||||
self.load_next_page()
|
||||
self.load_next_page().await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,18 +154,15 @@ impl TagList {
|
||||
Some(i) => match &self.lines[i] {
|
||||
Line::Status(_) => Err(Error::SelectedStatus),
|
||||
Line::Image(i) => Ok(i.get_name().to_string()),
|
||||
Line::NextPage(_) => {
|
||||
self.load_next_page();
|
||||
Err(Error::NextPageSelected)
|
||||
}
|
||||
Line::NextPage(_) => Err(Error::NextPageSelected),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// load new tags from the next page
|
||||
fn load_next_page(&mut self) {
|
||||
async fn load_next_page(&mut self) {
|
||||
match &self.tags {
|
||||
Some(tags) => match tags.next_page() {
|
||||
Some(tags) => match tags.next_page().await {
|
||||
None => (),
|
||||
Some(new_tags) => {
|
||||
//load new tags object
|
||||
@ -183,7 +182,7 @@ impl TagList {
|
||||
}
|
||||
|
||||
//readd next page
|
||||
match self.tags.as_ref().unwrap().next_page() {
|
||||
match self.tags.as_ref().unwrap().next_page().await {
|
||||
None => (),
|
||||
Some(_) => self.lines.push(next_page.unwrap()),
|
||||
}
|
||||
@ -194,21 +193,26 @@ impl TagList {
|
||||
}
|
||||
|
||||
/// select next tag
|
||||
fn next(&mut self) {
|
||||
async fn next(&mut self) {
|
||||
if let Some(Line::Status(_)) = self.lines.get(0) {
|
||||
return;
|
||||
}
|
||||
match self.state.selected() {
|
||||
None if !self.lines.is_empty() => self.state.select(Some(0)),
|
||||
None => (),
|
||||
Some(i) if i == self.lines.len() - 1 => self.state.select(Some(0)),
|
||||
Some(i) if i == self.lines.len() - 2 => self.load_next_page().await,
|
||||
Some(i) => self.state.select(Some(i + 1)),
|
||||
}
|
||||
}
|
||||
|
||||
/// select previous tag
|
||||
fn previous(&mut self) {
|
||||
if let Some(Line::Status(_)) = self.lines.get(0) {
|
||||
return;
|
||||
}
|
||||
match self.state.selected() {
|
||||
None if !self.lines.is_empty() => self.state.select(Some(self.lines.len())),
|
||||
None => (),
|
||||
Some(i) if i == 0 => self.state.select(Some(self.lines.len() - 1)),
|
||||
None => self.state.select(Some(0)),
|
||||
Some(i) if i == 0 => self.state.select(Some(self.lines.len() - 2)),
|
||||
Some(i) => self.state.select(Some(i - 1)),
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
pub mod async_tag_list;
|
||||
pub mod details;
|
||||
pub mod info;
|
||||
pub mod repo_entry;
|
||||
pub mod service_switcher;
|
||||
pub mod tag_list;
|
||||
|
@ -12,7 +12,7 @@ pub struct RepoEntry {
|
||||
|
||||
impl RepoEntry {
|
||||
pub fn new(text: Option<&str>) -> Self {
|
||||
let default_text = "enter a repository here or select one from file widget";
|
||||
let default_text = "edit me or select a repository";
|
||||
Self {
|
||||
text: String::from(text.unwrap_or(default_text)),
|
||||
old_text: String::from(text.unwrap_or(default_text)),
|
||||
|
Loading…
Reference in New Issue
Block a user