Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
eaae59749d | ||
|
7e1ee0f699 | ||
|
dee25f911b | ||
|
445b21975a | ||
|
70b163cf81 | ||
|
431b062420 | ||
|
bd829f23ad | ||
|
28ae313797 | ||
|
1c72d4cd24 | ||
|
017bafa19b | ||
|
646e48a208 | ||
|
1f2a098c26 | ||
|
f4132d4125 | ||
|
7fb519e70b | ||
|
70cf17f1c7 | ||
|
935a9e5709 | ||
|
a517c7d6d0 | ||
|
308fa8dbb8 | ||
|
cd0479a618 | ||
dad62ebd98 | |||
|
5e67446123 | ||
|
87039541a5 | ||
|
c98558c41d | ||
|
8ab2690d17 | ||
|
8363896052 | ||
|
22429ac74b |
@ -20,7 +20,7 @@ pipeline:
|
||||
# http://plugins.drone.io/drone-plugins/drone-github-release/
|
||||
image: plugins/github-release
|
||||
files: target/release/reel-moby
|
||||
secrets: [github_release_api_key]
|
||||
secrets: [github_api_key]
|
||||
when:
|
||||
event: tag
|
||||
tag: v*
|
||||
|
524
Cargo.lock
generated
524
Cargo.lock
generated
@ -20,22 +20,28 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c91f1f46651137be86f3a2b9a8359f9ab421d04d941c62b5982e1ca21113adf9"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
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"
|
||||
@ -107,22 +113,6 @@ dependencies = [
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.28"
|
||||
@ -138,21 +128,6 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.1"
|
||||
@ -165,62 +140,54 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.16"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.16"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.16"
|
||||
name = "futures-macro"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
name = "futures-sink"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"futures-core",
|
||||
"futures-macro",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -266,6 +233,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"
|
||||
@ -325,16 +301,18 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
name = "hyper-rustls"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"log",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -387,9 +365,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"
|
||||
@ -420,51 +398,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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -488,11 +429,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",
|
||||
]
|
||||
|
||||
@ -508,39 +449,6 @@ version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "549430950c79ae24e6d02e0b7404534ecf311d94cc9f861e9e4020187d13d885"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a7907e3bfa08bb85105209cdfcb6c63d109f8f6c1ed6ca318fff5c1853fbc1d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@ -559,18 +467,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
@ -613,46 +509,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
@ -675,6 +531,7 @@ dependencies = [
|
||||
name = "reel-moby"
|
||||
version = "1.2.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
"regex",
|
||||
@ -683,6 +540,8 @@ dependencies = [
|
||||
"serde_json",
|
||||
"structopt",
|
||||
"termion",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tui",
|
||||
]
|
||||
|
||||
@ -703,15 +562,6 @@ version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.4"
|
||||
@ -726,27 +576,56 @@ dependencies = [
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"hyper-rustls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
@ -754,36 +633,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.19"
|
||||
name = "sct"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
|
||||
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -837,14 +693,20 @@ 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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
@ -886,20 +748,6 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rand",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.6"
|
||||
@ -921,6 +769,26 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
@ -928,7 +796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@ -949,9 +817,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",
|
||||
@ -960,17 +828,31 @@ dependencies = [
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"winapi",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.0"
|
||||
name = "tokio-macros"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1065,6 +947,12 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.2"
|
||||
@ -1077,12 +965,6 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
@ -1111,6 +993,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"
|
||||
@ -1189,6 +1077,25 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -1211,6 +1118,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,14 +9,18 @@ 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", default-features = false, features = ["json", "rustls-tls"] }
|
||||
chrono = "0.4.19"
|
||||
tui = "0.16"
|
||||
termion = "1.5"
|
||||
regex = "1.5.4"
|
||||
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"
|
||||
opt-level = "z"
|
||||
opt-level = "z"
|
||||
strip="debuginfo"
|
||||
|
32
src/error.rs
Normal file
32
src/error.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// no valid tag found
|
||||
#[error("Expected a tag")]
|
||||
NoTagFound,
|
||||
|
||||
/// input that can't be interpreted
|
||||
#[error("Unexpected input")]
|
||||
MisformedInput,
|
||||
|
||||
/// a serde error
|
||||
#[error("Converting error: {0}")]
|
||||
Converting(String),
|
||||
|
||||
/// invalid repos show a valid json with 0 tags
|
||||
#[error("Given Repo does not exists or has 0 tags.")]
|
||||
NoTagsFound,
|
||||
|
||||
/// converting serde error
|
||||
#[error("Serde error: {0}")]
|
||||
Serde(#[from] serde_json::Error),
|
||||
|
||||
/// error while handling requests
|
||||
#[error("reqwest error: {0}")]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
|
||||
/// error while sending to channel
|
||||
#[error("sending to channel error: {0}")]
|
||||
ChannelSendError(#[from] std::sync::mpsc::SendError<crate::ui::UiEvent>),
|
||||
}
|
14
src/main.rs
14
src/main.rs
@ -1,12 +1,14 @@
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
mod common;
|
||||
mod error;
|
||||
mod repo;
|
||||
mod repository;
|
||||
mod ui;
|
||||
mod widget;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
/// helps you searching or updating tags of your used docker images
|
||||
#[derive(StructOpt, Debug)]
|
||||
pub struct Opt {
|
||||
@ -19,8 +21,10 @@ pub struct Opt {
|
||||
repo: Option<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<()> {
|
||||
//parse parameter
|
||||
let opt = Opt::from_args();
|
||||
ui::create_ui(&opt);
|
||||
ui::create_ui(&opt)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
106
src/repo.rs
106
src/repo.rs
@ -1,21 +1,6 @@
|
||||
use std::fmt;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
NoTagFound,
|
||||
MisformedInput,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::NoTagFound => write!(f, "Expected a tag"),
|
||||
Error::MisformedInput => write!(f, "Unexpected input"),
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::error::Error;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Repo {
|
||||
@ -102,80 +87,89 @@ mod tests {
|
||||
use crate::repo::{Error, Repo};
|
||||
|
||||
#[test]
|
||||
fn test_split_repo_without_tag() {
|
||||
let input: Vec<(&str, Result<Repo, Error>)> = vec![
|
||||
("", Err(Error::MisformedInput)),
|
||||
("NGINX", Err(Error::MisformedInput)),
|
||||
("nginx", Ok(Repo::Project("nginx".into()))),
|
||||
fn test_split_repo_without_tag_error() {
|
||||
let input: Vec<&str> = vec!["", "NGINX"];
|
||||
for i in input {
|
||||
assert!(super::split_repo_without_tag(i).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_repo_without_tag() -> Result<(), Error> {
|
||||
let input: Vec<(&str, Repo)> = vec![
|
||||
("nginx", Repo::Project("nginx".into())),
|
||||
(
|
||||
"library/nginx",
|
||||
Ok(Repo::WithOrga("library".into(), "nginx".into())),
|
||||
Repo::WithOrga("library".into(), "nginx".into()),
|
||||
),
|
||||
(
|
||||
"ghcr.io/library/nginx",
|
||||
Ok(Repo::WithServer(
|
||||
"ghcr.io".into(),
|
||||
"library".into(),
|
||||
"nginx".into(),
|
||||
)),
|
||||
Repo::WithServer("ghcr.io".into(), "library".into(), "nginx".into()),
|
||||
),
|
||||
(
|
||||
"te-st/test-hypen",
|
||||
Ok(Repo::WithOrga("te-st".into(), "test-hypen".into())),
|
||||
Repo::WithOrga("te-st".into(), "test-hypen".into()),
|
||||
),
|
||||
(
|
||||
"test/test.dot",
|
||||
Ok(Repo::WithOrga("test".into(), "test.dot".into())),
|
||||
Repo::WithOrga("test".into(), "test.dot".into()),
|
||||
),
|
||||
];
|
||||
|
||||
for i in input {
|
||||
assert_eq!(super::split_repo_without_tag(i.0), i.1);
|
||||
assert_eq!(super::split_repo_without_tag(i.0)?, i.1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_yaml_image_error() {
|
||||
let input: Vec<&str> = vec!["", "version: '2'", "image: ", " image: "];
|
||||
for i in input {
|
||||
assert!(super::match_yaml_image(i).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_yaml_image() {
|
||||
let input: Vec<(&str, Result<(&str, &str), Error>)> = vec![
|
||||
("", Err(Error::NoTagFound)),
|
||||
("version: '2'", Err(Error::NoTagFound)),
|
||||
("image: ", Err(Error::NoTagFound)),
|
||||
(" image: ", Err(Error::NoTagFound)),
|
||||
(" image: nginx", Ok((" image: ", "nginx"))),
|
||||
(" image: library/nginx", Ok((" image: ", "library/nginx"))),
|
||||
fn test_match_yaml_image() -> Result<(), Error> {
|
||||
let input: Vec<(&str, (&str, &str))> = vec![
|
||||
(" image: nginx", (" image: ", "nginx")),
|
||||
(" image: library/nginx", (" image: ", "library/nginx")),
|
||||
(
|
||||
" image: gchr.io/library/nginx",
|
||||
Ok((" image: ", "gchr.io/library/nginx")),
|
||||
" image: ghcr.io/library/nginx",
|
||||
(" image: ", "ghcr.io/library/nginx"),
|
||||
),
|
||||
(" image: nginx # comment", Ok((" image: ", "nginx"))),
|
||||
(" image: test-hyphen", Ok((" image: ", "test-hyphen"))),
|
||||
(" image: test.dot", Ok((" image: ", "test.dot"))),
|
||||
(" image: nginx # comment", (" image: ", "nginx")),
|
||||
(" image: test-hyphen", (" image: ", "test-hyphen")),
|
||||
(" image: test.dot", (" image: ", "test.dot")),
|
||||
];
|
||||
|
||||
for i in input {
|
||||
assert_eq!(super::match_yaml_image(i.0), i.1);
|
||||
assert_eq!(super::match_yaml_image(i.0)?, i.1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_tag_from_repo() {
|
||||
let input: Vec<(&str, Result<(&str, &str), super::Error>)> = vec![
|
||||
("nginx", Ok(("nginx", ""))),
|
||||
("library/nginx", Ok(("library/nginx", ""))),
|
||||
("ghcr.io/library/nginx", Ok(("ghcr.io/library/nginx", ""))),
|
||||
("nginx:", Ok(("nginx", ""))),
|
||||
("nginx:1", Ok(("nginx", "1"))),
|
||||
("nginx:latest", Ok(("nginx", "latest"))),
|
||||
("hy-phen:latest", Ok(("hy-phen", "latest"))),
|
||||
("test.dot:latest", Ok(("test.dot", "latest"))),
|
||||
fn test_split_tag_from_repo() -> Result<(), Error> {
|
||||
let input: Vec<(&str, (&str, &str))> = vec![
|
||||
("nginx", ("nginx", "")),
|
||||
("library/nginx", ("library/nginx", "")),
|
||||
("ghcr.io/library/nginx", ("ghcr.io/library/nginx", "")),
|
||||
("nginx:", ("nginx", "")),
|
||||
("nginx:1", ("nginx", "1")),
|
||||
("nginx:latest", ("nginx", "latest")),
|
||||
("hy-phen:latest", ("hy-phen", "latest")),
|
||||
("test.dot:latest", ("test.dot", "latest")),
|
||||
(
|
||||
"woodpeckerci/woodpecker-server",
|
||||
Ok(("woodpeckerci/woodpecker-server", "")),
|
||||
("woodpeckerci/woodpecker-server", ""),
|
||||
),
|
||||
];
|
||||
|
||||
for i in input {
|
||||
assert_eq!(super::split_tag_from_repo(i.0), i.1);
|
||||
assert_eq!(super::split_tag_from_repo(i.0)?, i.1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::repository::Error;
|
||||
use crate::error::Error;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
struct ImageDetails {
|
||||
@ -19,11 +19,11 @@ pub struct Images {
|
||||
}
|
||||
|
||||
impl Images {
|
||||
pub fn convert(&self) -> super::Tag {
|
||||
pub fn from_tag(images: &Self) -> super::Tag {
|
||||
super::Tag {
|
||||
name: self.tag_name.clone(),
|
||||
last_updated: Some(self.last_updated.clone()),
|
||||
details: self
|
||||
name: images.tag_name.clone(),
|
||||
last_updated: Some(images.last_updated.clone()),
|
||||
details: images
|
||||
.images
|
||||
.iter()
|
||||
.map(|d| super::TagDetails {
|
||||
@ -46,30 +46,23 @@ 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 = match reqwest::blocking::get(url) {
|
||||
Ok(result) => result,
|
||||
Err(e) => return Err(Error::Fetching(format!("reqwest error: {}", e))),
|
||||
};
|
||||
pub async fn with_url(url: &str) -> Result<super::Repo, Error> {
|
||||
let response = reqwest::get(url).await?;
|
||||
|
||||
//convert it to json
|
||||
let tags = match response.json::<Self>() {
|
||||
Ok(result) => result,
|
||||
Err(e) => return Err(Error::Converting(format!("invalid json: {}", e))),
|
||||
};
|
||||
|
||||
let tags = response.json::<Self>().await?;
|
||||
if tags.results.is_empty() {
|
||||
return Err(Error::NoTagsFound);
|
||||
}
|
||||
|
||||
Ok(super::Repo {
|
||||
tags: tags.results.iter().map(|t| t.convert()).collect(),
|
||||
tags: tags.results.iter().map(Images::from_tag).collect(),
|
||||
next_page: tags.next_page,
|
||||
})
|
||||
}
|
||||
|
@ -1,32 +1,11 @@
|
||||
mod dockerhub;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use chrono::DateTime;
|
||||
|
||||
use crate::common::display_duration_ext::DisplayDurationExt;
|
||||
use crate::error::Error;
|
||||
use crate::repo;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
/// couldn't fetch json with reqwest
|
||||
Fetching(String),
|
||||
/// a serde error
|
||||
Converting(String),
|
||||
/// invalid repos show a valid json with 0 tags
|
||||
NoTagsFound,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::Fetching(s) => write!(f, "Fetching error: {}", s),
|
||||
Error::Converting(s) => write!(f, "Converting error: {}", s),
|
||||
Error::NoTagsFound => write!(f, "Given Repo has 0 tags. Is it valid?"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct TagDetails {
|
||||
pub arch: Option<String>,
|
||||
@ -67,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)),
|
||||
@ -83,42 +63,41 @@ impl Repo {
|
||||
};
|
||||
|
||||
if registry.unwrap_or_default().is_empty() {
|
||||
dockerhub::DockerHub::create_repo(&repo)
|
||||
dockerhub::DockerHub::create_repo(&repo).await
|
||||
} else {
|
||||
return Err(Error::Converting("This registry is not supported".into()));
|
||||
Err(Error::Converting(
|
||||
"This registry is not supported yet".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
match &self.next_page {
|
||||
Some(url) => match Self::with_url(url) {
|
||||
Ok(tags) => Some(tags),
|
||||
Err(_) => None,
|
||||
},
|
||||
None => None,
|
||||
pub async fn next_page(&self) -> Option<Self> {
|
||||
if let Some(url) = &self.next_page {
|
||||
match Self::with_url(url).await {
|
||||
Ok(tags) => return Some(tags),
|
||||
Err(e) => println!("Encountered error: {e}"),
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// checks the repo name and may add a prefix for official images
|
||||
pub fn check_repo(name: &str) -> Result<String, Error> {
|
||||
let repo = match repo::split_tag_from_repo(name) {
|
||||
Err(e) => return Err(Error::Converting(format!("{}", e))),
|
||||
Ok((name, _)) => name,
|
||||
};
|
||||
let repo = repo::split_tag_from_repo(name)?;
|
||||
|
||||
match repo::split_repo_without_tag(name) {
|
||||
Ok(repo::Repo::Project(s)) => Ok(format!("library/{}", s)),
|
||||
Ok(_) => Ok(repo.to_string()),
|
||||
Ok(_) => Ok(repo.0.to_string()),
|
||||
Err(e) => Err(Error::Converting(format!("{}", e))),
|
||||
}
|
||||
}
|
||||
|
@ -1,224 +0,0 @@
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::Opt;
|
||||
use termion::event::Key;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::layout::{Constraint, Direction, Layout};
|
||||
use tui::Terminal;
|
||||
|
||||
use crate::repository;
|
||||
use crate::widget::info;
|
||||
use crate::widget::repo_entry;
|
||||
use crate::widget::service_switcher;
|
||||
use crate::widget::tag_list;
|
||||
|
||||
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) {
|
||||
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: service_switcher::ServiceSwitcher::new(&opt.file).unwrap(),
|
||||
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().unwrap();
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend).unwrap();
|
||||
|
||||
//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]);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
//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().unwrap();
|
||||
}
|
||||
}
|
@ -1,32 +1,22 @@
|
||||
mod default;
|
||||
mod no_yaml;
|
||||
mod no_yaml_found;
|
||||
mod yaml_found;
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::Opt;
|
||||
use termion::input::TermRead;
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::widget::service_switcher;
|
||||
use crate::Opt;
|
||||
|
||||
pub fn create_ui(opt: &Opt) {
|
||||
pub enum UiEvent {
|
||||
Input(termion::event::Key),
|
||||
RefreshOnNewData,
|
||||
}
|
||||
|
||||
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(_) => default::Ui::run(opt),
|
||||
}
|
||||
}
|
||||
Some(switcher) => yaml_found::Ui::run(opt, switcher),
|
||||
_ => no_yaml_found::Ui::run(opt),
|
||||
}?;
|
||||
|
||||
/// create a thread for catching input and send them to core loop
|
||||
pub fn spawn_stdin_channel() -> mpsc::Receiver<termion::event::Key> {
|
||||
let (tx, rx) = mpsc::channel::<termion::event::Key>();
|
||||
|
||||
thread::spawn(move || loop {
|
||||
let stdin = io::stdin();
|
||||
for c in stdin.keys() {
|
||||
tx.send(c.unwrap()).unwrap();
|
||||
}
|
||||
});
|
||||
thread::sleep(std::time::Duration::from_millis(64));
|
||||
rx
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
use std::{io, thread};
|
||||
|
||||
use termion::event::Key;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::layout::{Constraint, Direction, Layout};
|
||||
use tui::Terminal;
|
||||
|
||||
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) {
|
||||
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().unwrap();
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend).unwrap();
|
||||
|
||||
//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]);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
//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().unwrap();
|
||||
}
|
||||
}
|
263
src/ui/no_yaml_found.rs
Normal file
263
src/ui/no_yaml_found.rs
Normal file
@ -0,0 +1,263 @@
|
||||
use anyhow::Result;
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::layout::{Constraint, Direction, Layout};
|
||||
use tui::Terminal;
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::UiEvent;
|
||||
use crate::error::Error;
|
||||
use crate::widget::async_tag_list::{self, TagList};
|
||||
use crate::widget::{info, 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 DeferredEvent {
|
||||
Quit,
|
||||
NewRepo(String),
|
||||
LoadMoreTags,
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
/// catch input and send them to core loop
|
||||
pub fn wait_for_input(sender: mpsc::Sender<UiEvent>) -> Result<(), Error> {
|
||||
let stdin = std::io::stdin();
|
||||
for c in stdin.keys() {
|
||||
sender.send(UiEvent::Input(c.unwrap()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn work_requests(
|
||||
ui: &Arc<Mutex<Ui>>,
|
||||
events: mpsc::Receiver<DeferredEvent>,
|
||||
sender: mpsc::Sender<UiEvent>,
|
||||
) -> Result<(), Error> {
|
||||
use std::sync::atomic::Ordering;
|
||||
let fetching_tags = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
loop {
|
||||
match events.recv() {
|
||||
Ok(DeferredEvent::Quit) => break,
|
||||
Ok(DeferredEvent::NewRepo(name)) => {
|
||||
{
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.tags = TagList::with_status("fetching new tags...");
|
||||
sender.send(UiEvent::RefreshOnNewData)?;
|
||||
}
|
||||
let list = async_tag_list::TagList::with_repo_name(name).await;
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.tags = list;
|
||||
}
|
||||
Ok(DeferredEvent::LoadMoreTags) if !fetching_tags.load(Ordering::Relaxed) => {
|
||||
fetching_tags.store(true, Ordering::Relaxed);
|
||||
let mut tags_copy = {
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.info.set_text("Fetching more tags...");
|
||||
sender.send(UiEvent::RefreshOnNewData)?;
|
||||
ui.tags.clone()
|
||||
};
|
||||
|
||||
let sender_copy = sender.clone();
|
||||
let ui_copy = ui.clone();
|
||||
let fetching_tags_copy = fetching_tags.clone();
|
||||
std::thread::spawn(move || {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
tags_copy.load_next_page().await;
|
||||
let mut ui = ui_copy.lock().unwrap();
|
||||
//set position to the position of old TagList
|
||||
//it may have changed since tag fetching has been invoked
|
||||
tags_copy.set_cursor(ui.tags.get_cursor().clone());
|
||||
ui.tags = tags_copy;
|
||||
ui.details = ui.tags.create_detail_widget();
|
||||
ui.info.set_text("Fetching tags done");
|
||||
sender_copy.send(UiEvent::RefreshOnNewData).unwrap();
|
||||
fetching_tags_copy.store(false, Ordering::Relaxed);
|
||||
})
|
||||
});
|
||||
}
|
||||
Ok(DeferredEvent::LoadMoreTags) => {
|
||||
//do nothing, as we are fetching tags
|
||||
}
|
||||
Err(e) => {
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.info.set_info(&e);
|
||||
}
|
||||
};
|
||||
sender.send(UiEvent::RefreshOnNewData)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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) = mpsc::channel();
|
||||
let (deferred_sender, deferred_receiver) = mpsc::channel();
|
||||
let ui_clone = ui.clone();
|
||||
let sender2 = sender.clone();
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = Self::work_requests(&ui_clone, deferred_receiver, sender2) {
|
||||
let mut ui = ui_clone.lock().unwrap();
|
||||
ui.info.set_info(&e);
|
||||
}
|
||||
});
|
||||
|
||||
//setup tui
|
||||
let stdout = std::io::stdout().into_raw_mode()?;
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
//setup input thread
|
||||
let ui_clone = ui.clone();
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = Self::wait_for_input(sender) {
|
||||
let mut ui = ui_clone.lock().unwrap();
|
||||
ui.info.set_info(&e);
|
||||
}
|
||||
});
|
||||
|
||||
//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(30)].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]);
|
||||
})?;
|
||||
drop(ui_data);
|
||||
|
||||
//handle input
|
||||
//wait before locking
|
||||
let event = receiver.recv();
|
||||
let mut ui_data = ui.lock().unwrap();
|
||||
match event {
|
||||
Ok(UiEvent::Input(key)) => match key {
|
||||
//quit without saving
|
||||
Key::Ctrl('q') | Key::Ctrl('c') => {
|
||||
deferred_sender.send(DeferredEvent::Quit)?;
|
||||
break 'core; //quit program without saving
|
||||
}
|
||||
//cycle widgets
|
||||
Key::Char('\t') => {
|
||||
ui_data.state.next();
|
||||
let state = ui_data.state.clone();
|
||||
ui_data.info.set_info(&state);
|
||||
}
|
||||
//refresh repository
|
||||
Key::Ctrl('r') => {
|
||||
ui_data.repo.confirm();
|
||||
deferred_sender.send(DeferredEvent::NewRepo(ui_data.repo.get()))?;
|
||||
}
|
||||
//enter on selecting tags
|
||||
Key::Char('\n') if ui_data.state == State::EditRepo => {
|
||||
ui_data.repo.confirm();
|
||||
deferred_sender.send(DeferredEvent::NewRepo(ui_data.repo.get()))?;
|
||||
}
|
||||
//delete last char on repository
|
||||
Key::Backspace if ui_data.state == State::EditRepo => {
|
||||
ui_data.info.set_text("Editing Repository");
|
||||
ui_data.repo.handle_input(Key::Backspace);
|
||||
}
|
||||
//moving up on selecting tags
|
||||
Key::Up | Key::Char('k') if ui_data.state == State::SelectTag => {
|
||||
ui_data.tags.previous();
|
||||
ui_data.details = ui_data.tags.create_detail_widget();
|
||||
}
|
||||
//moving down on selecting tags
|
||||
Key::Down | Key::Char('j') if ui_data.state == State::SelectTag => {
|
||||
if ui_data.tags.next().is_some() {
|
||||
deferred_sender.send(DeferredEvent::LoadMoreTags).unwrap();
|
||||
}
|
||||
ui_data.details = ui_data.tags.create_detail_widget();
|
||||
}
|
||||
//append character on editing repository
|
||||
Key::Char(key) if ui_data.state == State::EditRepo => {
|
||||
ui_data.info.set_text("Editing Repository");
|
||||
ui_data.repo.handle_input(Key::Char(key));
|
||||
}
|
||||
//ignore all else input
|
||||
_ => {}
|
||||
},
|
||||
Ok(UiEvent::RefreshOnNewData) | Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
terminal.clear()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
352
src/ui/yaml_found.rs
Normal file
352
src/ui/yaml_found.rs
Normal file
@ -0,0 +1,352 @@
|
||||
use anyhow::Result;
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::layout::{Constraint, Direction, Layout};
|
||||
use tui::Terminal;
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::UiEvent;
|
||||
use crate::error::Error;
|
||||
use crate::repository;
|
||||
use crate::widget::async_tag_list::{self, TagList};
|
||||
use crate::widget::{info, repo_entry, service_switcher};
|
||||
use crate::Opt;
|
||||
|
||||
pub struct Ui {
|
||||
state: State,
|
||||
repo: repo_entry::RepoEntry,
|
||||
tags: 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 DeferredEvent {
|
||||
Quit,
|
||||
NewRepo(String),
|
||||
LoadMoreTags,
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
/// catch input and send them to core loop
|
||||
pub fn wait_for_input(sender: mpsc::Sender<UiEvent>) -> Result<(), Error> {
|
||||
let stdin = std::io::stdin();
|
||||
for c in stdin.keys() {
|
||||
sender.send(UiEvent::Input(c.unwrap()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn work_requests(
|
||||
ui: &Arc<Mutex<Ui>>,
|
||||
events: mpsc::Receiver<DeferredEvent>,
|
||||
sender: mpsc::Sender<UiEvent>,
|
||||
) -> Result<(), Error> {
|
||||
use std::sync::atomic::Ordering;
|
||||
let fetching_tags = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
loop {
|
||||
match events.recv() {
|
||||
Ok(DeferredEvent::Quit) => break,
|
||||
Ok(DeferredEvent::NewRepo(name)) => {
|
||||
{
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.tags = TagList::with_status("Fetching new tags...");
|
||||
sender.send(UiEvent::RefreshOnNewData)?;
|
||||
}
|
||||
let list = TagList::with_repo_name(name).await;
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.tags = list;
|
||||
}
|
||||
Ok(DeferredEvent::LoadMoreTags) if !fetching_tags.load(Ordering::Relaxed) => {
|
||||
fetching_tags.store(true, Ordering::Relaxed);
|
||||
let mut tags_copy = {
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.info.set_text("Fetching more tags...");
|
||||
sender.send(UiEvent::RefreshOnNewData)?;
|
||||
ui.tags.clone()
|
||||
};
|
||||
|
||||
let sender_copy = sender.clone();
|
||||
let ui_copy = ui.clone();
|
||||
let fetching_tags_copy = fetching_tags.clone();
|
||||
std::thread::spawn(move || {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
tags_copy.load_next_page().await;
|
||||
let mut ui = ui_copy.lock().unwrap();
|
||||
//set position to the position of old TagList
|
||||
//it may have changed since tag fetching has been invoked
|
||||
tags_copy.set_cursor(ui.tags.get_cursor().clone());
|
||||
ui.tags = tags_copy;
|
||||
ui.details = ui.tags.create_detail_widget();
|
||||
ui.info.set_text("Fetching tags done");
|
||||
sender_copy.send(UiEvent::RefreshOnNewData).unwrap();
|
||||
fetching_tags_copy.store(false, Ordering::Relaxed);
|
||||
})
|
||||
});
|
||||
}
|
||||
Ok(DeferredEvent::LoadMoreTags) => {
|
||||
//do nothing, as we are fetching tags
|
||||
}
|
||||
Err(e) => {
|
||||
let mut ui = ui.lock().unwrap();
|
||||
ui.info.set_info(&e);
|
||||
}
|
||||
};
|
||||
sender.send(UiEvent::RefreshOnNewData)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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: 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) = mpsc::channel();
|
||||
let (deferred_sender, deferred_receiver) = mpsc::channel();
|
||||
let ui_clone = ui.clone();
|
||||
let sender2 = sender.clone();
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = Self::work_requests(&ui_clone, deferred_receiver, sender2) {
|
||||
let mut ui = ui_clone.lock().unwrap();
|
||||
ui.info.set_info(&e);
|
||||
}
|
||||
});
|
||||
|
||||
//setup tui
|
||||
let stdout = std::io::stdout().into_raw_mode()?;
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
//setup input thread
|
||||
let ui_clone = ui.clone();
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = Self::wait_for_input(sender) {
|
||||
let mut ui = ui_clone.lock().unwrap();
|
||||
ui.info.set_info(&e);
|
||||
}
|
||||
});
|
||||
|
||||
//core interaction loop
|
||||
'core: loop {
|
||||
//draw
|
||||
let mut ui_data = ui.lock().unwrap();
|
||||
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]);
|
||||
})?;
|
||||
drop(ui_data);
|
||||
|
||||
//handle events
|
||||
//wait before locking
|
||||
let event = receiver.recv();
|
||||
let mut ui_data = ui.lock().unwrap();
|
||||
match event {
|
||||
//handling input
|
||||
Ok(UiEvent::Input(key)) => match key {
|
||||
//quit without saving
|
||||
Key::Ctrl('q') | Key::Ctrl('c') => {
|
||||
deferred_sender.send(DeferredEvent::Quit)?;
|
||||
break 'core; //quit program without saving
|
||||
}
|
||||
//cycle widgets
|
||||
Key::Char('\t') => {
|
||||
ui_data.state.next();
|
||||
let state = ui_data.state.clone();
|
||||
ui_data.info.set_info(&state);
|
||||
}
|
||||
//save file
|
||||
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"),
|
||||
},
|
||||
//refresh repository
|
||||
Key::Ctrl('r') => {
|
||||
ui_data.repo.confirm();
|
||||
deferred_sender
|
||||
.send(DeferredEvent::NewRepo(ui_data.repo.get()))
|
||||
.unwrap();
|
||||
}
|
||||
//enter on selecting tags
|
||||
Key::Char('\n') if ui_data.state == 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);
|
||||
}
|
||||
//enter on editing repository
|
||||
Key::Char('\n') if ui_data.state == State::EditRepo => {
|
||||
ui_data.repo.confirm();
|
||||
deferred_sender
|
||||
.send(DeferredEvent::NewRepo(ui_data.repo.get()))
|
||||
.unwrap();
|
||||
}
|
||||
//delete last char on repository
|
||||
Key::Backspace if ui_data.state == State::EditRepo => {
|
||||
ui_data.info.set_text("Editing Repository");
|
||||
ui_data.repo.handle_input(Key::Backspace);
|
||||
}
|
||||
//moving up on selecting service
|
||||
Key::Up | Key::Char('k')
|
||||
if ui_data.state == State::SelectService
|
||||
&& 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());
|
||||
deferred_sender
|
||||
.send(DeferredEvent::NewRepo(ui_data.repo.get()))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
//moving down on selecting service
|
||||
Key::Down | Key::Char('j')
|
||||
if ui_data.state == State::SelectService
|
||||
&& 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());
|
||||
deferred_sender
|
||||
.send(DeferredEvent::NewRepo(ui_data.repo.get()))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
//moving up on selecting tags
|
||||
Key::Up | Key::Char('k') if ui_data.state == State::SelectTag => {
|
||||
ui_data.tags.previous();
|
||||
ui_data.details = ui_data.tags.create_detail_widget();
|
||||
}
|
||||
//moving down on selecting tags
|
||||
Key::Down | Key::Char('j') if ui_data.state == State::SelectTag => {
|
||||
if ui_data.tags.next().is_some() {
|
||||
deferred_sender.send(DeferredEvent::LoadMoreTags).unwrap();
|
||||
}
|
||||
ui_data.details = ui_data.tags.create_detail_widget();
|
||||
}
|
||||
//append character on editing repository
|
||||
Key::Char(key) if ui_data.state == State::EditRepo => {
|
||||
ui_data.info.set_text("Editing Repository");
|
||||
ui_data.repo.handle_input(Key::Char(key));
|
||||
}
|
||||
//ignore all else input
|
||||
_ => {}
|
||||
},
|
||||
Ok(UiEvent::RefreshOnNewData) | Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
terminal.clear()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
use std::fmt;
|
||||
|
||||
use termion::event::Key;
|
||||
use tui::style::{Color, Style};
|
||||
use tui::widgets::{Block, Borders, List, ListState};
|
||||
|
||||
@ -22,6 +21,7 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Line {
|
||||
Status(String),
|
||||
Image(repository::Tag),
|
||||
@ -38,6 +38,7 @@ impl fmt::Display for Line {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TagList {
|
||||
lines: Vec<Line>,
|
||||
state: ListState,
|
||||
@ -55,22 +56,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")));
|
||||
@ -85,6 +86,14 @@ impl TagList {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor(&mut self, state: ListState) {
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
pub fn get_cursor(&self) -> &ListState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub fn render(&mut self, colored: bool) -> (List, &mut ListState) {
|
||||
let border_style = if colored {
|
||||
Style::default().fg(Color::Green)
|
||||
@ -128,42 +137,21 @@ impl TagList {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_input(&mut self, key: termion::event::Key) {
|
||||
match key {
|
||||
Key::Down => self.next(),
|
||||
Key::Up => self.previous(),
|
||||
Key::Char('\n') => self.select(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// loads new tags when matching line is selected
|
||||
fn select(&mut self) {
|
||||
if let Some(i) = self.state.selected() {
|
||||
if let Line::NextPage(_) = &self.lines[i] {
|
||||
self.load_next_page()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_selected(&mut self) -> Result<String, Error> {
|
||||
match self.state.selected() {
|
||||
None => Err(Error::NoneSelected),
|
||||
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) {
|
||||
pub 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
|
||||
@ -173,19 +161,15 @@ impl TagList {
|
||||
let next_page = self.lines.pop();
|
||||
|
||||
//add tags
|
||||
match &self.tags {
|
||||
None => (),
|
||||
Some(tags) => {
|
||||
for image in tags.get_tags().iter() {
|
||||
self.lines.push(Line::Image(image.clone()));
|
||||
}
|
||||
if let Some(tags) = &self.tags {
|
||||
for image in tags.get_tags().iter() {
|
||||
self.lines.push(Line::Image(image.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
//readd next page
|
||||
match self.tags.as_ref().unwrap().next_page() {
|
||||
None => (),
|
||||
Some(_) => self.lines.push(next_page.unwrap()),
|
||||
//readd next page item
|
||||
if (self.tags.as_ref().unwrap().next_page().await).is_some() {
|
||||
self.lines.push(next_page.unwrap());
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -194,21 +178,29 @@ impl TagList {
|
||||
}
|
||||
|
||||
/// select next tag
|
||||
fn next(&mut self) {
|
||||
/// returns Some when more tags need to be fetched otherwise None
|
||||
pub fn next(&mut self) -> Option<()> {
|
||||
if let Some(Line::Status(_)) = self.lines.get(0) {
|
||||
return None;
|
||||
}
|
||||
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 => return Some(()),
|
||||
// Some(i) if i == self.lines.len() - 2 => return self.load_next_page().await,
|
||||
Some(i) => self.state.select(Some(i + 1)),
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// select previous tag
|
||||
fn previous(&mut self) {
|
||||
pub 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)),
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ impl Details {
|
||||
let mut lines = vec![format!("{:^10}|{:^6}|{:^6}", "ARCH", "OS", "SIZE")];
|
||||
for d in &self.details {
|
||||
lines.push(format!(
|
||||
"{:^10}|{:^6}|{:^6}MB",
|
||||
"{:>10}|{:>6}|{:>6} MB",
|
||||
format!(
|
||||
"{}{}",
|
||||
d.arch.clone().unwrap_or_default(),
|
||||
|
@ -11,7 +11,7 @@ impl Info {
|
||||
Self {
|
||||
info: String::from(info),
|
||||
keys: String::from(
|
||||
"Tab Cycle widgets C-s Save C-r Reload C-q Quit ↑ ↓ Select tags or image line Return Select current selection",
|
||||
"Tab Cycle widgets C-s Save C-r Reload C-q Quit ↑ ↓ Select tags or image line Return Select",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -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