forge/forge-client/src/main.rs

200 lines
5.3 KiB
Rust
Raw Normal View History

2023-04-03 03:17:57 +00:00
use clap::{Args, Parser, Subcommand, ValueEnum};
2023-04-02 23:22:41 +00:00
use paris::{error, info, success};
2023-04-02 22:07:23 +00:00
use serde::{Deserialize, Serialize};
2023-04-02 20:33:58 +00:00
use std::error::Error;
2023-04-02 22:07:23 +00:00
use std::fmt;
2023-04-03 03:29:32 +00:00
use std::process::exit;
2023-04-02 20:33:58 +00:00
use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream;
2023-04-02 22:07:23 +00:00
use url::Url;
2023-04-02 23:01:47 +00:00
use uuid::Uuid;
2023-04-02 22:07:23 +00:00
2023-04-03 03:17:57 +00:00
#[derive(Debug, Parser)]
#[command(name = "forge-server")]
#[command(about = "A simple remote build system", long_about = None)]
struct Cli {
2023-04-03 03:29:32 +00:00
#[arg(value_name = "IP")]
host: String,
#[arg(value_name = "PORT", default_value_t = 9134)]
port: u16,
2023-04-03 03:17:57 +00:00
#[command(subcommand)]
command: Commands,
}
#[derive(Debug, Subcommand)]
enum Commands {
/// Send a new build request
#[command(arg_required_else_help = true)]
Send {
/// Password for authentication
#[arg(long, value_name = "PASSWD")]
auth: Option<String>,
/// Whether to profile the build
#[arg(long, default_value_t = false)]
profile: bool,
/// Command(s) to run before building
#[arg(long, value_name = "CMD")]
prexec: Option<String>,
/// Remote git repository to clone from
#[arg(long, value_name = "URL")]
repo: String,
/// Build system to use
#[arg(long,require_equals = true, num_args = 0..=1, default_value_t = BuildSystem::Cargo,default_missing_value = "cargo")]
build: BuildSystem,
/// Cargo/Make subcommand to use
#[arg(long, require_equals = true, num_args = 0..=1, default_value_t = BuildSubcommand::Build, default_missing_value = "build")]
subcommand: BuildSubcommand,
/// Comma-separated cargo features
#[arg(long, value_name = "FEATURES")]
features: Option<Vec<String>>,
/// Optional cargo flag
#[arg(long, require_equals = true, num_args = 0..=1, default_value_t = Tag::Debug, default_missing_value = "debug")]
tag: Tag,
2023-04-03 04:07:17 +00:00
/// Basename of the repository
#[arg(long, value_name = "NAME")]
basename: String,
2023-04-03 03:17:57 +00:00
},
}
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
2023-04-02 22:07:23 +00:00
enum BuildSystem {
Cargo,
Make,
}
2023-04-03 03:17:57 +00:00
impl std::fmt::Display for BuildSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
2023-04-02 22:07:23 +00:00
}
}
2023-04-03 03:17:57 +00:00
#[derive(Serialize, Deserialize, ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
enum BuildSubcommand {
2023-04-02 22:07:23 +00:00
Run,
Build,
Install,
}
2023-04-03 03:17:57 +00:00
impl std::fmt::Display for BuildSubcommand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
2023-04-02 22:07:23 +00:00
}
}
2023-04-03 03:17:57 +00:00
#[derive(Serialize, Deserialize, ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
2023-04-02 22:07:23 +00:00
enum Tag {
Release,
Debug,
}
2023-04-03 03:17:57 +00:00
impl std::fmt::Display for Tag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
2023-04-02 22:07:23 +00:00
}
}
#[derive(Serialize, Deserialize)]
struct Command {
build_system: BuildSystem,
2023-04-03 03:17:57 +00:00
subcommand: BuildSubcommand,
2023-04-02 22:07:23 +00:00
features: Option<Vec<String>>,
tag: Tag,
2023-04-02 23:01:47 +00:00
}
#[derive(Serialize, Deserialize)]
struct Message {
2023-04-03 00:46:03 +00:00
authentication: Option<String>,
2023-04-02 23:01:47 +00:00
uuid: Uuid,
pre_exec: Option<String>,
profile: bool,
command: Command,
2023-04-02 22:07:23 +00:00
repository: Url,
2023-04-02 23:01:47 +00:00
basename: String,
2023-04-02 22:07:23 +00:00
}
2023-04-02 20:33:58 +00:00
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
2023-04-03 03:17:57 +00:00
let args = Cli::parse();
match args.command {
Commands::Send {
auth,
repo,
profile,
prexec,
build,
subcommand,
features,
tag,
2023-04-03 04:07:17 +00:00
basename,
2023-04-03 03:17:57 +00:00
} => {
// Connect to a peer
2023-04-03 03:29:32 +00:00
let remote = format!("{}:{}", args.host, args.port);
let mut stream = match TcpStream::connect(&remote).await {
Ok(s) => s,
Err(e) => {
error!("Could not connect to remote host: {}", e);
exit(1);
}
};
success!("Connected to: <green>{}<//>", &remote);
2023-04-03 03:17:57 +00:00
let test_command = Command {
build_system: build,
subcommand,
features,
tag,
};
let test_message = Message {
authentication: auth,
uuid: Uuid::new_v4(),
pre_exec: prexec,
profile,
command: test_command,
repository: Url::parse(&repo).unwrap(),
2023-04-03 04:07:17 +00:00
basename,
2023-04-03 03:17:57 +00:00
};
2023-04-03 03:34:36 +00:00
info!("Message UUID: <magenta>{}<//>", &test_message.uuid);
2023-04-03 03:29:32 +00:00
let j = match serde_json::to_string(&test_message) {
Ok(s) => s,
Err(e) => {
error!("Could not serialize JSON message: {}", e);
exit(1);
}
};
match stream.write_all(&j.as_bytes()).await {
Ok(_) => {
info!(
"Sent JSON:\n<bright-white>{}<//>",
serde_json::to_string_pretty(&test_message)?
);
}
Err(e) => error!("Could not write to stream: {}", e),
}
2023-04-03 03:17:57 +00:00
}
}
2023-04-02 20:33:58 +00:00
Ok(())
2023-04-02 18:01:11 +00:00
}