Debug
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -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",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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
1
src/global.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
19
src/main.rs
19
src/main.rs
@@ -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
2
src/session.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod connection;
|
||||||
|
mod credential;
|
||||||
99
src/session/connection.rs
Normal file
99
src/session/connection.rs
Normal 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
58
src/session/credential.rs
Normal 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
1
src/util.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
2
src/util/file.rs
Normal file
2
src/util/file.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|||||||
Reference in New Issue
Block a user