refactor(caesar): restructure the project to a monorepo
This commit is contained in:
parent
17ebd0261b
commit
b39e88107a
27 changed files with 195 additions and 142 deletions
23
caesar-cli/Cargo.toml
Normal file
23
caesar-cli/Cargo.toml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "caesar-cli"
|
||||
version = "0.3.1"
|
||||
edition = "2021"
|
||||
authors = ["Manuel Keidel, Patryk Hegenberg, Krzysztof Stankiewicz"]
|
||||
|
||||
[[bin]]
|
||||
name = "caesar"
|
||||
path = "src/main.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
caesar-core = { path = "../caesar-core" }
|
||||
tokio = { version = "1.28.1", features = ["full"] }
|
||||
serde_json = { version = "1.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
dotenv = { version = "0.15.0", features = ["clap", "cli"] }
|
||||
clap = { version = "4.5.4", features = ["derive"] }
|
||||
axum = { version = "0.7.5", features = ["ws"] }
|
||||
axum-client-ip = "0.6.0"
|
||||
197
caesar-cli/src/cli/args.rs
Normal file
197
caesar-cli/src/cli/args.rs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
use caesar_core::receiver;
|
||||
use caesar_core::relay;
|
||||
use caesar_core::sender;
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::{env, sync::Arc};
|
||||
use tracing::debug;
|
||||
|
||||
/// This struct defines the CLI arguments and subcommands for the caesar command line application.
|
||||
///
|
||||
/// The #[derive(Parser, Debug)] macro generates code that:
|
||||
/// - parses the command line arguments using the clap library
|
||||
/// - provides a Debug implementation for the struct
|
||||
///
|
||||
/// The #[command(version, about, long_about = None)] macro generates code that:
|
||||
/// - defines the version and about strings for the application
|
||||
/// - specifies that there is no long about help text
|
||||
///
|
||||
/// The #[command(subcommand)] macro generates code that:
|
||||
/// - defines a subcommand for the caesar command line application.
|
||||
/// Subcommands are used to break up a large number of options into
|
||||
/// smaller, more manageable groups.
|
||||
///
|
||||
/// The #[command] macro is used to annotate the `command` field of the struct.
|
||||
/// The `command` field is an Option<Commands> type, which means that the
|
||||
/// subcommand is optional.
|
||||
/// If the subcommand is not provided, the program will exit with a status code
|
||||
/// of 0 and without printing any output.
|
||||
///
|
||||
/// The Commands enum defines the possible subcommands for the caesar command
|
||||
/// line application.
|
||||
/// See the Commands enum definition for more information about the available
|
||||
/// subcommands.
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version = env!("CARGO_PKG_VERSION"), about = "Send and receive files securely")]
|
||||
#[command(long_about = None)]
|
||||
pub struct Args {
|
||||
/// The subcommand for the caesar command line application.
|
||||
/// Subcommands are used to break up a large number of options into smaller,
|
||||
/// more manageable groups.
|
||||
/// If no subcommand is provided, the program will exit with a status code
|
||||
/// of 0 and without printing any output.
|
||||
#[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>,
|
||||
|
||||
/// Overwrite existing Files
|
||||
#[arg(short, long)]
|
||||
overwrite: bool,
|
||||
|
||||
/// 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>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for Args {
|
||||
// This function is called by the Default trait when no value is
|
||||
// provided for a field of type Args. It returns an instance of
|
||||
// Args that has been created by calling the new() function.
|
||||
//
|
||||
// The Default trait is used by various parts of the program to
|
||||
// provide a sensible default value for a field when no value is
|
||||
// provided. For example, the clap crate uses the Default trait when
|
||||
// parsing command line arguments to provide a default value for
|
||||
// a field.
|
||||
//
|
||||
// The new() function is a constructor function for Args that
|
||||
// creates an instance of Args with default field values.
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Args {
|
||||
/// Creates a new instance of Args by parsing command line arguments
|
||||
///
|
||||
/// This function is a constructor for Args. It uses the clap crate to parse
|
||||
/// command line arguments and creates an instance of Args with the values
|
||||
/// provided by the user.
|
||||
///
|
||||
/// The clap crate is a command line argument parser that is well tested and
|
||||
/// widely used. It provides a simple way to define command line
|
||||
/// arguments and generate helpful documentation for the user.
|
||||
///
|
||||
/// The `parse()` function is used to parse the command line arguments and
|
||||
/// return an instance of Args.
|
||||
pub fn new() -> Self {
|
||||
Self::parse()
|
||||
}
|
||||
|
||||
/// Runs the command specified by the user
|
||||
///
|
||||
/// This function is called after the command line arguments have been
|
||||
/// parsed. It matches on the `command` field of the Args struct to determine
|
||||
/// what command the user wants to run.
|
||||
///
|
||||
/// The match statement checks the value of `command` and calls the
|
||||
/// appropriate function to run the command. The functions that are called
|
||||
/// are located in other modules of the program.
|
||||
///
|
||||
/// The `run()` function is called by the `main()` function of the program.
|
||||
/// The program's entry point is the `main()` function, which parses the
|
||||
/// command line arguments and then calls `run()` on the resulting Args
|
||||
/// instance.
|
||||
///
|
||||
/// The `run()` function returns a Result. The error type is `Box<dyn
|
||||
/// std::error::Error + Send + Sync>`. This means that the error type is a
|
||||
/// trait object that represents an error that can be sent across threads
|
||||
/// and sent over a network connection. The `Send` and `Sync` traits are part
|
||||
/// of the standard library and are used to indicate that the error type can
|
||||
/// be sent across threads and sent over a network connection.
|
||||
///
|
||||
/// The `run()` function does not return anything if the command is `None`.
|
||||
/// This is because `command` is an `Option<Commands>`. If the user does
|
||||
/// not specify a command, then `command` is `None`. In this case, there is
|
||||
/// nothing to run, so `run()` returns early with no error.
|
||||
pub async fn run(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
debug!("args: {:#?}", self);
|
||||
match &self.command {
|
||||
// If the user wants to send files, call `start_sender()` in the
|
||||
// `sender` module with the list of files that the user wants to
|
||||
// send.
|
||||
Some(Commands::Send { relay, files }) => {
|
||||
let relay_string: String = relay
|
||||
.as_deref()
|
||||
.unwrap_or(
|
||||
&env::var("APP_ORIGIN")
|
||||
.unwrap_or("wss://caesar-transfer-iu.shuttleapp.rs/ws".to_string()),
|
||||
)
|
||||
.to_string();
|
||||
let relay_arc = Arc::new(relay_string);
|
||||
let files_arc = Arc::new(files.to_vec());
|
||||
sender::start_sender(relay_arc, files_arc).await;
|
||||
}
|
||||
// If the user wants to receive files, call `start_receiver()` in the
|
||||
// `receiver` module with the name of the transfer that the user
|
||||
// wants to download.
|
||||
Some(Commands::Receive {
|
||||
relay,
|
||||
overwrite: _,
|
||||
name,
|
||||
}) => {
|
||||
println!("Receive for {name:?}");
|
||||
receiver::start_receiver(
|
||||
relay.as_deref().unwrap_or(
|
||||
env::var("APP_ORIGIN")
|
||||
.unwrap_or("ws://0.0.0.0:8000/ws".to_string())
|
||||
.as_str(),
|
||||
),
|
||||
name,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
// If the user wants to start a relay server, call `start_ws()` in the
|
||||
// `relay` module with the port and listen address that the user
|
||||
// specified.
|
||||
Some(Commands::Serve {
|
||||
port,
|
||||
listen_address,
|
||||
}) => {
|
||||
println!("Serve with address '{listen_address:?}' and '{port:?}'");
|
||||
relay::server::start_ws(port.as_ref(), listen_address.as_ref()).await;
|
||||
}
|
||||
// If the user does not specify a command, return early with no error.
|
||||
// This is because `command` is an `Option<Commands>`. If the user does
|
||||
// not specify a command, then `command` is `None`.
|
||||
None => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
1
caesar-cli/src/cli/mod.rs
Normal file
1
caesar-cli/src/cli/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod args;
|
||||
33
caesar-cli/src/main.rs
Normal file
33
caesar-cli/src/main.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use crate::cli::args::Args;
|
||||
use dotenv::dotenv;
|
||||
use tracing::error;
|
||||
use tracing_subscriber::filter::EnvFilter;
|
||||
|
||||
mod cli;
|
||||
|
||||
#[tokio::main]
|
||||
// This is the entrypoint of caesar.
|
||||
// The #[tokio::main] attribute is required for any async code, and it
|
||||
// sets up the tokio runtime.
|
||||
// The async fn main() is the entrypoint of the application, and it's where
|
||||
// we kick off our program.
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
// Load environment variables from a .env file if one is present.
|
||||
dotenv().ok();
|
||||
// Set up our logging subscriber.
|
||||
// TheEnvFilter::from_default_env reads the env variable RUST_LOG
|
||||
// and sets up the logging accordingly.
|
||||
// The default is INFO level logging.
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(EnvFilter::from_default_env())
|
||||
.init();
|
||||
// Parse the command line arguments.
|
||||
let args = Args::new();
|
||||
// Run the commands based on the parsed arguments.
|
||||
// If there is an error, print it to the console with the error! macro.
|
||||
if let Err(e) = args.run().await {
|
||||
error!("{e}");
|
||||
}
|
||||
// Return an Ok result, which just means that our program exited successfully.
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue