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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.1.19",
|
||||||
"libc",
|
"libc",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
@ -184,12 +184,6 @@ version = "0.3.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99"
|
checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-io"
|
|
||||||
version = "0.3.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.16"
|
version = "0.3.16"
|
||||||
@ -210,12 +204,9 @@ checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -226,7 +217,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -272,6 +263,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@ -393,9 +393,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.99"
|
version = "0.2.139"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
|
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
@ -426,24 +426,14 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.7.13"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
|
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"miow",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"ntapi",
|
"windows-sys",
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miow"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -464,15 +454,6 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ntapi"
|
|
||||||
version = "0.3.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
@ -494,11 +475,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.13.0"
|
version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.2.6",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -691,6 +672,7 @@ dependencies = [
|
|||||||
"structopt",
|
"structopt",
|
||||||
"termion",
|
"termion",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"tui",
|
"tui",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -845,9 +827,9 @@ checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4.1"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad"
|
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"winapi",
|
"winapi",
|
||||||
@ -956,7 +938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -977,9 +959,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.10.0"
|
version = "1.24.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b"
|
checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -988,7 +970,20 @@ dependencies = [
|
|||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"pin-project-lite",
|
"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]]
|
[[package]]
|
||||||
@ -1139,6 +1134,12 @@ version = "0.10.0+wasi-snapshot-preview1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
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]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.75"
|
version = "0.2.75"
|
||||||
@ -1239,6 +1240,63 @@ 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 = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
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]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
@ -9,7 +9,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.127", features = ["derive"] }
|
serde = { version = "1.0.127", features = ["derive"] }
|
||||||
serde_json = "1.0.66"
|
serde_json = "1.0.66"
|
||||||
reqwest = { version = "0.11.4", features = ["blocking", "json"] }
|
reqwest = { version = "0.11.4", features = ["json"] }
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
tui = "0.16"
|
tui = "0.16"
|
||||||
termion = "1.5"
|
termion = "1.5"
|
||||||
@ -18,6 +18,7 @@ lazy_static = "1.4.0"
|
|||||||
structopt = "0.3.23"
|
structopt = "0.3.23"
|
||||||
thiserror = "1.0.32"
|
thiserror = "1.0.32"
|
||||||
anyhow = "1.0.59"
|
anyhow = "1.0.59"
|
||||||
|
tokio = { version = "1.24.2", features = ["macros", "rt-multi-thread"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "yes"
|
lto = "yes"
|
||||||
|
@ -46,17 +46,17 @@ pub struct DockerHub {
|
|||||||
|
|
||||||
impl 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
|
/// 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);
|
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
|
/// fetches tag information from a url
|
||||||
pub fn with_url(url: &str) -> Result<super::Repo, Error> {
|
pub async fn with_url(url: &str) -> Result<super::Repo, Error> {
|
||||||
let response = reqwest::blocking::get(url)?;
|
let response = reqwest::get(url).await?;
|
||||||
|
|
||||||
//convert it to json
|
//convert it to json
|
||||||
let tags = response.json::<Self>()?;
|
let tags = response.json::<Self>().await?;
|
||||||
if tags.results.is_empty() {
|
if tags.results.is_empty() {
|
||||||
return Err(Error::NoTagsFound);
|
return Err(Error::NoTagsFound);
|
||||||
}
|
}
|
||||||
|
@ -46,13 +46,14 @@ impl Tag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Repo {
|
pub struct Repo {
|
||||||
tags: Vec<Tag>,
|
tags: Vec<Tag>,
|
||||||
next_page: Option<String>,
|
next_page: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repo {
|
impl Repo {
|
||||||
pub fn new(repo: &str) -> Result<Self, Error> {
|
pub async fn new(repo: &str) -> Result<Self, Error> {
|
||||||
use crate::repo::Repo;
|
use crate::repo::Repo;
|
||||||
let (registry, repo) = match crate::repo::split_repo_without_tag(repo) {
|
let (registry, repo) = match crate::repo::split_repo_without_tag(repo) {
|
||||||
Ok(Repo::WithServer(reg, org, pro)) => (Some(reg), format!("{}/{}", org, pro)),
|
Ok(Repo::WithServer(reg, org, pro)) => (Some(reg), format!("{}/{}", org, pro)),
|
||||||
@ -62,7 +63,7 @@ impl Repo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if registry.unwrap_or_default().is_empty() {
|
if registry.unwrap_or_default().is_empty() {
|
||||||
dockerhub::DockerHub::create_repo(&repo)
|
dockerhub::DockerHub::create_repo(&repo).await
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Converting(
|
Err(Error::Converting(
|
||||||
"This registry is not supported yet".into(),
|
"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
|
//TODO fix for other registries
|
||||||
dockerhub::DockerHub::with_url(url)
|
dockerhub::DockerHub::with_url(url).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tags(&self) -> &Vec<Tag> {
|
pub fn get_tags(&self) -> &Vec<Tag> {
|
||||||
&self.tags
|
&self.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_page(&self) -> Option<Self> {
|
pub async fn next_page(&self) -> Option<Self> {
|
||||||
if let Some(url) = &self.next_page {
|
if let Some(url) = &self.next_page {
|
||||||
match Self::with_url(url) {
|
match Self::with_url(url).await {
|
||||||
Ok(tags) => return Some(tags),
|
Ok(tags) => return Some(tags),
|
||||||
Err(e) => println!("Encountered error: {e}"),
|
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_found;
|
||||||
mod no_yaml;
|
mod yaml_found;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
@ -13,8 +13,8 @@ use std::{io, thread};
|
|||||||
pub fn create_ui(opt: &Opt) -> Result<()> {
|
pub fn create_ui(opt: &Opt) -> Result<()> {
|
||||||
let service_result = service_switcher::ServiceSwitcher::new(&opt.file);
|
let service_result = service_switcher::ServiceSwitcher::new(&opt.file);
|
||||||
match service_result {
|
match service_result {
|
||||||
None => no_yaml::NoYaml::run(opt),
|
Some(switcher) => yaml_found::Ui::run(opt, switcher),
|
||||||
Some(switcher) => default::Ui::run(opt, switcher),
|
_ => no_yaml_found::Ui::run(opt),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
Ok(())
|
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 {
|
enum Line {
|
||||||
Status(String),
|
Status(String),
|
||||||
Image(repository::Tag),
|
Image(repository::Tag),
|
||||||
@ -38,6 +39,7 @@ impl fmt::Display for Line {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct TagList {
|
pub struct TagList {
|
||||||
lines: Vec<Line>,
|
lines: Vec<Line>,
|
||||||
state: ListState,
|
state: ListState,
|
||||||
@ -55,22 +57,22 @@ impl TagList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// list the tags of the repository if the input is valid
|
/// list the tags of the repository if the input is valid
|
||||||
pub fn with_repo_name(repo: String) -> Self {
|
pub async fn with_repo_name(repo: String) -> Self {
|
||||||
match repository::Repo::new(&repo) {
|
match repository::Repo::new(&repo).await {
|
||||||
Ok(tags) => Self::with_tags(tags),
|
Ok(tags) => Self::with_tags(tags).await,
|
||||||
Err(_) => Self::with_status("input repo was not found"),
|
Err(_) => Self::with_status("input repo was not found"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// list the tags of the input
|
/// 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
|
let mut lines: Vec<Line> = tags
|
||||||
.get_tags()
|
.get_tags()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| Line::Image(r.clone()))
|
.map(|r| Line::Image(r.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
match tags.next_page() {
|
match tags.next_page().await {
|
||||||
None => (),
|
None => (),
|
||||||
Some(new_tags) => {
|
Some(new_tags) => {
|
||||||
lines.push(Line::NextPage(String::from("load more 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 {
|
match key {
|
||||||
Key::Down => self.next(),
|
Key::Down => self.next().await,
|
||||||
Key::Up => self.previous(),
|
Key::Up => self.previous(),
|
||||||
Key::Char('\n') => self.select(),
|
Key::Char('\n') => self.select().await,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// loads new tags when matching line is selected
|
/// 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 Some(i) = self.state.selected() {
|
||||||
if let Line::NextPage(_) = &self.lines[i] {
|
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] {
|
Some(i) => match &self.lines[i] {
|
||||||
Line::Status(_) => Err(Error::SelectedStatus),
|
Line::Status(_) => Err(Error::SelectedStatus),
|
||||||
Line::Image(i) => Ok(i.get_name().to_string()),
|
Line::Image(i) => Ok(i.get_name().to_string()),
|
||||||
Line::NextPage(_) => {
|
Line::NextPage(_) => Err(Error::NextPageSelected),
|
||||||
self.load_next_page();
|
|
||||||
Err(Error::NextPageSelected)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// load new tags from the next page
|
/// load new tags from the next page
|
||||||
fn load_next_page(&mut self) {
|
async fn load_next_page(&mut self) {
|
||||||
match &self.tags {
|
match &self.tags {
|
||||||
Some(tags) => match tags.next_page() {
|
Some(tags) => match tags.next_page().await {
|
||||||
None => (),
|
None => (),
|
||||||
Some(new_tags) => {
|
Some(new_tags) => {
|
||||||
//load new tags object
|
//load new tags object
|
||||||
@ -183,7 +182,7 @@ impl TagList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//readd next page
|
//readd next page
|
||||||
match self.tags.as_ref().unwrap().next_page() {
|
match self.tags.as_ref().unwrap().next_page().await {
|
||||||
None => (),
|
None => (),
|
||||||
Some(_) => self.lines.push(next_page.unwrap()),
|
Some(_) => self.lines.push(next_page.unwrap()),
|
||||||
}
|
}
|
||||||
@ -194,21 +193,26 @@ impl TagList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// select next tag
|
/// 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() {
|
match self.state.selected() {
|
||||||
None if !self.lines.is_empty() => self.state.select(Some(0)),
|
None if !self.lines.is_empty() => self.state.select(Some(0)),
|
||||||
None => (),
|
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)),
|
Some(i) => self.state.select(Some(i + 1)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// select previous tag
|
/// select previous tag
|
||||||
fn previous(&mut self) {
|
fn previous(&mut self) {
|
||||||
|
if let Some(Line::Status(_)) = self.lines.get(0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
match self.state.selected() {
|
match self.state.selected() {
|
||||||
None if !self.lines.is_empty() => self.state.select(Some(self.lines.len())),
|
None => self.state.select(Some(0)),
|
||||||
None => (),
|
Some(i) if i == 0 => self.state.select(Some(self.lines.len() - 2)),
|
||||||
Some(i) if i == 0 => self.state.select(Some(self.lines.len() - 1)),
|
|
||||||
Some(i) => self.state.select(Some(i - 1)),
|
Some(i) => self.state.select(Some(i - 1)),
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
|
pub mod async_tag_list;
|
||||||
pub mod details;
|
pub mod details;
|
||||||
pub mod info;
|
pub mod info;
|
||||||
pub mod repo_entry;
|
pub mod repo_entry;
|
||||||
pub mod service_switcher;
|
pub mod service_switcher;
|
||||||
pub mod tag_list;
|
|
||||||
|
@ -12,7 +12,7 @@ pub struct RepoEntry {
|
|||||||
|
|
||||||
impl RepoEntry {
|
impl RepoEntry {
|
||||||
pub fn new(text: Option<&str>) -> Self {
|
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 {
|
Self {
|
||||||
text: String::from(text.unwrap_or(default_text)),
|
text: String::from(text.unwrap_or(default_text)),
|
||||||
old_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