This commit is contained in:
2024-10-30 21:18:02 +08:00
parent aea8ffc9da
commit 9bb6854125
15 changed files with 216 additions and 24 deletions

7
Cargo.lock generated
View File

@@ -172,6 +172,12 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.159" version = "0.2.159"
@@ -326,6 +332,7 @@ name = "rstemp"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"crossterm", "crossterm",
"lazy_static",
"ratatui", "ratatui",
"unicode-width 0.2.0", "unicode-width 0.2.0",
] ]

View File

@@ -5,5 +5,6 @@ edition = "2021"
[dependencies] [dependencies]
crossterm = { version = "0.28.1", features = ["event-stream"] } crossterm = { version = "0.28.1", features = ["event-stream"] }
lazy_static = "1.5.0"
ratatui = { version = "0.28.1", features = ["all-widgets"] } ratatui = { version = "0.28.1", features = ["all-widgets"] }
unicode-width = "0.2.0" unicode-width = "0.2.0"

1
src/global.rs Normal file
View File

@@ -0,0 +1 @@

View File

@@ -1,13 +1,24 @@
use std::io; use std::io;
use app::App; use app::App;
use session::connection::{Connection, ShConnection};
mod app; mod app;
mod session;
mod view; mod view;
mod global;
mod util;
// fn main() -> io::Result<()> {
// let mut terminal = ratatui::init();
// let app_result = App::default().run(&mut terminal);
// ratatui::restore();
// app_result
// }
//
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
let mut terminal = ratatui::init(); let sh = ShConnection::new("zsh".to_string(), "/usr/bin/nu".to_string(), None);
let app_result = App::default().run(&mut terminal); let _ = sh.exec_cmd();
ratatui::restore(); Ok(())
app_result
} }

2
src/session.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod connection;
mod credential;

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

@@ -0,0 +1,99 @@
use std::{env, io, path::Path, process::Command};
use super::credential::{Credential, CredentialType};
pub trait Connection {
fn exec_cmd(&self) -> io::Result<()>;
}
pub struct ShConnection {
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),
}
}
}
pub struct SshConnection {
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::Password("".to_string()));
let mut ssh_command = Command::new("ssh");
let secret_path = env::temp_dir().join("tethers").join("secret.tmp");
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(())
}
}
pub enum Connections {
Sh(ShConnection),
Ssh(SshConnection),
}
pub struct ConnectionManager {
connections: Vec<Connections>,
}

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

@@ -0,0 +1,58 @@
use std::{
fs::OpenOptions,
io::{self, Write},
os::unix::fs::OpenOptionsExt,
path::Path,
};
pub enum CredentialType {
Password(String),
Secret(String),
}
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 {
Self { name, credential }
}
pub fn credential(&self) -> &CredentialType {
&self.credential
}
}
#[derive(Default)]
pub struct CredentialManager {
credentials: Vec<Credential>,
}
impl CredentialManager {
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"))
}
}

1
src/util.rs Normal file
View File

@@ -0,0 +1 @@

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

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

View File

@@ -6,10 +6,10 @@ use ratatui::widgets::Widget;
use super::ActiveState; use super::ActiveState;
pub mod block; pub mod block;
pub mod button;
pub mod help; pub mod help;
pub mod input; pub mod input;
pub mod list; pub mod list;
pub mod button;
pub trait Component { pub trait Component {
type EventResult; type EventResult;

View File

@@ -1,6 +1,9 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use ratatui::{style::{Style, Stylize}, widgets::{Block, Borders}}; use ratatui::{
style::{Style, Stylize},
widgets::{Block, Borders},
};
#[derive(Default)] #[derive(Default)]
pub struct BlockComponent<'a> { pub struct BlockComponent<'a> {
@@ -13,7 +16,11 @@ impl<'a> BlockComponent<'a> {
pub fn widget(&self) -> Block<'a> { pub fn widget(&self) -> Block<'a> {
Block::default() Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.border_style(if self.active {Style::new().blue()} else {Style::new().white()}) .border_style(if self.active {
Style::new().blue()
} else {
Style::new().white()
})
.title(self.title.clone()) .title(self.title.clone())
} }

View File

@@ -9,7 +9,7 @@ use super::{block::BlockComponent, Component};
#[derive(Default)] #[derive(Default)]
pub struct HelpComponent { pub struct HelpComponent {
active_state: ActiveState active_state: ActiveState,
} }
impl Component for HelpComponent { impl Component for HelpComponent {

View File

@@ -21,7 +21,7 @@ pub struct InputComponent {
name: String, name: String,
value: String, value: String,
cursor: usize, cursor: usize,
active_state: ActiveState active_state: ActiveState,
} }
impl InputComponent { impl InputComponent {
@@ -39,7 +39,10 @@ impl Component for InputComponent {
type EventResult = InputEvent; type EventResult = InputEvent;
fn widget(&self) -> impl Widget { fn widget(&self) -> impl Widget {
let block = BlockComponent::default().title(self.name.clone()).active(self.active_state.is_active()).widget(); let block = BlockComponent::default()
.title(self.name.clone())
.active(self.active_state.is_active())
.widget();
let input = Paragraph::new(self.value.clone()).block(block); let input = Paragraph::new(self.value.clone()).block(block);
input input
} }

View File

@@ -37,8 +37,8 @@ impl Default for Home {
help: HelpComponent::default(), help: HelpComponent::default(),
setting: SettingView::default(), setting: SettingView::default(),
}; };
home.table.active_state().set_active(true); // home.table.active_state().set_active(true);
// home.setting.active_state().set_active(true); home.setting.active_state().set_active(true);
home home
} }
} }

View File

@@ -25,32 +25,32 @@ impl SettingView {}
impl Default for SettingView { impl Default for SettingView {
fn default() -> Self { fn default() -> Self {
SettingView { let mut seting = SettingView {
area: Rect::new(0, 0, 10, 5), area: Rect::new(0, 0, 10, 5),
menu: ListComponent::default().selectable(), menu: ListComponent::default().selectable(),
activate_state: Default::default(), activate_state: Default::default(),
} };
seting.menu.active_state().set_active(true);
seting
} }
} }
impl View for SettingView { impl View for SettingView {
fn draw(&self, frame: &mut ratatui::Frame) { fn draw(&self, frame: &mut ratatui::Frame) {
let area = center_rect(frame.area(), self.area.width, self.area.height); if !self.activate_state.is_active() {
if self.activate_state.is_active() { return;
let layout = Layout::default()
.constraints([Constraint::default()])
.split(area);
frame.render_widget(Clear, area);
frame.render_widget(self.menu.widget(), layout[0]);
} else {
frame.render_widget(Clear, area);
} }
let area = center_rect(frame.area(), self.area.width, self.area.height);
let layout = Layout::default()
.constraints([Constraint::default()])
.split(area);
frame.render_widget(self.menu.widget(), layout[0]);
} }
fn handle_event(&mut self, event: &crossterm::event::Event) -> io::Result<()> { fn handle_event(&mut self, event: &crossterm::event::Event) -> io::Result<()> {
match self.menu.event_handler(event) { match self.menu.event_handler(event) {
Ok(ListEvent::Select(idx)) => { Ok(ListEvent::Select(idx)) => {
// self.menu.active_state().set_active(false); self.activate_state.set_active(false);
println!("{}", idx); println!("{}", idx);
} }
_ => {} _ => {}