Compare commits

1 Commits

Author SHA1 Message Date
ea04413e8a couple ui and data 2025-02-27 01:54:08 +08:00
18 changed files with 464 additions and 313 deletions

303
Cargo.lock generated
View File

@@ -2,21 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "allocator-api2" name = "allocator-api2"
version = "0.2.18" version = "0.2.18"
@@ -29,21 +14,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
version = "0.3.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
@@ -56,12 +26,6 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bytes"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
[[package]] [[package]]
name = "cassowary" name = "cassowary"
version = "0.3.0" version = "0.3.0"
@@ -77,48 +41,12 @@ dependencies = [
"rustversion", "rustversion",
] ]
[[package]]
name = "cc"
version = "1.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
dependencies = [
"shlex",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "color-eyre"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
dependencies = [
"backtrace",
"color-spantrace",
"eyre",
"indenter",
"once_cell",
"owo-colors",
"tracing-error",
]
[[package]]
name = "color-spantrace"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
dependencies = [
"once_cell",
"owo-colors",
"tracing-core",
"tracing-error",
]
[[package]] [[package]]
name = "compact_str" name = "compact_str"
version = "0.8.0" version = "0.8.0"
@@ -211,16 +139,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "eyre"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
]
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.3" version = "0.1.3"
@@ -244,12 +162,6 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.0" version = "0.15.0"
@@ -274,16 +186,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]] [[package]]
name = "indenter" name = "indexmap"
version = "0.3.3" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
[[package]] "equivalent",
name = "indoc" "hashbrown",
version = "2.0.5" ]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]] [[package]]
name = "instability" name = "instability"
@@ -318,9 +228,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.170" version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]] [[package]]
name = "libredox" name = "libredox"
@@ -369,15 +279,6 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
dependencies = [
"adler",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.0.2" version = "1.0.2"
@@ -406,33 +307,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
[[package]] [[package]]
name = "option-ext" name = "option-ext"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "owo-colors"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.3" version = "0.12.3"
@@ -462,12 +342,6 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]] [[package]]
name = "powerfmt" name = "powerfmt"
version = "0.2.0" version = "0.2.0"
@@ -494,24 +368,24 @@ dependencies = [
[[package]] [[package]]
name = "ratatui" name = "ratatui"
version = "0.29.0" version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cassowary", "cassowary",
"compact_str", "compact_str",
"crossterm", "crossterm",
"indoc",
"instability", "instability",
"itertools", "itertools",
"lru", "lru",
"paste", "paste",
"strum", "strum",
"strum_macros",
"time", "time",
"unicode-segmentation", "unicode-segmentation",
"unicode-truncate", "unicode-truncate",
"unicode-width 0.2.0", "unicode-width 0.1.14",
] ]
[[package]] [[package]]
@@ -539,23 +413,15 @@ name = "rstemp"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base64", "base64",
"color-eyre",
"crossterm", "crossterm",
"dirs", "dirs",
"lazy_static", "lazy_static",
"ratatui", "ratatui",
"serde", "serde",
"serde_json", "toml",
"tokio",
"unicode-width 0.2.0", "unicode-width 0.2.0",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.37" version = "0.38.37"
@@ -589,18 +455,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.218" version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.218" version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -608,32 +474,14 @@ dependencies = [
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_spanned"
version = "1.0.139" version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [ dependencies = [
"itoa",
"memchr",
"ryu",
"serde", "serde",
] ]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
version = "0.3.17" version = "0.3.17"
@@ -670,16 +518,6 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "static_assertions" name = "static_assertions"
version = "1.1.0" version = "1.1.0"
@@ -739,16 +577,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.36" version = "0.3.36"
@@ -771,73 +599,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]] [[package]]
name = "tokio" name = "toml"
version = "1.43.0" version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
dependencies = [ dependencies = [
"backtrace", "serde",
"bytes", "serde_spanned",
"libc", "toml_datetime",
"mio", "toml_edit",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "tokio-macros" name = "toml_datetime"
version = "2.5.0" version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [ dependencies = [
"proc-macro2", "serde",
"quote",
"syn",
] ]
[[package]] [[package]]
name = "tracing" name = "toml_edit"
version = "0.1.41" version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [ dependencies = [
"pin-project-lite", "indexmap",
"tracing-core", "serde",
] "serde_spanned",
"toml_datetime",
[[package]] "winnow",
name = "tracing-core"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-error"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
] ]
[[package]] [[package]]
@@ -875,12 +667,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@@ -1047,3 +833,12 @@ name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
dependencies = [
"memchr",
]

View File

@@ -5,12 +5,10 @@ edition = "2021"
[dependencies] [dependencies]
base64 = "0.22.1" base64 = "0.22.1"
color-eyre = "0.6.3"
crossterm = { version = "0.28.1", features = ["event-stream"] } crossterm = { version = "0.28.1", features = ["event-stream"] }
dirs = "5.0.1" dirs = "5.0.1"
lazy_static = "1.5.0" lazy_static = "1.5.0"
ratatui = { version = "0.29.0", features = ["all-widgets"] } ratatui = { version = "0.28.1", features = ["all-widgets"] }
serde = { version = "1.0.218", features = ["derive"] } serde = { version = "1.0.214", features = ["derive"] }
serde_json = "1.0.139" toml = "0.8.19"
tokio = { version = "1.43.0", features = ["full"] }
unicode-width = "0.2.0" unicode-width = "0.2.0"

View File

@@ -1,14 +1,24 @@
use color_eyre::Result; use std::{default, io};
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
use ratatui::{DefaultTerminal, Frame}; use ratatui::{DefaultTerminal, Frame};
use crate::view::{home::Home, View}; use crate::{
session::{connection::ConnectionManager, credential::CredentialManager, SessionManager},
view::{home::Home, View},
};
pub struct App { pub struct App {
running: bool, running: bool,
view: Home, view: Home,
} }
pub struct AppState {
pub connection_manager: ConnectionManager,
pub credential_manager: CredentialManager,
pub running: bool,
}
impl Default for App { impl Default for App {
fn default() -> Self { fn default() -> Self {
App { App {
@@ -18,27 +28,41 @@ impl Default for App {
} }
} }
impl Default for AppState {
fn default() -> Self {
let mut sem = SessionManager::read_from_file();
let mut cnm = sem.connection_manager;
let mut crm = sem.credential_manager;
AppState {
connection_manager: cnm,
credential_manager: crm,
running: true,
}
}
}
impl App { impl App {
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> Result<()> { pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
while self.running { let mut state = AppState::default();
terminal.draw(|frame| self.draw(frame))?; while state.running {
self.handle_event()?; terminal.draw(|frame| self.draw(frame, &state))?;
self.handle_event(&mut state)?;
} }
Ok(()) Ok(())
} }
fn draw(&self, frame: &mut Frame) { fn draw(&mut self, frame: &mut Frame, ctx: &AppState) {
self.view.draw(frame); self.view.draw(frame, ctx);
} }
fn handle_event(&mut self) -> Result<()> { fn handle_event(&mut self, ctx: &mut AppState) -> io::Result<()> {
match event::read()? { match event::read()? {
Event::Key(KeyEvent { Event::Key(KeyEvent {
kind: KeyEventKind::Press, kind: KeyEventKind::Press,
code: KeyCode::Esc, code: KeyCode::Esc,
.. ..
}) => self.running = false, }) => ctx.running = false,
event => self.view.handle_event(&event)?, event => self.view.handle_event(&event, ctx)?,
} }
Ok(()) Ok(())
} }

View File

@@ -1,5 +1,13 @@
use std::{io, path::Path};
use app::App; use app::App;
use color_eyre::Result; use base64::{engine::general_purpose::STANDARD, Engine as _};
use session::{
connection::{Connection, ConnectionManager, Connections, ShConnection, SshConnection},
credential::{Credential, CredentialManager, CredentialType},
SessionManager,
};
use util::file;
mod app; mod app;
mod global; mod global;
@@ -7,10 +15,60 @@ mod session;
mod util; mod util;
mod view; mod view;
fn main() -> Result<()> { fn main() -> io::Result<()> {
color_eyre::install()?;
let mut terminal = ratatui::init(); let mut terminal = ratatui::init();
let app_result = App::default().run(&mut terminal); let app_result = App::default().run(&mut terminal);
ratatui::restore(); ratatui::restore();
app_result app_result
} }
/* fn main() {
let mut conf = SessionManager::read_from_file();
println!("{:?}", conf.connection_manager.connections().len());
} */
// fn main() -> io::Result<()> {
// // let sh = ShConnection::new("zsh".to_string(), "/usr/bin/nu".to_string(), None);
// // let _ = sh.exec_cmd();
// let ssh = SshConnection::new("".to_string(), "myhost.fallen-angle.com".to_string(), 7044, "rainbus".to_string(), "".to_string());
// ssh.exec_cmd();
// Ok(())
// }
/* fn main() -> io::Result<()> {
let mut cnm = ConnectionManager::default();
let sh = Connections::Sh(ShConnection::new(
"zsh".to_string(),
"/usr/bin/zsh".to_string(),
None,
));
let ssh_sec = Connections::Ssh(SshConnection::new(
"Tencent".to_string(),
"myhost.fallen-angle.com".to_string(),
22,
"rainbus".to_string(),
"Server".to_string(),
));
let ssh_pwd = Connections::Ssh(SshConnection::new(
"Aliyun".to_string(),
"myweb.fallen-angle.com".to_string(),
22,
"rainbus".to_string(),
"AliPwd".to_string(),
));
cnm.connections().extend([sh, ssh_sec, ssh_pwd]);
let mut crm = CredentialManager::default();
let ser_crd = Credential::new(
"Server".to_string(),
CredentialType::Secret(STANDARD.encode(file::read_file(Path::new("")))),
);
let pwd_crd = Credential::new(
"Alipwd".to_string(),
CredentialType::Password("20001001".to_string()),
);
crm.credentials().extend([ser_crd, pwd_crd]);
let sem = SessionManager::new(cnm, crm);
// println!("{}", toml::to_string_pretty(&sem).unwrap());
file::write_file(&global::CONFIG_DIR.join("tethers.toml"), toml::to_string_pretty(&sem).unwrap().as_str());
Ok(())
} */

View File

@@ -1,2 +1,32 @@
use connection::ConnectionManager;
use credential::CredentialManager;
use serde::{Deserialize, Serialize};
use crate::global;
use crate::util::file;
pub mod connection;
pub mod credential;
#[derive(Serialize, Deserialize)]
pub struct SessionManager {
pub connection_manager: ConnectionManager,
pub credential_manager: CredentialManager,
}
impl SessionManager {
pub fn new(cnm: ConnectionManager, crm: CredentialManager) -> Self {
Self {
connection_manager: cnm,
credential_manager: crm,
}
}
pub fn read_from_file() -> SessionManager {
let conf = file::read_file(&global::CONFIG_DIR.join("tethers.toml"));
// println!("{}", conf);
let sem: SessionManager = toml::from_str(&conf).unwrap();
// let cnm: ConnectionManager = ConnectionManager { connections: vec![] };
sem
}
}

121
src/session/connection.rs Normal file
View File

@@ -0,0 +1,121 @@
use std::{env, fs, io, path::Path, process::Command};
use serde::{Deserialize, Serialize};
use crate::global;
use crate::util::file;
use super::credential::{Credential, CredentialType};
pub trait Connection {
fn exec_cmd(&self) -> io::Result<()>;
}
#[derive(Serialize, Deserialize)]
pub struct ShConnection {
pub name: String,
path: String,
args: Vec<String>,
}
impl Connection for ShConnection {
fn exec_cmd(&self) -> io::Result<()> {
Command::new(&self.path).args(&self.args).spawn()?.wait()?;
Ok(())
// if output.status.success() {
// Ok(String::from_utf8_lossy(&output.stdout).into_owned())
// } else {
// Err(io::Error::new(
// io::ErrorKind::Other,
// String::from_utf8_lossy(&output.stderr).into_owned(),
// ))
// }
}
}
impl ShConnection {
pub fn new(name: String, path: String, args: Option<Vec<String>>) -> Self {
ShConnection {
name,
path,
args: args.unwrap_or_else(Vec::new),
}
}
}
#[derive(Serialize, Deserialize)]
pub struct SshConnection {
pub name: String,
host: String,
port: u16,
user: String,
credential_name: String,
}
impl SshConnection {
pub fn new(
name: String,
host: String,
port: u16,
user: String,
credential_name: String,
) -> Self {
Self {
name,
host,
port,
user,
credential_name,
}
}
}
impl Connection for SshConnection {
fn exec_cmd(&self) -> io::Result<()> {
let credential = Credential::new(
"zsh".to_string(),
CredentialType::Secret(file::read_file(Path::new(""))),
);
let mut ssh_command = Command::new("ssh");
let secret_path = env::temp_dir().join("tethers").join("secret.tmp");
let _ = fs::create_dir_all(secret_path.parent().unwrap());
ssh_command
.args(["-p", &self.port.to_string()])
.args(["-l", &self.user]);
match credential.credential() {
CredentialType::Password(pwd) => {
println!("{}", pwd);
}
CredentialType::Secret(secret) => {
let _ = credential.write_secret(&secret_path);
ssh_command.args(["-i", secret_path.to_str().unwrap()]);
println!("{}", secret);
}
}
ssh_command.arg(&self.host);
ssh_command.spawn()?.wait()?;
Ok(())
}
}
#[derive(Serialize, Deserialize)]
pub enum Connections {
Sh(ShConnection),
Ssh(SshConnection),
}
#[derive(Default, Serialize, Deserialize)]
pub struct ConnectionManager {
connections: Vec<Connections>,
}
impl ConnectionManager {
pub fn connections(&mut self) -> &mut Vec<Connections> {
&mut self.connections
}
pub fn connections_imut(&self) -> &Vec<Connections> {
&self.connections
}
}

76
src/session/credential.rs Normal file
View File

@@ -0,0 +1,76 @@
use std::{
fs::OpenOptions,
io::{self, Write},
os::unix::fs::OpenOptionsExt,
path::Path,
};
use base64::{engine::general_purpose::STANDARD, Engine};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub enum CredentialType {
Password(String),
Secret(String),
}
impl CredentialType {}
#[derive(Serialize, Deserialize)]
pub struct Credential {
credential: CredentialType,
name: String,
}
impl Credential {
pub fn write_secret(&self, secret_path: &Path) -> io::Result<()> {
match &self.credential {
CredentialType::Secret(secret) => {
let mut secret_file = OpenOptions::new()
.write(true)
.create(true)
.mode(0o600)
.open(secret_path)?;
let _ = secret_file.set_len(0);
let _ = secret_file.write_all(secret.as_bytes());
}
_ => {}
}
Ok(())
}
}
impl Credential {
pub fn new(name: String, credential: CredentialType) -> Self {
// TODO: modify this to Type
let credential = match credential {
CredentialType::Password(password) => {
CredentialType::Password(STANDARD.encode(password))
}
CredentialType::Secret(secret) => CredentialType::Secret(STANDARD.encode(secret)),
};
Self { name, credential }
}
pub fn credential(&self) -> &CredentialType {
&self.credential
}
}
#[derive(Default, Serialize, Deserialize)]
pub struct CredentialManager {
credentials: Vec<Credential>,
}
impl CredentialManager {
pub fn credentials(&mut self) -> &mut Vec<Credential> {
&mut self.credentials
}
pub fn find_by_name(&self, name: &str) -> io::Result<&Credential> {
self.credentials
.iter()
.find(|credential| credential.name == name)
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Credential not found"))
}
}

View File

@@ -1 +1 @@
pub mod file;

18
src/util/file.rs Normal file
View File

@@ -0,0 +1,18 @@
use std::{
fs::File,
io::{Read, Write},
path::{self, Path},
};
pub fn read_file(path: &Path) -> String {
// let path = dirs::home_dir().unwrap().join(".ssh/Server");
let mut file = File::open(path).unwrap();
let mut content = String::new();
file.read_to_string(&mut content).unwrap();
content
}
pub fn write_file(path: &Path, content: &str) {
let mut file = File::create(path).unwrap();
let res = file.write_all(content.as_bytes());
}

View File

@@ -1,17 +1,20 @@
use color_eyre::Result; use std::io;
use crossterm::event::Event; use crossterm::event::Event;
use ratatui::{ use ratatui::{
layout::{Flex, Layout, Rect}, layout::{Flex, Layout, Rect},
Frame, Frame,
}; };
use crate::app::AppState;
pub mod component; pub mod component;
pub mod home; pub mod home;
pub mod setting; pub mod setting;
pub trait View { pub trait View {
fn draw(&self, frame: &mut Frame); fn draw(&mut self, frame: &mut Frame, ctx: &AppState);
fn handle_event(&mut self, event: &Event) -> Result<()>; fn handle_event(&mut self, event: &Event, ctx: &mut AppState) -> io::Result<()>;
} }
pub trait PopupView { pub trait PopupView {

View File

@@ -1,4 +1,5 @@
use color_eyre::Result; use std::io;
use crossterm::event::Event; use crossterm::event::Event;
use ratatui::widgets::Widget; use ratatui::widgets::Widget;
@@ -13,6 +14,6 @@ pub mod list;
pub trait Component { pub trait Component {
type EventResult; type EventResult;
fn widget(&self) -> impl Widget; fn widget(&self) -> impl Widget;
fn event_handler(&mut self, event: &Event) -> Result<Self::EventResult>; fn event_handler(&mut self, event: &Event) -> io::Result<Self::EventResult>;
fn active_state(&mut self) -> &mut ActiveState; fn active_state(&mut self) -> &mut ActiveState;
} }

View File

@@ -1,5 +1,4 @@
use color_eyre::Result; use ratatui::{symbols::block, widgets::Paragraph};
use ratatui::widgets::Paragraph;
use crate::view::ActiveState; use crate::view::ActiveState;
@@ -15,7 +14,6 @@ pub struct ButtonComponent {
active_state: ActiveState, active_state: ActiveState,
} }
#[allow(dead_code)]
impl ButtonComponent { impl ButtonComponent {
pub fn text(mut self, text: String) -> Self { pub fn text(mut self, text: String) -> Self {
self.text = text; self.text = text;
@@ -33,8 +31,8 @@ impl Component for ButtonComponent {
} }
fn event_handler( fn event_handler(
&mut self, &mut self,
_event: &crossterm::event::Event, event: &crossterm::event::Event,
) -> Result<ButtonEvent> { ) -> std::io::Result<Self::EventResult> {
Ok(ButtonEvent::Click) Ok(ButtonEvent::Click)
} }
fn active_state(&mut self) -> &mut ActiveState { fn active_state(&mut self) -> &mut ActiveState {

View File

@@ -1,4 +1,5 @@
use color_eyre::Result; use std::io;
use crossterm::event::Event; use crossterm::event::Event;
use ratatui::widgets::{Paragraph, Widget}; use ratatui::widgets::{Paragraph, Widget};
@@ -18,7 +19,7 @@ impl Component for HelpComponent {
let help = Paragraph::new("Temp").block(block); let help = Paragraph::new("Temp").block(block);
help help
} }
fn event_handler(&mut self, _event: &Event) -> Result<()> { fn event_handler(&mut self, _event: &Event) -> io::Result<()> {
Ok(()) Ok(())
} }
fn active_state(&mut self) -> &mut ActiveState { fn active_state(&mut self) -> &mut ActiveState {

View File

@@ -1,4 +1,5 @@
use color_eyre::Result; use std::io;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use ratatui::{ use ratatui::{
layout::{Position, Rect}, layout::{Position, Rect},
@@ -10,7 +11,6 @@ use crate::view::ActiveState;
use super::{block::BlockComponent, Component}; use super::{block::BlockComponent, Component};
#[allow(dead_code)]
pub enum InputEvent { pub enum InputEvent {
Compelete, Compelete,
None, None,
@@ -47,7 +47,7 @@ impl Component for InputComponent {
input input
} }
fn event_handler(&mut self, event: &Event) -> Result<InputEvent> { fn event_handler(&mut self, event: &Event) -> io::Result<InputEvent> {
if !self.active_state.is_active() { if !self.active_state.is_active() {
return Ok(InputEvent::None); return Ok(InputEvent::None);
} }

View File

@@ -1,4 +1,3 @@
use color_eyre::Result;
use crossterm::{ use crossterm::{
event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers}, event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
style::Color, style::Color,
@@ -27,9 +26,8 @@ pub struct ListComponent {
} }
impl ListComponent { impl ListComponent {
#[allow(dead_code)]
pub fn add_item(&mut self, item_keys: Vec<String>) { pub fn add_item(&mut self, item_keys: Vec<String>) {
self.item_keys.extend(item_keys); self.item_keys = item_keys;
} }
pub fn name(mut self, name: String) -> Self { pub fn name(mut self, name: String) -> Self {
self.name = name; self.name = name;
@@ -76,10 +74,7 @@ impl Component for ListComponent {
list list
} }
fn event_handler(&mut self, event: &Event) -> Result<ListEvent> { fn event_handler(&mut self, event: &Event) -> std::io::Result<ListEvent> {
if self.item_keys.is_empty() {
return Ok(ListEvent::None);
}
if !self.active_state.is_active() { if !self.active_state.is_active() {
return Ok(ListEvent::None); return Ok(ListEvent::None);
} }
@@ -93,7 +88,6 @@ impl Component for ListComponent {
use KeyCode::*; use KeyCode::*;
match code { match code {
Up => { Up => {
if self.item_keys.len() == 0 {}
self.cursor = if self.cursor == 0 { self.cursor = if self.cursor == 0 {
self.item_keys.len() - 1 self.item_keys.len() - 1
} else { } else {
@@ -118,7 +112,6 @@ impl Component for ListComponent {
} }
Ok(ListEvent::None) Ok(ListEvent::None)
} }
fn active_state(&mut self) -> &mut ActiveState { fn active_state(&mut self) -> &mut ActiveState {
&mut self.active_state &mut self.active_state
} }

View File

@@ -1,14 +1,20 @@
use color_eyre::Result; use std::{io, string};
use crossterm::event::Event;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
layout::{Constraint, Direction, Layout}, layout::{Constraint, Direction, Layout, Rect},
style::{Color, Style},
widgets::{Block, BorderType, Borders},
Frame, Frame,
}; };
use serde::Serialize;
use crate::{app::AppState, session::connection::{Connection, Connections, ShConnection}};
use super::{ use super::{
component::{ component::{
help::HelpComponent, help::HelpComponent,
input::{InputComponent, InputEvent}, input::{self, InputComponent, InputEvent},
list::{ListComponent, ListEvent}, list::{ListComponent, ListEvent},
Component, Component,
}, },
@@ -41,7 +47,7 @@ impl Default for Home {
} }
impl View for Home { impl View for Home {
fn draw(&self, frame: &mut Frame) { fn draw(&mut self, frame: &mut Frame, ctx: &AppState) {
let layout = Layout::default() let layout = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(vec![ .constraints(vec![
@@ -51,26 +57,42 @@ impl View for Home {
]) ])
.split(frame.area()); .split(frame.area());
self.input.show_cursor(frame, layout[0]); self.input.show_cursor(frame, layout[0]);
let conn_names: Vec<_> = ctx
.connection_manager
.connections_imut()
.iter()
.map(|conn| match conn {
Connections::Sh(sh) => sh.name.clone(),
Connections::Ssh(ssh) => ssh.name.clone(),
})
.collect();
self.table.add_item(conn_names);
frame.render_widget(self.input.widget(), layout[0]); frame.render_widget(self.input.widget(), layout[0]);
frame.render_widget(self.table.widget(), layout[1]); frame.render_widget(self.table.widget(), layout[1]);
frame.render_widget(self.help.widget(), layout[2]); frame.render_widget(self.help.widget(), layout[2]);
self.setting.draw(frame); self.setting.draw(frame, ctx);
} }
fn handle_event(&mut self, event: &Event) -> Result<()> { fn handle_event(&mut self, event: &Event, ctx: &mut AppState) -> io::Result<()> {
match self.input.event_handler(event) { match self.input.event_handler(event) {
Ok(InputEvent::None) => {} Ok(InputEvent::None) => {}
_ => {} _ => {}
} }
match self.table.event_handler(event) { match self.table.event_handler(event) {
Ok(ListEvent::Select(i)) => { Ok(ListEvent::Select(i)) => {
let active_state = self.setting.active_state().is_active(); let conn = &ctx.connection_manager.connections()[i];
self.setting.active_state().set_active(active_state); match &conn {
println!("{}", i); &Connections::Sh(sh) => {
sh.exec_cmd();
},
&Connections::Ssh(ssh) => {
println!("{:?}", toml::to_string_pretty(&ctx.credential_manager));
},
};
} }
_ => {} _ => {}
} }
self.setting.handle_event(event)?; // self.setting.handle_event(event)?;
Ok(()) Ok(())
} }
} }

View File

@@ -1,9 +1,16 @@
use color_eyre::Result; use std::io;
use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::{
layout::{Constraint, Layout, Rect},
widgets::{Block, Clear},
};
use crate::app::AppState;
use super::{ use super::{
center_rect, center_rect,
component::{ component::{
block::BlockComponent,
list::{ListComponent, ListEvent}, list::{ListComponent, ListEvent},
Component, Component,
}, },
@@ -31,7 +38,7 @@ impl Default for SettingView {
} }
impl View for SettingView { impl View for SettingView {
fn draw(&self, frame: &mut ratatui::Frame) { fn draw(&mut self, frame: &mut ratatui::Frame, ctx: &AppState) {
if !self.activate_state.is_active() { if !self.activate_state.is_active() {
return; return;
} }
@@ -42,7 +49,11 @@ impl View for SettingView {
frame.render_widget(self.menu.widget(), layout[0]); frame.render_widget(self.menu.widget(), layout[0]);
} }
fn handle_event(&mut self, event: &crossterm::event::Event) -> Result<()> { fn handle_event(
&mut self,
event: &crossterm::event::Event,
ctx: &mut AppState,
) -> io::Result<()> {
match self.menu.event_handler(event) { match self.menu.event_handler(event) {
Ok(ListEvent::Select(idx)) => { Ok(ListEvent::Select(idx)) => {
self.activate_state.set_active(false); self.activate_state.set_active(false);

2
src/view/util.rs Normal file
View File

@@ -0,0 +1,2 @@