mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Too many things to do here... working on activity manager
This commit is contained in:
106
src/activity_manager.rs
Normal file
106
src/activity_manager.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
//! ## ActivityManager
|
||||||
|
//!
|
||||||
|
//! `activity_manager` is the module which provides run methods and handling for activities
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 Christian Visintin - christian.visintin1997@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of "TermSCP"
|
||||||
|
*
|
||||||
|
* TermSCP is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* TermSCP is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with TermSCP. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
// Deps
|
||||||
|
use crate::filetransfer::FileTransfer;
|
||||||
|
use crate::host::Localhost;
|
||||||
|
use crate::ui::activities::{auth_activity::AuthActivity, auth_activity::ScpProtocol, Activity};
|
||||||
|
use crate::ui::context::Context;
|
||||||
|
|
||||||
|
/// ### NextActivity
|
||||||
|
///
|
||||||
|
/// NextActivity identified the next identity to run once the current has ended
|
||||||
|
pub enum NextActivity {
|
||||||
|
Authentication,
|
||||||
|
FileTransfer,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ### FileTransferParams
|
||||||
|
///
|
||||||
|
/// Holds connection parameters for file transfers
|
||||||
|
struct FileTransferParams {
|
||||||
|
address: String,
|
||||||
|
port: u16,
|
||||||
|
protocol: ScpProtocol,
|
||||||
|
username: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ### ActivityManager
|
||||||
|
///
|
||||||
|
/// The activity manager takes care of running activities and handling them until the application has ended
|
||||||
|
pub struct ActivityManager {
|
||||||
|
context: Context,
|
||||||
|
ftparams: Option<FileTransferParams>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActivityManager {
|
||||||
|
/// ### new
|
||||||
|
///
|
||||||
|
/// Initializes a new Activity Manager
|
||||||
|
pub fn new(client: Box<dyn FileTransfer>, local_dir: &PathBuf) -> Result<ActivityManager, ()> {
|
||||||
|
// Prepare Context
|
||||||
|
let host: Localhost = match Localhost::new(local_dir.clone()) {
|
||||||
|
Ok(h) => h,
|
||||||
|
Err(_) => return Err(()),
|
||||||
|
};
|
||||||
|
let ctx: Context = Context::new(client, host);
|
||||||
|
Ok(ActivityManager {
|
||||||
|
context: ctx,
|
||||||
|
ftparams: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ### set_filetransfer_params
|
||||||
|
///
|
||||||
|
/// Set file transfer params
|
||||||
|
pub fn set_filetransfer_params(
|
||||||
|
&mut self,
|
||||||
|
address: String,
|
||||||
|
port: u16,
|
||||||
|
protocol: ScpProtocol,
|
||||||
|
username: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
) {
|
||||||
|
self.ftparams = Some(FileTransferParams {
|
||||||
|
address: address,
|
||||||
|
port: port,
|
||||||
|
protocol: protocol,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ### run
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Loop for activity manager. You need to provide the activity to start with
|
||||||
|
/// Returns the exitcode
|
||||||
|
pub fn run(&mut self, launch_activity: NextActivity) -> i32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
pub mod activity_manager;
|
||||||
pub mod filetransfer;
|
pub mod filetransfer;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
pub mod host;
|
pub mod host;
|
||||||
|
|||||||
112
src/main.rs
112
src/main.rs
@@ -28,31 +28,44 @@ extern crate getopts;
|
|||||||
// External libs
|
// External libs
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
// Include
|
// Include
|
||||||
|
mod activity_manager;
|
||||||
mod filetransfer;
|
mod filetransfer;
|
||||||
mod fs;
|
mod fs;
|
||||||
mod host;
|
mod host;
|
||||||
|
mod ui;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
// namespaces
|
||||||
|
use activity_manager::{ActivityManager, NextActivity};
|
||||||
|
use filetransfer::{FileTransfer, sftp_transfer::SftpFileTransfer};
|
||||||
|
use ui::activities::auth_activity::ScpProtocol;
|
||||||
|
|
||||||
/// ### print_usage
|
/// ### print_usage
|
||||||
///
|
///
|
||||||
/// Print usage
|
/// Print usage
|
||||||
|
|
||||||
fn print_usage(opts: Options) {
|
fn print_usage(opts: Options) {
|
||||||
let brief = format!("Usage: termscp [Options]... Remote");
|
let brief = format!("Usage: termscp [Options]... [protocol:user@address:port]");
|
||||||
print!("{}", opts.usage(&brief));
|
print!("{}", opts.usage(&brief));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
//Program CLI options
|
//Program CLI options
|
||||||
// TODO: insert opts here
|
let mut address: Option<String> = None; // None
|
||||||
|
let mut port: u16 = 22; // Default port
|
||||||
|
let mut username: Option<String> = None; // Default username
|
||||||
|
let mut password: Option<String> = None; // Default password
|
||||||
|
let mut protocol: ScpProtocol = ScpProtocol::Sftp; // Default protocol
|
||||||
|
let mut ticks: Duration = Duration::from_micros(50);
|
||||||
//Process options
|
//Process options
|
||||||
|
// FIXME: there is no way to provide password from CLI atm
|
||||||
let mut opts = Options::new();
|
let mut opts = Options::new();
|
||||||
// opts.optopt("c", "command", "Specify command to run. Shell returns after running the command", "<command>");
|
opts.optopt("T", "ticks", "Set UI ticks; default 50µs", "<µs>");
|
||||||
// opts.optopt("C", "config", "Specify YAML configuration file", "<config>");
|
|
||||||
// opts.optopt("l", "lang", "Specify shell language", "<ru|рус>");
|
|
||||||
// opts.optopt("s", "shell", "Force the shell binary path", "</bin/bash>");
|
|
||||||
opts.optflag("v", "version", "");
|
opts.optflag("v", "version", "");
|
||||||
opts.optflag("h", "help", "Print this menu");
|
opts.optflag("h", "help", "Print this menu");
|
||||||
let matches = match opts.parse(&args[1..]) {
|
let matches = match opts.parse(&args[1..]) {
|
||||||
@@ -75,6 +88,89 @@ fn main() {
|
|||||||
);
|
);
|
||||||
std::process::exit(255);
|
std::process::exit(255);
|
||||||
}
|
}
|
||||||
// TODO: ...
|
// Match protocol first
|
||||||
std::process::exit(0);
|
if let Some(val) = matches.opt_str("P") {
|
||||||
|
match val.as_str() {
|
||||||
|
"sftp" => {
|
||||||
|
protocol = ScpProtocol::Sftp;
|
||||||
|
// Set port to 22 as default
|
||||||
|
port = 22;
|
||||||
|
// Set default username to current
|
||||||
|
username = Some(whoami::username());
|
||||||
|
}
|
||||||
|
"ftp" => {
|
||||||
|
protocol = ScpProtocol::Ftp;
|
||||||
|
// Set port to 21
|
||||||
|
port = 21;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
eprintln!("Unknown protocol '{}'", val);
|
||||||
|
print_usage(opts);
|
||||||
|
std::process::exit(255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Match ticks
|
||||||
|
if let Some(val) = matches.opt_str("T") {
|
||||||
|
match val.parse::<usize>() {
|
||||||
|
Ok(val) => ticks = Duration::from_micros(val as u64),
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("Ticks is not a number '{}'", val);
|
||||||
|
print_usage(opts);
|
||||||
|
std::process::exit(255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check free args
|
||||||
|
let extra_args: Vec<String> = matches.free.clone();
|
||||||
|
if let Some(remote) = extra_args.get(0) {
|
||||||
|
// Parse address
|
||||||
|
match utils::parse_remote_opt(remote) {
|
||||||
|
Ok((addr, portn, proto, user)) => {
|
||||||
|
// Set params
|
||||||
|
address = Some(addr);
|
||||||
|
port = portn;
|
||||||
|
protocol = proto;
|
||||||
|
username = user;
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Bad address option: {}", err);
|
||||||
|
print_usage(opts);
|
||||||
|
std::process::exit(255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Prepare file transfer
|
||||||
|
let file_transfer: Box<dyn FileTransfer> = match protocol {
|
||||||
|
ScpProtocol::Sftp => Box::new(SftpFileTransfer::new()),
|
||||||
|
_ => {
|
||||||
|
// FIXME: complete with ftp client
|
||||||
|
eprintln!("Unsupported protocol!");
|
||||||
|
std::process::exit(255);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Get working directory
|
||||||
|
let wrkdir: PathBuf = match env::current_dir() {
|
||||||
|
Ok(dir) => dir,
|
||||||
|
Err(_) => PathBuf::from("/")
|
||||||
|
};
|
||||||
|
// Create activity manager
|
||||||
|
let mut manager: ActivityManager = match ActivityManager::new(file_transfer, &wrkdir) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("Invalid directory '{}'", wrkdir.display());
|
||||||
|
std::process::exit(255);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Initialize client if necessary
|
||||||
|
let mut start_activity: NextActivity = NextActivity::Authentication;
|
||||||
|
if let Some(address) = address {
|
||||||
|
// In this case the first activity will be FileTransfer
|
||||||
|
start_activity = NextActivity::FileTransfer;
|
||||||
|
manager.set_filetransfer_params(address, port, protocol, username, password);
|
||||||
|
}
|
||||||
|
// Run
|
||||||
|
let rc: i32 = manager.run(start_activity);
|
||||||
|
// Then return
|
||||||
|
std::process::exit(rc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,14 @@ use super::{Activity, Context};
|
|||||||
// Includes
|
// Includes
|
||||||
use crossterm::event::Event as InputEvent;
|
use crossterm::event::Event as InputEvent;
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
use tui::layout;
|
use tui::{
|
||||||
use tui::style;
|
backend::CrosstermBackend,
|
||||||
use tui::widgets;
|
layout::{Constraint, Direction, Layout},
|
||||||
|
style::{Color, Modifier, Style},
|
||||||
|
text::{Span, Spans, Text},
|
||||||
|
widgets::{Block, Borders, List, ListItem, Paragraph},
|
||||||
|
Terminal,
|
||||||
|
};
|
||||||
|
|
||||||
/// ### InputField
|
/// ### InputField
|
||||||
///
|
///
|
||||||
@@ -52,6 +57,7 @@ enum InputField {
|
|||||||
/// ### ScpProtocol
|
/// ### ScpProtocol
|
||||||
///
|
///
|
||||||
/// ScpProtocol describes the communication protocol selected by the user to communicate with the remote
|
/// ScpProtocol describes the communication protocol selected by the user to communicate with the remote
|
||||||
|
#[derive(std::cmp::PartialEq, std::fmt::Debug)]
|
||||||
pub enum ScpProtocol {
|
pub enum ScpProtocol {
|
||||||
Sftp,
|
Sftp,
|
||||||
Ftp,
|
Ftp,
|
||||||
@@ -147,6 +153,7 @@ impl Activity for AuthActivity {
|
|||||||
}
|
}
|
||||||
// Everything OK, set enter
|
// Everything OK, set enter
|
||||||
self.form_submit = true;
|
self.form_submit = true;
|
||||||
|
popup = Some(format!("Connecting to {}:{}...", self.address, self.port));
|
||||||
}
|
}
|
||||||
KeyCode::Backspace => {
|
KeyCode::Backspace => {
|
||||||
// Pop last char
|
// Pop last char
|
||||||
@@ -225,7 +232,40 @@ impl Activity for AuthActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: draw interface
|
// draw interface
|
||||||
|
context.terminal.draw(|f| {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.margin(2)
|
||||||
|
.constraints(
|
||||||
|
[
|
||||||
|
Constraint::Length(1),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Min(1),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(f.size());
|
||||||
|
// Write header
|
||||||
|
let (header, h_style) = (
|
||||||
|
vec![
|
||||||
|
Span::raw("Press "),
|
||||||
|
Span::styled("<ESC>", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::raw(" to exit, "),
|
||||||
|
Span::styled("<UP,DOWN>", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::raw(" to change input field,"),
|
||||||
|
Span::styled("<ENTER>", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::raw(" to submit form"),
|
||||||
|
],
|
||||||
|
Style::default().add_modifier(Modifier::RAPID_BLINK),
|
||||||
|
);
|
||||||
|
let mut header_text = Text::from(Spans::from(header));
|
||||||
|
header_text.patch_style(h_style);
|
||||||
|
let header_message = Paragraph::new(header_text);
|
||||||
|
f.render_widget(header_message, chunks[0]);
|
||||||
|
// Create input fields
|
||||||
|
// TODO:
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### on_destroy
|
/// ### on_destroy
|
||||||
|
|||||||
Reference in New Issue
Block a user