mirror of
https://github.com/veeso/termscp.git
synced 2026-09-12 07:06:02 -07:00
Use a regex to parse the remote host args
This commit is contained in:
@@ -23,6 +23,7 @@ Released on ???
|
|||||||
- Enhancements:
|
- Enhancements:
|
||||||
- Default choice for deleting file set to "NO" (way too easy to delete files by mistake)
|
- Default choice for deleting file set to "NO" (way too easy to delete files by mistake)
|
||||||
- Added CLI options to set starting workind directory on both local and remote hosts
|
- Added CLI options to set starting workind directory on both local and remote hosts
|
||||||
|
- Parse remote host now uses a Regex to gather parts (increased stability).
|
||||||
- Now bookmarks and recents are sorted in the UI (bookmarks are sorted by name; recents are sorted by connection datetime)
|
- Now bookmarks and recents are sorted in the UI (bookmarks are sorted by name; recents are sorted by connection datetime)
|
||||||
|
|
||||||
## 0.3.2
|
## 0.3.2
|
||||||
|
|||||||
@@ -53,16 +53,16 @@ TermSCP is basically a porting of WinSCP to terminal. So basically is a terminal
|
|||||||
|
|
||||||
### Why TermSCP 🤔
|
### Why TermSCP 🤔
|
||||||
|
|
||||||
It happens quite often to me, when using SCP at work to forget the path of a file on a remote machine, which forces me then to connect through SSH, gather the file path and finally download it through SCP. I could use WinSCP, but I use Linux and I pratically use the terminal for everything, so I wanted something like WinSCP on my terminal. Yeah, I know there is midnight commander too, but actually I don't like it very much tbh (and hasn't a decent support for scp).
|
It happens quite often to me, when using SCP at work to forget the path of a file on a remote machine, which forces me to connect through SSH, gather the file path and finally download it through SCP. I could use WinSCP, but I use Linux and I pratically use the terminal for everything, so I wanted something like WinSCP on my terminal. Yeah, I know there is midnight commander too, but actually I don't like it very much tbh (and hasn't a decent support for scp).
|
||||||
|
|
||||||
## Features 🎁
|
## Features 🎁
|
||||||
|
|
||||||
- Different communication protocols
|
- Different communication protocols support
|
||||||
- SFTP
|
- SFTP
|
||||||
- SCP
|
- SCP
|
||||||
- FTP and FTPS
|
- FTP and FTPS
|
||||||
- Compatible with Windows, Linux, BSD and MacOS
|
- Compatible with Windows, Linux, BSD and MacOS
|
||||||
- Practical user interface to explore and operate on the remote and on the local machine file system
|
- Handy user interface to explore and operate on the remote and on the local machine file system
|
||||||
- Bookmarks and recent connections can be saved to access quickly to your favourite hosts
|
- Bookmarks and recent connections can be saved to access quickly to your favourite hosts
|
||||||
- Supports text editors to view and edit text files
|
- Supports text editors to view and edit text files
|
||||||
- Supports both SFTP/SCP authentication through SSH keys and username/password
|
- Supports both SFTP/SCP authentication through SSH keys and username/password
|
||||||
@@ -163,7 +163,7 @@ brew install termscp
|
|||||||
|
|
||||||
TermSCP can be started with the following options:
|
TermSCP can be started with the following options:
|
||||||
|
|
||||||
`termscp [options]... [protocol://user@address:port] [local-wrkdir] [remote-wrkdir]`
|
`termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]`
|
||||||
|
|
||||||
- `-P, --password <password>` if address is provided, password will be this argument
|
- `-P, --password <password>` if address is provided, password will be this argument
|
||||||
- `-v, --version` Print version info
|
- `-v, --version` Print version info
|
||||||
@@ -173,14 +173,14 @@ TermSCP can be started in two different mode, if no extra arguments is provided,
|
|||||||
|
|
||||||
Alternatively, the user can provide an address as argument to skip the authentication form and starting directly the connection to the remote server.
|
Alternatively, the user can provide an address as argument to skip the authentication form and starting directly the connection to the remote server.
|
||||||
|
|
||||||
If address argument is provided you can also provide the start working directory for both local and remote hosts.
|
If address argument is provided you can also provide the start working directory for local host
|
||||||
|
|
||||||
### Address argument 🌎
|
### Address argument 🌎
|
||||||
|
|
||||||
The address argument has the following syntax:
|
The address argument has the following syntax:
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
[protocol]://[username@]<address>[:port]
|
[protocol://][username@]<address>[:port][:wrkdir]
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's see some example of this particular syntax, since it's very comfortable and you'll probably going to use this instead of the other one...
|
Let's see some example of this particular syntax, since it's very comfortable and you'll probably going to use this instead of the other one...
|
||||||
@@ -203,6 +203,12 @@ Let's see some example of this particular syntax, since it's very comfortable an
|
|||||||
termscp scp://omar@192.168.1.31:4022
|
termscp scp://omar@192.168.1.31:4022
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Connect using scp to 192.168.1.31, port is 4022; username is `omar`. You will start in directory `/tmp`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
termscp scp://omar@192.168.1.31:4022:/tmp
|
||||||
|
```
|
||||||
|
|
||||||
#### How Password can be provided 🔐
|
#### How Password can be provided 🔐
|
||||||
|
|
||||||
You have probably noticed, that, when providing the address as argument, there's no way to provide the password.
|
You have probably noticed, that, when providing the address as argument, there's no way to provide the password.
|
||||||
|
|||||||
+4
-7
@@ -59,7 +59,7 @@ use filetransfer::FileTransferProtocol;
|
|||||||
|
|
||||||
fn print_usage(opts: Options) {
|
fn print_usage(opts: Options) {
|
||||||
let brief = String::from(
|
let brief = String::from(
|
||||||
"Usage: termscp [options]... [protocol://user@address:port] [local-wrkdir] [remote-wrkdir]",
|
"Usage: termscp [options]... [protocol://user@address:port:wrkdir] [local-wrkdir]",
|
||||||
);
|
);
|
||||||
print!("{}", opts.usage(&brief));
|
print!("{}", opts.usage(&brief));
|
||||||
println!("\nPlease, report issues to <https://github.com/veeso/termscp>");
|
println!("\nPlease, report issues to <https://github.com/veeso/termscp>");
|
||||||
@@ -72,6 +72,7 @@ fn main() {
|
|||||||
let mut port: u16 = 22; // Default port
|
let mut port: u16 = 22; // Default port
|
||||||
let mut username: Option<String> = None; // Default username
|
let mut username: Option<String> = None; // Default username
|
||||||
let mut password: Option<String> = None; // Default password
|
let mut password: Option<String> = None; // Default password
|
||||||
|
let mut remote_wrkdir: Option<PathBuf> = None;
|
||||||
let mut protocol: FileTransferProtocol = FileTransferProtocol::Sftp; // Default protocol
|
let mut protocol: FileTransferProtocol = FileTransferProtocol::Sftp; // Default protocol
|
||||||
let mut ticks: Duration = Duration::from_millis(10);
|
let mut ticks: Duration = Duration::from_millis(10);
|
||||||
//Process options
|
//Process options
|
||||||
@@ -126,12 +127,13 @@ fn main() {
|
|||||||
if let Some(remote) = extra_args.get(0) {
|
if let Some(remote) = extra_args.get(0) {
|
||||||
// Parse address
|
// Parse address
|
||||||
match utils::parser::parse_remote_opt(remote) {
|
match utils::parser::parse_remote_opt(remote) {
|
||||||
Ok((addr, portn, proto, user)) => {
|
Ok((addr, portn, proto, user, wrkdir)) => {
|
||||||
// Set params
|
// Set params
|
||||||
address = Some(addr);
|
address = Some(addr);
|
||||||
port = portn;
|
port = portn;
|
||||||
protocol = proto;
|
protocol = proto;
|
||||||
username = user;
|
username = user;
|
||||||
|
remote_wrkdir = wrkdir;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Bad address option: {}", err);
|
eprintln!("Bad address option: {}", err);
|
||||||
@@ -149,11 +151,6 @@ fn main() {
|
|||||||
std::process::exit(255);
|
std::process::exit(255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remote directory
|
|
||||||
let remote_wrkdir: Option<PathBuf> = match extra_args.get(2) {
|
|
||||||
Some(p) => Some(PathBuf::from(p)),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
// Get working directory
|
// Get working directory
|
||||||
let wrkdir: PathBuf = match env::current_dir() {
|
let wrkdir: PathBuf = match env::current_dir() {
|
||||||
Ok(dir) => dir,
|
Ok(dir) => dir,
|
||||||
|
|||||||
+202
-108
@@ -25,29 +25,47 @@
|
|||||||
|
|
||||||
// Dependencies
|
// Dependencies
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
|
extern crate regex;
|
||||||
extern crate whoami;
|
extern crate whoami;
|
||||||
|
|
||||||
// Locals
|
// Locals
|
||||||
use crate::filetransfer::FileTransferProtocol;
|
use crate::filetransfer::FileTransferProtocol;
|
||||||
|
#[cfg(not(test))] // NOTE: don't use configuration during tests
|
||||||
use crate::system::config_client::ConfigClient;
|
use crate::system::config_client::ConfigClient;
|
||||||
|
#[cfg(not(test))] // NOTE: don't use configuration during tests
|
||||||
use crate::system::environment;
|
use crate::system::environment;
|
||||||
|
|
||||||
// Ext
|
// Ext
|
||||||
use chrono::format::ParseError;
|
use chrono::format::ParseError;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
use regex::Regex;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
// Regex
|
||||||
|
lazy_static! {
|
||||||
|
/**
|
||||||
|
* Regex matches:
|
||||||
|
* - group 1: Some(protocol) | None
|
||||||
|
* - group 2: Some(user) | None
|
||||||
|
* - group 3: Address
|
||||||
|
* - group 4: Some(port) | None
|
||||||
|
* - group 5: Some(path) | None
|
||||||
|
*/
|
||||||
|
static ref REMOTE_OPT_REGEX: Regex = Regex::new(r"(?:([a-z]+)://)?(?:([^@]+)@)?(?:([^:]+))(?::((?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])))?(?::([^:]+))?").ok().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/// ### parse_remote_opt
|
/// ### parse_remote_opt
|
||||||
///
|
///
|
||||||
/// Parse remote option string. Returns in case of success a tuple made of (address, port, protocol, username)
|
/// Parse remote option string. Returns in case of success a tuple made of (address, port, protocol, username, wrkdir)
|
||||||
/// For ssh if username is not provided, current user will be used.
|
/// For ssh if username is not provided, current user will be used.
|
||||||
/// In case of error, message is returned
|
/// In case of error, message is returned
|
||||||
/// If port is missing default port will be used for each protocol
|
/// If port is missing default port will be used for each protocol
|
||||||
/// SFTP => 22
|
/// SFTP => 22
|
||||||
/// FTP => 21
|
/// FTP => 21
|
||||||
/// The option string has the following syntax
|
/// The option string has the following syntax
|
||||||
/// [protocol]://[username]@{address}:[port]
|
/// [protocol://][username@]{address}[:port][:path]
|
||||||
/// The only argument which is mandatory is address
|
/// The only argument which is mandatory is address
|
||||||
/// NOTE: possible strings
|
/// NOTE: possible strings
|
||||||
/// - 172.26.104.1
|
/// - 172.26.104.1
|
||||||
@@ -59,12 +77,18 @@ use std::time::{Duration, SystemTime};
|
|||||||
///
|
///
|
||||||
pub fn parse_remote_opt(
|
pub fn parse_remote_opt(
|
||||||
remote: &str,
|
remote: &str,
|
||||||
) -> Result<(String, u16, FileTransferProtocol, Option<String>), String> {
|
) -> Result<
|
||||||
let mut wrkstr: String = remote.to_string();
|
(
|
||||||
let address: String;
|
String,
|
||||||
let mut port: u16 = 22;
|
u16,
|
||||||
let mut username: Option<String> = None;
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
),
|
||||||
|
String,
|
||||||
|
> {
|
||||||
// Set protocol to default protocol
|
// Set protocol to default protocol
|
||||||
|
#[cfg(not(test))] // NOTE: don't use configuration during tests
|
||||||
let mut protocol: FileTransferProtocol = match environment::init_config_dir() {
|
let mut protocol: FileTransferProtocol = match environment::init_config_dir() {
|
||||||
Ok(p) => match p {
|
Ok(p) => match p {
|
||||||
Some(p) => {
|
Some(p) => {
|
||||||
@@ -79,71 +103,59 @@ pub fn parse_remote_opt(
|
|||||||
},
|
},
|
||||||
Err(_) => FileTransferProtocol::Sftp,
|
Err(_) => FileTransferProtocol::Sftp,
|
||||||
};
|
};
|
||||||
// Split string by '://'
|
#[cfg(test)] // NOTE: during test set protocol just to Sftp
|
||||||
let tokens: Vec<&str> = wrkstr.split("://").collect();
|
let mut protocol: FileTransferProtocol = FileTransferProtocol::Sftp;
|
||||||
// If length is > 1, then token[0] is protocol
|
// Match against regex
|
||||||
match tokens.len() {
|
match REMOTE_OPT_REGEX.captures(remote) {
|
||||||
1 => {}
|
Some(groups) => {
|
||||||
2 => {
|
// Match protocol
|
||||||
// Parse protocol
|
let mut port: u16 = 22;
|
||||||
let (m_protocol, m_port) = match FileTransferProtocol::from_str(tokens[0]) {
|
if let Some(group) = groups.get(1) {
|
||||||
Ok(proto) => match proto {
|
// Set protocol from group
|
||||||
FileTransferProtocol::Ftp(_) => (proto, 21),
|
let (m_protocol, m_port) = match FileTransferProtocol::from_str(group.as_str()) {
|
||||||
FileTransferProtocol::Scp => (proto, 22),
|
Ok(proto) => match proto {
|
||||||
FileTransferProtocol::Sftp => (proto, 22),
|
FileTransferProtocol::Ftp(_) => (proto, 21),
|
||||||
|
FileTransferProtocol::Scp => (proto, 22),
|
||||||
|
FileTransferProtocol::Sftp => (proto, 22),
|
||||||
|
},
|
||||||
|
Err(_) => return Err(format!("Unknown protocol \"{}\"", group.as_str())),
|
||||||
|
};
|
||||||
|
// NOTE: tuple destructuring assignment is not supported yet :(
|
||||||
|
protocol = m_protocol;
|
||||||
|
port = m_port;
|
||||||
|
}
|
||||||
|
// Match user
|
||||||
|
let username: Option<String> = match groups.get(2) {
|
||||||
|
Some(group) => Some(group.as_str().to_string()),
|
||||||
|
None => match protocol {
|
||||||
|
// If group is empty, set to current user
|
||||||
|
FileTransferProtocol::Scp | FileTransferProtocol::Sftp => {
|
||||||
|
Some(whoami::username())
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
},
|
},
|
||||||
Err(_) => return Err(format!("Unknown protocol '{}'", tokens[0])),
|
|
||||||
};
|
};
|
||||||
protocol = m_protocol;
|
// Get address
|
||||||
port = m_port;
|
let address: String = match groups.get(3) {
|
||||||
wrkstr = String::from(tokens[1]); // Wrkstr becomes tokens[1]
|
Some(group) => group.as_str().to_string(),
|
||||||
}
|
None => return Err(String::from("Missing address")),
|
||||||
_ => return Err(String::from("Bad syntax")), // Too many tokens...
|
|
||||||
}
|
|
||||||
// Set username to default if sftp or scp
|
|
||||||
if matches!(
|
|
||||||
protocol,
|
|
||||||
FileTransferProtocol::Sftp | FileTransferProtocol::Scp
|
|
||||||
) {
|
|
||||||
// Set username to current username
|
|
||||||
username = Some(whoami::username());
|
|
||||||
}
|
|
||||||
// Split wrkstring by '@'
|
|
||||||
let tokens: Vec<&str> = wrkstr.split('@').collect();
|
|
||||||
match tokens.len() {
|
|
||||||
1 => {}
|
|
||||||
2 => {
|
|
||||||
// Username is first token
|
|
||||||
username = Some(String::from(tokens[0]));
|
|
||||||
// Update wrkstr
|
|
||||||
wrkstr = String::from(tokens[1]);
|
|
||||||
}
|
|
||||||
_ => return Err(String::from("Bad syntax")), // Too many tokens...
|
|
||||||
}
|
|
||||||
// Split wrkstring by ':'
|
|
||||||
let tokens: Vec<&str> = wrkstr.split(':').collect();
|
|
||||||
match tokens.len() {
|
|
||||||
1 => {
|
|
||||||
// Address is wrkstr
|
|
||||||
address = wrkstr.clone();
|
|
||||||
}
|
|
||||||
2 => {
|
|
||||||
// Address is first token
|
|
||||||
address = String::from(tokens[0]);
|
|
||||||
// Port is second str
|
|
||||||
port = match tokens[1].parse::<u16>() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => {
|
|
||||||
return Err(format!(
|
|
||||||
"Port must be a number in range [0-65535], but is '{}'",
|
|
||||||
tokens[1]
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
// Get port
|
||||||
|
if let Some(group) = groups.get(4) {
|
||||||
|
port = match group.as_str().parse::<u16>() {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(err) => return Err(format!("Bad port \"{}\": {}", group.as_str(), err)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Get workdir
|
||||||
|
let wrkdir: Option<PathBuf> = match groups.get(5) {
|
||||||
|
Some(group) => Some(PathBuf::from(group.as_str())),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
Ok((address, port, protocol, username, wrkdir))
|
||||||
}
|
}
|
||||||
_ => return Err(String::from("Bad syntax")), // Too many tokens...
|
None => Err(String::from("Bad remote host syntax!")),
|
||||||
}
|
}
|
||||||
Ok((address, port, protocol, username))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### parse_lstime
|
/// ### parse_lstime
|
||||||
@@ -205,90 +217,172 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_utils_parse_remote_opt() {
|
fn test_utils_parse_remote_opt() {
|
||||||
// Base case
|
// Base case
|
||||||
let result: (String, u16, FileTransferProtocol, Option<String>) =
|
let result: (
|
||||||
parse_remote_opt(&String::from("172.26.104.1"))
|
String,
|
||||||
.ok()
|
u16,
|
||||||
.unwrap();
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("172.26.104.1"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.0, String::from("172.26.104.1"));
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
assert_eq!(result.1, 22);
|
assert_eq!(result.1, 22);
|
||||||
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
||||||
assert!(result.3.is_some());
|
assert!(result.3.is_some());
|
||||||
// User case
|
// User case
|
||||||
let result: (String, u16, FileTransferProtocol, Option<String>) =
|
let result: (
|
||||||
parse_remote_opt(&String::from("root@172.26.104.1"))
|
String,
|
||||||
.ok()
|
u16,
|
||||||
.unwrap();
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("root@172.26.104.1"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.0, String::from("172.26.104.1"));
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
assert_eq!(result.1, 22);
|
assert_eq!(result.1, 22);
|
||||||
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
||||||
assert_eq!(result.3.unwrap(), String::from("root"));
|
assert_eq!(result.3.unwrap(), String::from("root"));
|
||||||
|
assert!(result.4.is_none());
|
||||||
// User + port
|
// User + port
|
||||||
let result: (String, u16, FileTransferProtocol, Option<String>) =
|
let result: (
|
||||||
parse_remote_opt(&String::from("root@172.26.104.1:8022"))
|
String,
|
||||||
.ok()
|
u16,
|
||||||
.unwrap();
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("root@172.26.104.1:8022"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.0, String::from("172.26.104.1"));
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
assert_eq!(result.1, 8022);
|
assert_eq!(result.1, 8022);
|
||||||
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
||||||
assert_eq!(result.3.unwrap(), String::from("root"));
|
assert_eq!(result.3.unwrap(), String::from("root"));
|
||||||
|
assert!(result.4.is_none());
|
||||||
// Port only
|
// Port only
|
||||||
let result: (String, u16, FileTransferProtocol, Option<String>) =
|
let result: (
|
||||||
parse_remote_opt(&String::from("172.26.104.1:4022"))
|
String,
|
||||||
.ok()
|
u16,
|
||||||
.unwrap();
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("172.26.104.1:4022"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.0, String::from("172.26.104.1"));
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
assert_eq!(result.1, 4022);
|
assert_eq!(result.1, 4022);
|
||||||
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
||||||
assert!(result.3.is_some());
|
assert!(result.3.is_some());
|
||||||
|
assert!(result.4.is_none());
|
||||||
// Protocol
|
// Protocol
|
||||||
let result: (String, u16, FileTransferProtocol, Option<String>) =
|
let result: (
|
||||||
parse_remote_opt(&String::from("ftp://172.26.104.1"))
|
String,
|
||||||
.ok()
|
u16,
|
||||||
.unwrap();
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("ftp://172.26.104.1"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.0, String::from("172.26.104.1"));
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
assert_eq!(result.1, 21); // Fallback to ftp default
|
assert_eq!(result.1, 21); // Fallback to ftp default
|
||||||
assert_eq!(result.2, FileTransferProtocol::Ftp(false));
|
assert_eq!(result.2, FileTransferProtocol::Ftp(false));
|
||||||
assert!(result.3.is_none()); // Doesn't fall back
|
assert!(result.3.is_none()); // Doesn't fall back
|
||||||
// Protocol
|
assert!(result.4.is_none());
|
||||||
let result: (String, u16, FileTransferProtocol, Option<String>) =
|
// Protocol
|
||||||
parse_remote_opt(&String::from("sftp://172.26.104.1"))
|
let result: (
|
||||||
.ok()
|
String,
|
||||||
.unwrap();
|
u16,
|
||||||
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("sftp://172.26.104.1"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.0, String::from("172.26.104.1"));
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
assert_eq!(result.1, 22); // Fallback to sftp default
|
assert_eq!(result.1, 22); // Fallback to sftp default
|
||||||
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
||||||
assert!(result.3.is_some()); // Doesn't fall back
|
assert!(result.3.is_some()); // Doesn't fall back
|
||||||
let result: (String, u16, FileTransferProtocol, Option<String>) =
|
assert!(result.4.is_none());
|
||||||
parse_remote_opt(&String::from("scp://172.26.104.1"))
|
let result: (
|
||||||
.ok()
|
String,
|
||||||
.unwrap();
|
u16,
|
||||||
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("scp://172.26.104.1"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.0, String::from("172.26.104.1"));
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
assert_eq!(result.1, 22); // Fallback to scp default
|
assert_eq!(result.1, 22); // Fallback to scp default
|
||||||
assert_eq!(result.2, FileTransferProtocol::Scp);
|
assert_eq!(result.2, FileTransferProtocol::Scp);
|
||||||
assert!(result.3.is_some()); // Doesn't fall back
|
assert!(result.3.is_some()); // Doesn't fall back
|
||||||
// Protocol + user
|
assert!(result.4.is_none());
|
||||||
let result: (String, u16, FileTransferProtocol, Option<String>) =
|
// Protocol + user
|
||||||
parse_remote_opt(&String::from("ftps://anon@172.26.104.1"))
|
let result: (
|
||||||
.ok()
|
String,
|
||||||
.unwrap();
|
u16,
|
||||||
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("ftps://anon@172.26.104.1"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.0, String::from("172.26.104.1"));
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
assert_eq!(result.1, 21); // Fallback to ftp default
|
assert_eq!(result.1, 21); // Fallback to ftp default
|
||||||
assert_eq!(result.2, FileTransferProtocol::Ftp(true));
|
assert_eq!(result.2, FileTransferProtocol::Ftp(true));
|
||||||
assert_eq!(result.3.unwrap(), String::from("anon"));
|
assert_eq!(result.3.unwrap(), String::from("anon"));
|
||||||
|
assert!(result.4.is_none());
|
||||||
|
// Path
|
||||||
|
let result: (
|
||||||
|
String,
|
||||||
|
u16,
|
||||||
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("root@172.26.104.1:8022:/var"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
|
assert_eq!(result.1, 8022);
|
||||||
|
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
||||||
|
assert_eq!(result.3.unwrap(), String::from("root"));
|
||||||
|
assert_eq!(result.4.unwrap(), PathBuf::from("/var"));
|
||||||
|
// Port only
|
||||||
|
let result: (
|
||||||
|
String,
|
||||||
|
u16,
|
||||||
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("172.26.104.1:home"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
|
assert_eq!(result.1, 22);
|
||||||
|
assert_eq!(result.2, FileTransferProtocol::Sftp);
|
||||||
|
assert!(result.3.is_some());
|
||||||
|
assert_eq!(result.4.unwrap(), PathBuf::from("home"));
|
||||||
// All together now
|
// All together now
|
||||||
let result: (String, u16, FileTransferProtocol, Option<String>) =
|
let result: (
|
||||||
parse_remote_opt(&String::from("ftp://anon@172.26.104.1:8021"))
|
String,
|
||||||
.ok()
|
u16,
|
||||||
.unwrap();
|
FileTransferProtocol,
|
||||||
|
Option<String>,
|
||||||
|
Option<PathBuf>,
|
||||||
|
) = parse_remote_opt(&String::from("ftp://anon@172.26.104.1:8021:/tmp"))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(result.0, String::from("172.26.104.1"));
|
assert_eq!(result.0, String::from("172.26.104.1"));
|
||||||
assert_eq!(result.1, 8021); // Fallback to ftp default
|
assert_eq!(result.1, 8021); // Fallback to ftp default
|
||||||
assert_eq!(result.2, FileTransferProtocol::Ftp(false));
|
assert_eq!(result.2, FileTransferProtocol::Ftp(false));
|
||||||
assert_eq!(result.3.unwrap(), String::from("anon"));
|
assert_eq!(result.3.unwrap(), String::from("anon"));
|
||||||
|
assert_eq!(result.4.unwrap(), PathBuf::from("/tmp"));
|
||||||
// bad syntax
|
// bad syntax
|
||||||
assert!(parse_remote_opt(&String::from("://172.26.104.1")).is_err()); // Missing protocol
|
|
||||||
assert!(parse_remote_opt(&String::from("omar://172.26.104.1")).is_err()); // Bad protocol
|
assert!(parse_remote_opt(&String::from("omar://172.26.104.1")).is_err()); // Bad protocol
|
||||||
assert!(parse_remote_opt(&String::from("172.26.104.1:abc")).is_err()); // Bad port
|
assert!(parse_remote_opt(&String::from("omar://172.26.104.1:650000")).is_err());
|
||||||
|
// Bad port
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user