1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
use caesar_core::relay;
use caesar_core::sender;
use caesar_core::{receiver, sender::util::generate_random_name};
use clap::{Parser, Subcommand};
use std::{env, sync::Arc};
use tracing::debug;
use crate::config::GLOBAL_CONFIG;
/// Struct representing the command line arguments parsed by clap.
///
/// It uses the clap library to define the command line arguments and their
/// attributes. The version of the application is obtained from the cargo.toml
/// file.
///
/// The `command` field is an optional subcommand. It is represented by the
/// `Commands` enum which defines the different subcommands that can be used.
#[derive(Parser, Debug)]
#[command(version = env!("CARGO_PKG_VERSION"), about = "Send and receive files securely")]
#[command(long_about = None)]
pub struct Args {
/// The subcommand to run.
///
/// It is an optional field. If it is not provided, the program will run without
/// any specific subcommand.
#[command(subcommand)]
pub command: Option<Commands>,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
/// Send files to the receiver or relay server
Send {
/// Address of the relay server. Accepted formats are: 127.0.0.1:8080, [::1]:8080, example.com
#[arg(short, long)]
relay: Option<String>,
/// Path to file(s)
#[arg(value_name = "FILES")]
files: Vec<String>,
},
/// Receives Files from the sender with the matching password
Receive {
/// Address of the relay server. Accepted formats are: 127.0.0.1:8080, [::1]:8080, example.com
#[arg(short, long)]
relay: Option<String>,
/// Name of Transfer to download files
#[arg(value_name = "Transfer_Name")]
name: String,
},
/// Start a relay server
Serve {
/// Port to run the relay server on
#[arg(short, long)]
port: Option<i32>,
/// The Listen address to run the relay server on
#[arg(short, long)]
listen_address: Option<String>,
},
}
/// Default implementation of the `Args` struct.
///
/// This implementation uses the `new` method to create a new instance of `Args`.
impl Default for Args {
/// Creates a new instance of `Args` by calling the `new` method.
///
/// # Returns
///
/// A new instance of `Args`.
fn default() -> Self {
Self::new()
}
}
/// Struct representing the parsed command line arguments.
///
/// This struct implements the `Default` trait to create a new instance of `Args` by calling the
/// `new` method.
///
/// The `run` method is used to execute the corresponding command based on the parsed arguments.
impl Args {
/// Creates a new instance of `Args` by calling the `parse` method.
pub fn new() -> Self {
Self::parse()
}
/// Executes the corresponding command based on the parsed arguments.
///
/// This method takes no parameters.
///
/// # Returns
///
/// A `Result` that either returns `Ok(())` indicating successful execution or an `Err`
/// indicating an error.
pub async fn run(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Retrieve the global configuration
let cfg = &GLOBAL_CONFIG;
debug!("args: {:#?}", self);
// Match on the `command` field of `Args` to execute the corresponding command
match &self.command {
// Command to send files to the receiver or relay server
Some(Commands::Send { relay, files }) => {
// Create a string representation of the relay address
let relay_string: String = relay.as_deref().unwrap_or(&cfg.app_origin).to_string();
// Create Arc wrappers for the relay address and file paths
let relay_arc = Arc::new(relay_string);
let files_arc = Arc::new(files.to_vec());
// Generate a random name
let rand_name = generate_random_name();
// Start the sender with the generated name, relay address, and file paths
sender::start_sender(rand_name, relay_arc, files_arc).await;
}
// Command to receive files from the sender with the matching password
Some(Commands::Receive {
relay,
name,
}) => {
// Print the received transfer name
println!("Receive for {name:?}");
// Start the receiver with the current directory, relay address, and transfer name
let _ = receiver::start_receiver(
".".to_string(),
relay.as_deref().unwrap_or(&cfg.app_origin),
name,
)
.await;
}
// Command to start a relay server
Some(Commands::Serve {
port,
listen_address,
}) => {
// Create a string representation of the listen address
let address: String = listen_address
.as_deref()
.unwrap_or(&cfg.app_host)
.to_string();
// Create an integer representation of the port
let port_value = port.unwrap_or(cfg.app_port.parse::<i32>().unwrap_or(0));
let port: i32 = port_value;
// Start the relay server with the port and listen address
relay::server::start_ws(&port, &address).await;
}
// No command provided
None => {}
}
Ok(())
}
}