Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial cli arg parser, http and https commands and placeholder timer commands #3

Merged
merged 7 commits into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,493 changes: 1,493 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,21 @@ keywords = ["cli", "terminal", "utility", "tool", "command"]
categories = ["command-line-interface", "command-line-utilities", "development-tools"]

[dependencies]
clap = "3"

# for http and https commands
tokio = { version = "1", features = ["full"] }
# warp = "0.3"
tracing-subscriber = "0.3.18"
# for https command
warp = { version = "0.3", features = ["default", "tls"] }
openssl = "0.10"

# for db command
rapiddb-web = "0.1"

[lints.rust]
unused_parens = "allow"

[lints.clippy]
needless_return = "allow"
93 changes: 93 additions & 0 deletions src/http/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use openssl::x509::extension::BasicConstraints;
use openssl::x509::{X509NameBuilder, X509};
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use tracing_subscriber::fmt::format::FmtSpan;
use warp::Filter;

pub fn open_or_create_file(path: &str) -> Result<File, Box<dyn Error>> {
let path = Path::new(path);

if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}

let file =
OpenOptions::new().read(true).write(true).create(true).open(path)?;

Ok(file)
}

pub fn generate_openssl_x509_rsa_cert_key_files(
cert_file_path: &str,
key_file_path: &str,
) -> Result<(), Box<dyn Error>> {
let rsa = Rsa::generate(2048)?;
let pkey = PKey::from_rsa(rsa)?;

let mut name = X509NameBuilder::new()?;
name.append_entry_by_text("CN", "localhost")?;
let name = name.build();

let mut builder = X509::builder()?;
builder.set_version(2)?;
builder.set_subject_name(&name)?;
builder.set_issuer_name(&name)?;
builder.set_pubkey(&pkey)?;
builder.set_not_before(&*openssl::asn1::Asn1Time::days_from_now(0)?)?;
builder.set_not_after(&*openssl::asn1::Asn1Time::days_from_now(3650)?)?;

builder.append_extension(BasicConstraints::new().critical().ca().build()?)?;

builder.sign(&pkey, MessageDigest::sha256())?;

let certificate = builder.build();

let mut key_file = open_or_create_file(key_file_path)?;
key_file.write_all(&pkey.private_key_to_pem_pkcs8()?)?;

let mut cert_file = open_or_create_file(cert_file_path)?;
cert_file.write_all(&certificate.to_pem()?)?;

return Ok(());
}

pub async fn create_server_http(port: u16) -> Result<(), Box<dyn Error>> {
tracing_subscriber::fmt().with_span_events(FmtSpan::CLOSE).init();

warp::serve(warp::fs::dir(".").with(warp::trace::request()))
.run(([0, 0, 0, 0], port))
.await;

return Ok(());
}

