use std::net::SocketAddr;
use clap::{Parser, Subcommand, ValueEnum};
use specmock_core::MockMode;
use specmock_sdk::MockServer;
#[derive(Debug, Parser)]
#[command(
name = "spec-mock",
version,
about = "Spec-driven mock runtime for OpenAPI/AsyncAPI/Protobuf"
)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Debug, Subcommand)]
enum Commands {
Serve(ServeArgs),
}
#[derive(Debug, Parser)]
struct ServeArgs {
#[arg(long)]
openapi: Option<std::path::PathBuf>,
#[arg(long)]
asyncapi: Option<std::path::PathBuf>,
#[arg(long)]
proto: Option<std::path::PathBuf>,
#[arg(long, value_enum, default_value = "mock")]
mode: ModeArg,
#[arg(long, required_if_eq("mode", "proxy"))]
upstream: Option<String>,
#[arg(long, default_value_t = 42)]
seed: u64,
#[arg(long, default_value = "127.0.0.1:4010")]
http_addr: SocketAddr,
#[arg(long, default_value = "127.0.0.1:5010")]
grpc_addr: SocketAddr,
#[arg(long, default_value_t = 10_485_760)]
max_body_size: usize,
}
#[derive(Debug, Clone, Copy, ValueEnum, Default)]
enum ModeArg {
#[default]
Mock,
Proxy,
}
impl ModeArg {
const fn into_runtime_mode(self) -> MockMode {
match self {
Self::Mock => MockMode::Mock,
Self::Proxy => MockMode::Proxy,
}
}
}
#[tokio::main]
async fn main() -> Result<(), eyre::Report> {
tracing_subscriber::fmt().with_target(false).with_level(true).compact().init();
let cli = Cli::parse();
match cli.command {
Commands::Serve(args) => serve_command(args).await?,
}
Ok(())
}
async fn serve_command(args: ServeArgs) -> Result<(), eyre::Report> {
let mut builder = MockServer::builder()
.seed(args.seed)
.mode(args.mode.into_runtime_mode())
.http_addr(args.http_addr)
.grpc_addr(args.grpc_addr)
.max_body_size(args.max_body_size);
if let Some(openapi) = args.openapi {
builder = builder.openapi(openapi);
}
if let Some(asyncapi) = args.asyncapi {
builder = builder.asyncapi(asyncapi);
}
if let Some(proto) = args.proto {
builder = builder.proto(proto);
}
if let Some(upstream) = args.upstream {
builder = builder.upstream(upstream);
}
let server = builder.start().await?;
tracing::info!("HTTP: {}", server.http_base_url());
if let Some(grpc_addr) = server.grpc_addr() {
tracing::info!("gRPC: {grpc_addr}");
}
tracing::info!("Press Ctrl+C to stop");
tokio::signal::ctrl_c().await?;
server.shutdown().await;
Ok(())
}