initialize new Rust project with Cargo configuration and dependencies
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
377
Cargo.lock
generated
Normal file
377
Cargo.lock
generated
Normal file
@@ -0,0 +1,377 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dialoguer"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de"
|
||||
dependencies = [
|
||||
"console",
|
||||
"shell-words",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.13.3+wasi-0.2.2",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad"
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"libredox",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shell-words"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||
|
||||
[[package]]
|
||||
name = "ssh-profiles"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dialoguer",
|
||||
"dirs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"getrandom 0.3.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "ssh-profiles"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
dialoguer = "0.11.0"
|
||||
dirs = "6.0.0"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
349
src/main.rs
Normal file
349
src/main.rs
Normal file
@@ -0,0 +1,349 @@
|
||||
use dialoguer::{Input, Select};
|
||||
use std::ops::Not;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SshProfile {
|
||||
host: String,
|
||||
hostname: String,
|
||||
port: u16,
|
||||
user: String,
|
||||
identity_file: Option<String>,
|
||||
password_authentication: Option<bool>,
|
||||
}
|
||||
|
||||
fn get_ssh_config_path() -> PathBuf {
|
||||
let home_dir = dirs::home_dir().expect("Failed to get home directory");
|
||||
home_dir.join(".ssh/config")
|
||||
}
|
||||
|
||||
fn ensure_config_exists() -> io::Result<()> {
|
||||
let path = get_ssh_config_path();
|
||||
if !path.exists() {
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
fs::write(&path, "")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_ssh_config() -> io::Result<String> {
|
||||
ensure_config_exists()?;
|
||||
|
||||
let path = get_ssh_config_path();
|
||||
fs::read_to_string(path)
|
||||
}
|
||||
|
||||
fn parse_ssh_profiles(config: &str) -> Vec<SshProfile> {
|
||||
let mut profiles = Vec::new();
|
||||
let mut lines = config.lines();
|
||||
while let Some(line) = lines.next() {
|
||||
if line.starts_with("Host ") {
|
||||
let host = line[5..].trim().to_string();
|
||||
let mut hostname = String::new();
|
||||
let mut user = String::new();
|
||||
let mut port = 22;
|
||||
let mut identity_file = None;
|
||||
let mut password_authentication = None;
|
||||
|
||||
while let Some(next_line) = lines.next() {
|
||||
let trimmed = next_line.trim();
|
||||
if trimmed.starts_with("HostName ") {
|
||||
hostname = trimmed[9..].to_string();
|
||||
} else if trimmed.starts_with("User ") {
|
||||
user = trimmed[5..].to_string();
|
||||
} else if trimmed.starts_with("Port ") {
|
||||
port = trimmed[5..].parse::<u16>().unwrap();
|
||||
} else if trimmed.starts_with("IdentityFile ") {
|
||||
identity_file = Some(trimmed[13..].to_string());
|
||||
} else if trimmed.starts_with("PasswordAuthentication ") {
|
||||
let value = trimmed[23..].to_lowercase();
|
||||
match value.as_str() {
|
||||
"yes" => password_authentication = Some(true),
|
||||
"no" => password_authentication = Some(false),
|
||||
_ => {}
|
||||
}
|
||||
} else if trimmed.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
profiles.push(SshProfile {
|
||||
host,
|
||||
hostname,
|
||||
port,
|
||||
user,
|
||||
identity_file,
|
||||
password_authentication,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
profiles
|
||||
}
|
||||
|
||||
fn write_ssh_config(profiles: &[SshProfile]) -> io::Result<()> {
|
||||
let path = get_ssh_config_path();
|
||||
let mut content = String::new();
|
||||
|
||||
for profile in profiles {
|
||||
match (&profile.identity_file, profile.password_authentication) {
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Profile cannot have both identity file and password authentication",
|
||||
));
|
||||
}
|
||||
(None, None) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Profile must have either identity file or password authentication",
|
||||
));
|
||||
}
|
||||
(Some(identity_file), None) => {
|
||||
content.push_str(&format!(
|
||||
"Host {}\n HostName {}\n User {}\n Port {}\n IdentityFile {}\n\n",
|
||||
profile.host, profile.hostname, profile.user, profile.port, identity_file
|
||||
));
|
||||
}
|
||||
(None, Some(password_authentication)) => match password_authentication {
|
||||
true => {
|
||||
content.push_str(&format!(
|
||||
"Host {}\n HostName {}\n User {}\n Port {}\n PasswordAuthentication yes\n\n",
|
||||
profile.host, profile.hostname, profile.user, profile.port
|
||||
));
|
||||
}
|
||||
false => {
|
||||
content.push_str(&format!(
|
||||
"Host {}\n HostName {}\n User {}\n Port {}\n PasswordAuthentication no\n\n",
|
||||
profile.host, profile.hostname, profile.user, profile.port
|
||||
));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fs::write(path, content)
|
||||
}
|
||||
|
||||
fn list_profiles(profiles: &[SshProfile]) {
|
||||
println!("Available SSH profiles:");
|
||||
|
||||
if profiles.is_empty() {
|
||||
println!("No profiles found");
|
||||
return;
|
||||
}
|
||||
|
||||
for profile in profiles {
|
||||
println!(
|
||||
"- {} ({}@{}:{})",
|
||||
profile.host, profile.user, profile.hostname, profile.port
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_profile(profiles: &mut Vec<SshProfile>) {
|
||||
let host: String = Input::new()
|
||||
.with_prompt("Enter Host name")
|
||||
.interact_text()
|
||||
.unwrap();
|
||||
let hostname: String = Input::new()
|
||||
.with_prompt("Enter HostName")
|
||||
.interact_text()
|
||||
.unwrap();
|
||||
let user: String = Input::new()
|
||||
.with_prompt("Enter User")
|
||||
.interact_text()
|
||||
.unwrap();
|
||||
let port: u16 = Input::new()
|
||||
.with_prompt("Enter Port")
|
||||
.default(22)
|
||||
.interact()
|
||||
.unwrap();
|
||||
let identity_file: Option<String> = Input::new()
|
||||
.with_prompt("Enter IdentityFile")
|
||||
.allow_empty(true)
|
||||
.default(String::new())
|
||||
.interact_text()
|
||||
.ok()
|
||||
.map(|s| s.is_empty().not().then(|| s))
|
||||
.flatten();
|
||||
let password_authentication: Option<bool> = Input::new()
|
||||
.with_prompt("Enter PasswordAuthentication")
|
||||
.allow_empty(true)
|
||||
.interact()
|
||||
.ok();
|
||||
|
||||
match (&identity_file, password_authentication) {
|
||||
(Some(_), Some(_)) => {
|
||||
println!("Profile cannot have both identity file and password authentication");
|
||||
return;
|
||||
}
|
||||
(None, None) => {
|
||||
println!("Profile must have either identity file or password authentication");
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
profiles.push(SshProfile {
|
||||
host,
|
||||
hostname,
|
||||
user,
|
||||
port,
|
||||
identity_file,
|
||||
password_authentication,
|
||||
});
|
||||
|
||||
println!("Profile added successfully");
|
||||
}
|
||||
|
||||
fn edit_profile(profiles: &mut Vec<SshProfile>) {
|
||||
let options = profiles
|
||||
.iter()
|
||||
.map(|profile| profile.host.clone())
|
||||
.collect::<Vec<String>>();
|
||||
if let Ok(selection) = Select::new()
|
||||
.with_prompt("Select a profile to edit")
|
||||
.items(&options)
|
||||
.interact()
|
||||
{
|
||||
let profile = &mut profiles[selection];
|
||||
let options = [
|
||||
"Host",
|
||||
"HostName",
|
||||
"User",
|
||||
"Port",
|
||||
"IdentityFile",
|
||||
"PasswordAuthentication",
|
||||
];
|
||||
let choice = Select::new()
|
||||
.with_prompt("Choose a field to edit")
|
||||
.items(&options)
|
||||
.interact()
|
||||
.unwrap();
|
||||
|
||||
match choice {
|
||||
0 => {
|
||||
let host: String = Input::new()
|
||||
.with_prompt("Enter Host name")
|
||||
.default(profile.host.clone())
|
||||
.interact_text()
|
||||
.unwrap();
|
||||
profile.host = host;
|
||||
}
|
||||
1 => {
|
||||
let hostname: String = Input::new()
|
||||
.with_prompt("Enter HostName")
|
||||
.default(profile.hostname.clone())
|
||||
.interact_text()
|
||||
.unwrap();
|
||||
profile.hostname = hostname;
|
||||
}
|
||||
2 => {
|
||||
let user: String = Input::new()
|
||||
.with_prompt("Enter User")
|
||||
.default(profile.user.clone())
|
||||
.interact_text()
|
||||
.unwrap();
|
||||
profile.user = user;
|
||||
}
|
||||
3 => {
|
||||
let port: u16 = Input::new()
|
||||
.with_prompt("Enter Port")
|
||||
.default(profile.port)
|
||||
.interact()
|
||||
.unwrap();
|
||||
profile.port = port;
|
||||
}
|
||||
4 => {
|
||||
if profile.password_authentication.is_some() {
|
||||
println!("Profile already has PasswordAuthentication set");
|
||||
return;
|
||||
}
|
||||
|
||||
let identity_file: Option<String> = Input::new()
|
||||
.with_prompt("Enter IdentityFile")
|
||||
.default(profile.identity_file.clone().unwrap_or_default())
|
||||
.interact_text()
|
||||
.ok()
|
||||
.map(|s| s.is_empty().not().then(|| s))
|
||||
.flatten();
|
||||
profile.identity_file = identity_file;
|
||||
}
|
||||
5 => {
|
||||
if profile.identity_file.is_some() {
|
||||
println!("Profile already has IdentityFile set");
|
||||
return;
|
||||
}
|
||||
|
||||
let value = profile.password_authentication.unwrap_or(false);
|
||||
let password_authentication: Option<bool> = Input::new()
|
||||
.with_prompt("Enter PasswordAuthentication")
|
||||
.default(value)
|
||||
.interact()
|
||||
.ok();
|
||||
profile.password_authentication = password_authentication;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
println!("Profile edited successfully");
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_profile(profiles: &mut Vec<SshProfile>) {
|
||||
let options: Vec<String> = profiles
|
||||
.iter()
|
||||
.map(|profile| profile.host.clone())
|
||||
.collect();
|
||||
if let Ok(selection) = Select::new()
|
||||
.with_prompt("Select a profile to remove")
|
||||
.items(&options)
|
||||
.interact()
|
||||
{
|
||||
profiles.remove(selection);
|
||||
println!("Profile removed successfully");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let config = read_ssh_config().expect("Failed to read SSH config");
|
||||
let mut profiles = parse_ssh_profiles(&config);
|
||||
|
||||
loop {
|
||||
let options = [
|
||||
"List Profiles",
|
||||
"Add Profile",
|
||||
"Edit Profile",
|
||||
"Remove Profile",
|
||||
"Exit",
|
||||
];
|
||||
let choice = Select::new()
|
||||
.with_prompt("Choose an action")
|
||||
.items(&options)
|
||||
.interact()
|
||||
.unwrap();
|
||||
|
||||
match choice {
|
||||
0 => list_profiles(&profiles),
|
||||
1 => {
|
||||
add_profile(&mut profiles);
|
||||
write_ssh_config(&profiles).unwrap();
|
||||
}
|
||||
2 => {
|
||||
edit_profile(&mut profiles);
|
||||
write_ssh_config(&profiles).unwrap();
|
||||
}
|
||||
3 => {
|
||||
remove_profile(&mut profiles);
|
||||
write_ssh_config(&profiles).unwrap();
|
||||
}
|
||||
4 => break,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user