pub async fn create_server_https(port: u16) -> Result<(), Box<dyn Error>> {
let key_file_path = &format!(
"{}/d4cd362e-89ef-4267-9e35-4cc8a79b60eb/key.pem",
std::env::temp_dir().to_str().unwrap_or(".")
);

let cert_file_path = &format!(
"{}/d4cd362e-89ef-4267-9e35-4cc8a79b60eb/cert.pem",
std::env::temp_dir().to_str().unwrap_or(".")
);

generate_openssl_x509_rsa_cert_key_files(cert_file_path, key_file_path)?;

tracing_subscriber::fmt().with_span_events(FmtSpan::CLOSE).init();

warp::serve(warp::fs::dir(".").with(warp::trace::request()))
.tls()
.cert_path(cert_file_path)
.key_path(key_file_path)
.run(([0, 0, 0, 0], port))
.await;

return Ok(());
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@
//! cargo install --locked --path .
//! i6
//! ```

pub mod http;
pub mod timer;
134 changes: 132 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,133 @@
fn main() {
println!("Hello, world!");
use std::error::Error;

use clap::{value_parser, App, Arg, SubCommand};

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let http_id = "http";
let https_id = "https";

let matches = App::new("i6")
.subcommand(
SubCommand::with_name(http_id).about("Start a static http server").arg(
Arg::with_name("port")
.index(1)
.default_value("3030")
.value_parser(value_parser!(u16)),
),
)
.subcommand(
SubCommand::with_name(https_id).about("Start a static https server").arg(
Arg::with_name("port")
.index(1)
.default_value("3030")
.value_parser(value_parser!(u16)),
),
)
.subcommand(
SubCommand::with_name("timer")
.about("Manages timers")
.arg(Arg::with_name("minutes").index(1).takes_value(true))
.arg(Arg::with_name("name").index(2).takes_value(true))
.subcommand(
SubCommand::with_name("create")
.about("Creates a new timer")
.arg(
Arg::with_name("minutes")
.short('m')
.long("minutes")
.takes_value(true)
.help("Sets the duration of the timer in minutes"),
)
.arg(
Arg::with_name("name")
.short('n')
.long("name")
.takes_value(true)
.help("Sets the name of the timer"),
),
)
.subcommand(
SubCommand::with_name("list").about("Lists all timers").alias("ls"),
)
.subcommand(
SubCommand::with_name("stop")
.about("Stops a timer")
.arg(
Arg::with_name("name")
.short('n')
.long("name")
.takes_value(true)
.help("Stops the timer with the given name"),
)
.arg(
Arg::with_name("all")
.short('a')
.long("all")
.takes_value(false)
.help("Stops all timers"),
),
)
.subcommand(
SubCommand::with_name("history")
.about("Shows the history of all timers")
.alias("log")
.arg(
Arg::with_name("json")
.short('j')
.long("json")
.takes_value(false)
.help("Prints the history in JSON format"),
),
),
)
.get_matches();

if let Some(matches) = matches.subcommand_matches(http_id) {
println!("http, {:?}", matches);

let port = *matches.get_one::<u16>("port").unwrap_or(&3030);

i6::http::create_server_http(port).await?;
}

if let Some(matches) = matches.subcommand_matches(https_id) {
println!("https, {:?}", matches);

let port = *matches.get_one::<u16>("port").unwrap_or(&3030);

i6::http::create_server_https(port).await?;
}

if let Some(matches) = matches.subcommand_matches("timer") {
if let Some(matches) = matches.subcommand_matches("create") {
let minutes = matches.value_of("minutes").unwrap_or_default();
let name = matches.value_of("name").unwrap_or_default();
i6::timer::create::create_timer(minutes, name);
} else if matches.subcommand_matches("list").is_some() {
i6::timer::list::list_timers();
} else if let Some(matches) = matches.subcommand_matches("stop") {
if matches.is_present("all") {
i6::timer::stop::stop_all_timers();
} else {
i6::timer::stop::stop_timer(
matches.value_of("name").unwrap_or_default(),
);
}
} else if let Some(matches) = matches.subcommand_matches("history") {
if matches.is_present("json") {
i6::timer::print::print_history_json();
} else {
i6::timer::print::print_history();
}
} else if let (Some(minutes), Some(name)) =
(matches.value_of("minutes"), matches.value_of("name"))
{
i6::timer::create::create_timer(minutes, name);
} else if let Some(minutes) = matches.value_of("minutes") {
i6::timer::create::create_timer(minutes, "");
}
}

return Ok(());
}
11 changes: 11 additions & 0 deletions src/timer/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub fn create_timer(minutes: &str, name: &str) {
// Implement the create timer functionality here
println!("Creating a timer named '{}' for {} minutes...", name, minutes);
}
#[test]
fn test_create_timer() {
let minutes = "10";
let name = "test";
create_timer(minutes, name);
// Add assertions here based on your implementation
}
9 changes: 9 additions & 0 deletions src/timer/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub fn list_timers() {
// Implement the list timers functionality here
println!("Listing all timers...");
}
#[test]
fn test_list_timers() {
list_timers();
// Add assertions here based on your implementation
}
4 changes: 4 additions & 0 deletions src/timer/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod create;
pub mod list;
pub mod print;
pub mod stop;
19 changes: 19 additions & 0 deletions src/timer/print.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pub fn print_history() {
// Implement the history functionality here
println!("Printing the history of all timers...");
}
#[test]
fn test_print_history() {
print_history();
// Add assertions here based on your implementation
}

pub fn print_history_json() {
// Implement the history in JSON format functionality here
println!("Printing the history of all timers in JSON format...");
}
#[test]
fn test_print_history_json() {
print_history_json();
// Add assertions here based on your implementation
}
20 changes: 20 additions & 0 deletions src/timer/stop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pub fn stop_timer(name: &str) {
// Implement the stop timer functionality here
println!("Stopping the timer named '{}'...", name);
}
#[test]
fn test_stop_timer() {
let name = "test";
stop_timer(name);
// Add assertions here based on your implementation
}

pub fn stop_all_timers() {
// Implement the stop all timers functionality here
println!("Stopping all timers...");
}
#[test]
fn test_stop_all_timers() {
stop_all_timers();
// Add assertions here based on your implementation
}
Loading