Handle input in environment
This commit is contained in:
parent
85b0b3dbbf
commit
0632d7b0a9
@ -1,18 +1,19 @@
|
|||||||
use std::env;
|
|
||||||
use std::env::VarError;
|
use std::env::VarError;
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::{env, io};
|
||||||
|
|
||||||
pub trait Environment {
|
pub trait Environment {
|
||||||
fn args_os(&self) -> Vec<OsString>;
|
fn args_os(&self) -> Vec<OsString>;
|
||||||
fn var<K>(&self, key: K) -> Result<String, VarError>
|
fn var<K>(&self, key: K) -> Result<String, VarError>
|
||||||
where
|
where
|
||||||
K: AsRef<OsStr>;
|
K: AsRef<OsStr>;
|
||||||
fn set_var<K, V>(&self, key: K, value: V)
|
fn set_var<K, V>(&mut self, key: K, value: V)
|
||||||
where
|
where
|
||||||
K: AsRef<OsStr>,
|
K: AsRef<OsStr>,
|
||||||
V: AsRef<OsStr>;
|
V: AsRef<OsStr>;
|
||||||
fn get_home_directory(&self) -> Option<PathBuf>;
|
fn get_home_directory(&self) -> Option<PathBuf>;
|
||||||
|
fn read_line(&mut self) -> Result<String, io::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -30,7 +31,7 @@ impl Environment for Prod {
|
|||||||
env::var(key)
|
env::var(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_var<K, V>(&self, key: K, value: V)
|
fn set_var<K, V>(&mut self, key: K, value: V)
|
||||||
where
|
where
|
||||||
K: AsRef<OsStr>,
|
K: AsRef<OsStr>,
|
||||||
V: AsRef<OsStr>
|
V: AsRef<OsStr>
|
||||||
@ -41,4 +42,10 @@ impl Environment for Prod {
|
|||||||
fn get_home_directory(&self) -> Option<PathBuf> {
|
fn get_home_directory(&self) -> Option<PathBuf> {
|
||||||
homedir::my_home().ok().flatten()
|
homedir::my_home().ok().flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_line(&mut self) -> Result<String, io::Error> {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
io::stdin().read_line(&mut buffer)?;
|
||||||
|
Ok(buffer.trim().to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
106
src/main.rs
106
src/main.rs
@ -141,24 +141,6 @@ enum OldVersionPolicy {
|
|||||||
Delete,
|
Delete,
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO IO would also need to be handled by the environment
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! input {
|
|
||||||
($prompt: tt) => {{
|
|
||||||
print!($prompt);
|
|
||||||
io::stdout().flush().expect("failed to flush stdout");
|
|
||||||
let mut buf = String::new();
|
|
||||||
io::stdin()
|
|
||||||
.read_line(&mut buf)
|
|
||||||
.expect("failed to read stdin");
|
|
||||||
buf.trim().to_string()
|
|
||||||
}};
|
|
||||||
() => {
|
|
||||||
input!()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Application<E> {
|
pub struct Application<E> {
|
||||||
pub environment: E,
|
pub environment: E,
|
||||||
@ -168,12 +150,14 @@ impl<E> Application<E>
|
|||||||
where
|
where
|
||||||
E: Environment,
|
E: Environment,
|
||||||
{
|
{
|
||||||
pub fn run(&self) -> Result<(), String> {
|
pub fn run(&mut self) -> Result<(), String> {
|
||||||
let args = Args::try_parse_from(self.environment.args_os()).map_err(|e| e.to_string())?;
|
let args = Args::try_parse_from(self.environment.args_os()).map_err(|e| e.to_string())?;
|
||||||
self.run_with_args(args)
|
self.run_with_args(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_with_args(&self, args: Args) -> Result<(), String> {
|
pub fn run_with_args(&mut self, args: Args) -> Result<(), String> {
|
||||||
|
let env = &mut self.environment;
|
||||||
|
|
||||||
let logger = Logger {
|
let logger = Logger {
|
||||||
//all the below options are conflicting with each other so an if else is fine
|
//all the below options are conflicting with each other so an if else is fine
|
||||||
level: if args.quiet {
|
level: if args.quiet {
|
||||||
@ -216,7 +200,6 @@ where
|
|||||||
} => {
|
} => {
|
||||||
Self::require_non_empty_servers(&servers)?;
|
Self::require_non_empty_servers(&servers)?;
|
||||||
Self::require_non_empty(&files, "files to upload")?;
|
Self::require_non_empty(&files, "files to upload")?;
|
||||||
self.start_ssh_agent(&logger)?;
|
|
||||||
|
|
||||||
//resolve file server
|
//resolve file server
|
||||||
let file_server = match file_server {
|
let file_server = match file_server {
|
||||||
@ -230,6 +213,8 @@ where
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.start_ssh_agent(&logger)?;
|
||||||
|
|
||||||
//make sure files exist
|
//make sure files exist
|
||||||
match &file_server {
|
match &file_server {
|
||||||
Some(file_server) => match &file_server.address {
|
Some(file_server) => match &file_server.address {
|
||||||
@ -448,14 +433,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !no_confirm {
|
if !no_confirm && !self.confirm("Continue?", true) {
|
||||||
match input!("Continue? [Y|n] ").to_lowercase().as_str() {
|
log!(logger, "Aborting...");
|
||||||
"n" | "no" => {
|
return Ok(());
|
||||||
log!(logger, "Aborting...");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for server_actions in actions {
|
for server_actions in actions {
|
||||||
@ -557,7 +537,8 @@ where
|
|||||||
let download_directory = match download_directory {
|
let download_directory = match download_directory {
|
||||||
Some(download_directory) => download_directory,
|
Some(download_directory) => download_directory,
|
||||||
None => {
|
None => {
|
||||||
let home_dir = self.get_home_directory()
|
let home_dir = self
|
||||||
|
.get_home_directory()
|
||||||
.map_err(|e| format!("Missing download-directory: {e}"))?;
|
.map_err(|e| format!("Missing download-directory: {e}"))?;
|
||||||
home_dir.join("Downloads")
|
home_dir.join("Downloads")
|
||||||
}
|
}
|
||||||
@ -588,14 +569,8 @@ where
|
|||||||
download_directory.to_string_lossy()
|
download_directory.to_string_lossy()
|
||||||
);
|
);
|
||||||
|
|
||||||
if !args.quiet {
|
if !args.quiet && self.confirm(format!("{duplication_notification}. Do you want to replace it?"), false) {
|
||||||
match input!("{duplication_notification}. Do you want to replace it? [N|y] ")
|
break 'duplicate_check;
|
||||||
.to_lowercase()
|
|
||||||
.as_str()
|
|
||||||
{
|
|
||||||
"y" | "yes" => break 'duplicate_check,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
@ -677,8 +652,8 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_ssh_agent(&self, logger: &Logger) -> Result<(), String> {
|
fn start_ssh_agent(&mut self, logger: &Logger) -> Result<(), String> {
|
||||||
let env = &self.environment;
|
let env = &mut self.environment;
|
||||||
|
|
||||||
//start the ssh agent
|
//start the ssh agent
|
||||||
let agent_output = ShellCmd::new("ssh-agent")
|
let agent_output = ShellCmd::new("ssh-agent")
|
||||||
@ -711,9 +686,36 @@ where
|
|||||||
.map_err(|_| format!("Missing environment variable {}", SERVERS_ENV_VAR))
|
.map_err(|_| format!("Missing environment variable {}", SERVERS_ENV_VAR))
|
||||||
.and_then(|value| parse_server_configuration(&value, || self.get_home_directory()))
|
.and_then(|value| parse_server_configuration(&value, || self.get_home_directory()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_home_directory(&self) -> Result<PathBuf, String> {
|
fn get_home_directory(&self) -> Result<PathBuf, String> {
|
||||||
self.environment.get_home_directory().ok_or("Failed to find your home directory".to_string())
|
self
|
||||||
|
.environment
|
||||||
|
.get_home_directory()
|
||||||
|
.ok_or("Failed to find your home directory".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm<S>(&mut self, prompt: S, default_value: bool) -> bool
|
||||||
|
where
|
||||||
|
S: ToString,
|
||||||
|
{
|
||||||
|
loop {
|
||||||
|
print!(
|
||||||
|
"{}[{}]",
|
||||||
|
prompt.to_string(),
|
||||||
|
if default_value { "Y|n" } else { "y|N" }
|
||||||
|
);
|
||||||
|
io::stdout().flush().expect("failed to flush stdout");
|
||||||
|
let line = self
|
||||||
|
.environment
|
||||||
|
.read_line()
|
||||||
|
.expect("Failed to read console input");
|
||||||
|
match line.to_lowercase().as_str() {
|
||||||
|
"" => return default_value,
|
||||||
|
"y" | "yes" => return true,
|
||||||
|
"n" | "no" => return false,
|
||||||
|
_ => println!("Invalid input, please choose one of the provided options"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,12 +738,22 @@ fn osstring_from_ssh_output(output: Vec<u8>) -> OsString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_server_configuration<F>(config_str: &str, get_home_directory: F) -> Result<Vec<Server>, String> where F: Fn() -> Result<PathBuf, String> {
|
fn parse_server_configuration<F>(
|
||||||
|
config_str: &str,
|
||||||
|
get_home_directory: F,
|
||||||
|
) -> Result<Vec<Server>, String>
|
||||||
|
where
|
||||||
|
F: Fn() -> Result<PathBuf, String>,
|
||||||
|
{
|
||||||
config_str
|
config_str
|
||||||
.split(',')
|
.split(',')
|
||||||
.map(|server_entry| {
|
.map(|server_entry| {
|
||||||
Server::from_str(server_entry, RelativeLocalPathAnker::Home, &get_home_directory)
|
Server::from_str(
|
||||||
.map_err(|e| format!("Invalid server entry '{server_entry}': {e}"))
|
server_entry,
|
||||||
|
RelativeLocalPathAnker::Home,
|
||||||
|
&get_home_directory,
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("Invalid server entry '{server_entry}': {e}"))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -755,8 +767,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_server_configuration() {
|
fn test_parse_server_configuration() {
|
||||||
let servers =
|
let servers = parse_server_configuration("foo:bar,.:fizz/buzz", || Ok(PathBuf::from("/test")))
|
||||||
parse_server_configuration("foo:bar,.:fizz/buzz", || Ok(PathBuf::from("/test"))).expect("valid server configuration");
|
.expect("valid server configuration");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vec![
|
vec![
|
||||||
Server {
|
Server {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user