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

feat(runtime): Remove dependency on clap #822

Merged
merged 1 commit into from
May 15, 2023
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 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ doctest = false
anyhow = { workspace = true }
async-trait = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true }
prost-types = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
Expand Down
50 changes: 23 additions & 27 deletions runtime/src/alpha/args.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
use std::path::PathBuf;
use std::{fmt::Debug, path::PathBuf, str::FromStr};

use clap::{Parser, ValueEnum};
use tonic::transport::{Endpoint, Uri};

#[derive(Parser, Debug)]
#[command(version)]
pub struct Args {
/// Port to start runtime on
#[arg(long)]
pub port: u16,
use crate::args::args;

/// Address to reach provisioner at
#[arg(long, default_value = "http://localhost:3000")]
pub provisioner_address: Endpoint,

/// Type of storage manager to start
#[arg(long, value_enum)]
pub storage_manager_type: StorageManagerType,

/// Path to use for storage manager
#[arg(long)]
pub storage_manager_path: PathBuf,

/// Address to reach the authentication service at
#[arg(long, default_value = "http://127.0.0.1:8008")]
pub auth_uri: Uri,
args! {
pub struct Args {
"--port" => pub port: u16,
"--provisioner-address" => #[arg(default_value = "http://localhost:3000")] pub provisioner_address: Endpoint,
"--storage-manager-type" => pub storage_manager_type: StorageManagerType,
"--storage-manager-path" => pub storage_manager_path: PathBuf,
"--auth-uri" => #[arg(default_value = "http://127.0.0.1:8008")] pub auth_uri: Uri,
}
}

#[derive(Clone, Debug, ValueEnum)]
#[derive(Clone, Debug)]
pub enum StorageManagerType {
/// Use a deloyer artifacts directory
Artifacts,

/// Use a local working directory
WorkingDir,
}

impl FromStr for StorageManagerType {
type Err = ();

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"artifacts" => Ok(StorageManagerType::Artifacts),
"working-dir" => Ok(StorageManagerType::WorkingDir),
_ => Err(()),
}
}
}
3 changes: 1 addition & 2 deletions runtime/src/alpha/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use std::{

use anyhow::Context;
use async_trait::async_trait;
use clap::Parser;
use core::future::Future;
use shuttle_common::{
backends::{
Expand Down Expand Up @@ -51,7 +50,7 @@ use self::args::Args;
mod args;

pub async fn start(loader: impl Loader<ProvisionerFactory> + Send + 'static) {
let args = Args::parse();
let args = Args::parse().expect("could not parse arguments");
let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), args.port);

let provisioner_address = args.provisioner_address;
Expand Down
80 changes: 80 additions & 0 deletions runtime/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#[derive(Debug)]
pub enum Error {
DuplicatedArgument { arg: &'static str },
MissingRequiredArgument { arg: &'static str },
UnexpectedArgument { arg: String },

InvalidValue { arg: &'static str, value: String },
MissingValue { arg: &'static str },
}

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DuplicatedArgument { arg } => write!(f, "duplicated argument {arg}"),
Self::MissingRequiredArgument { arg } => write!(f, "missing required argument {arg}"),
Self::UnexpectedArgument { arg } => write!(f, "unexpected argument: {arg}"),

Self::InvalidValue { arg, value } => {
write!(f, "invalid value for argument {arg}: {value}")
}
Self::MissingValue { arg } => write!(f, "missing value for argument {arg}"),
}
}
}

impl std::error::Error for Error {}

macro_rules! args {
// Internal rules used to handle optional default values
(@unwrap $arg:literal, $field:ident $(,)?) => {
$field.ok_or_else(|| $crate::args::Error::MissingRequiredArgument { arg: $arg })?
};
(@unwrap $arg:literal, $field:ident, $default:literal) => {
$field.unwrap_or_else(|| $default.parse().unwrap())
};
Comment on lines +29 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe these should be in a separate macro_rule (unwrap_arg?). They feel a bit unexpected here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I you call an exported macro from another module, then every macro it calls must also be exported. I tracked down this trick for dealing with it without polluting any namespaces. http://danielkeep.github.io/tlborm/book/pat-internal-rules.html

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, calling the new macro using its full namespace will also work here 😄


(
pub struct $struct:ident {
$($arg:literal => $(#[arg(default_value = $default:literal)])? pub $field:ident: $ty:ty),+ $(,)?
chesedo marked this conversation as resolved.
Show resolved Hide resolved
}
) => {
#[derive(::std::fmt::Debug)]
pub struct $struct {
$(pub $field: $ty,)+
}

impl $struct {
pub fn parse() -> ::std::result::Result<Self, $crate::args::Error> {
$(let mut $field: ::std::option::Option<$ty> = None;)+

// The first argument is the path of the executable.
let mut args_iter = ::std::env::args().skip(1);
while let ::std::option::Option::Some(arg) = args_iter.next() {
match arg.as_str() {
$($arg => {
if $field.is_some() {
return ::std::result::Result::Err($crate::args::Error::DuplicatedArgument { arg: $arg });
}
let raw_value = args_iter
.next()
.ok_or_else(|| $crate::args::Error::MissingValue { arg: $arg })?;
let value = raw_value.parse().map_err(|_| $crate::args::Error::InvalidValue {
arg: $arg,
value: raw_value,
})?;
$field = ::std::option::Option::Some(value);
})+
_ => return ::std::result::Result::Err($crate::args::Error::UnexpectedArgument { arg }),
}
}

::std::result::Result::Ok($struct {
$($field: $crate::args::args!(@unwrap $arg, $field, $($default)?),)+
})
}
}
}
}

pub(crate) use args;
3 changes: 1 addition & 2 deletions runtime/src/bin/shuttle-next.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::{
time::Duration,
};

use clap::Parser;
use shuttle_common::backends::tracing::{setup_tracing, ExtractPropagationLayer};
use shuttle_proto::runtime::runtime_server::RuntimeServer;
use shuttle_runtime::{AxumWasm, NextArgs};
Expand All @@ -12,7 +11,7 @@ use tracing::trace;

#[tokio::main(flavor = "multi_thread")]
async fn main() {
let args = NextArgs::parse();
let args = NextArgs::parse().unwrap();

setup_tracing(tracing_subscriber::registry(), "shuttle-next");

Expand Down
1 change: 1 addition & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@
//! You can also [open an issue or a discussion on GitHub](https://github.com/shuttle-hq/shuttle).
//!
mod alpha;
mod args;
mod logger;
#[cfg(feature = "next")]
mod next;
Expand Down
12 changes: 5 additions & 7 deletions runtime/src/next/args.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use clap::Parser;
use crate::args::args;

#[derive(Parser, Debug)]
#[command(version)]
pub struct NextArgs {
/// Port to start runtime on
#[arg(long)]
pub port: u16,
args! {
pub struct NextArgs {
"--port" => pub port: u16,
}
}