#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
use clap::Parser;
use mikcar::SidecarBuilder;
#[derive(Parser)]
#[command(name = "mikcar")]
#[command(version)]
#[command(about = "Sidecar infrastructure services for mikrozen")]
struct Cli {
#[arg(short, long, default_value = "3001", env = "PORT")]
port: u16,
#[arg(long, env = "SIDECAR_TOKEN")]
auth_token: Option<String>,
#[cfg(feature = "otlp")]
#[arg(long, env = "OTLP_ENDPOINT")]
otlp_endpoint: Option<String>,
#[cfg(feature = "otlp")]
#[arg(long, env = "OTLP_SERVICE_NAME", default_value = "mikcar")]
service_name: String,
#[cfg(feature = "storage")]
#[arg(long)]
storage: bool,
#[cfg(feature = "kv")]
#[arg(long)]
kv: bool,
#[cfg(feature = "sql")]
#[arg(long)]
sql: bool,
#[cfg(feature = "queue")]
#[arg(long)]
queue: bool,
#[cfg(feature = "secrets")]
#[arg(long)]
secrets: bool,
#[cfg(feature = "email-smtp")]
#[arg(long)]
email: bool,
#[arg(long)]
all: bool,
#[arg(long)]
no_auth: bool,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
init_tracing(&cli);
let mut builder = SidecarBuilder::new().port(cli.port);
if cli.no_auth {
builder = builder.no_auth();
} else if let Some(token) = cli.auth_token.clone() {
builder = builder.auth_token(token);
}
let mut services: Vec<(&str, Box<dyn mikcar::Sidecar>)> = Vec::new();
#[cfg(feature = "storage")]
if cli.storage || cli.all {
let storage = mikcar::storage::StorageService::from_env()?;
services.push(("/storage", Box::new(storage)));
}
#[cfg(feature = "kv")]
if cli.kv || cli.all {
let kv = mikcar::kv::KvService::from_env()?;
services.push(("/kv", Box::new(kv)));
}
#[cfg(feature = "sql")]
if cli.sql || cli.all {
let sql = mikcar::sql::SqlService::from_env().await?;
services.push(("/sql", Box::new(sql)));
}
#[cfg(feature = "queue")]
if cli.queue || cli.all {
let queue = mikcar::queue::QueueService::from_env().await?;
services.push(("/queue", Box::new(queue)));
}
#[cfg(feature = "secrets")]
if cli.secrets || cli.all {
let secrets = mikcar::secrets::SecretsService::from_env()?;
services.push(("/secrets", Box::new(secrets)));
}
#[cfg(feature = "email-smtp")]
if cli.email || cli.all {
let email = mikcar::email::EmailService::from_env()?;
services.push(("/email", Box::new(email)));
}
if services.is_empty() {
eprintln!(
"Error: No services enabled. Use --storage, --kv, --sql, --queue, --secrets, or --all"
);
eprintln!("\nAvailable features (compile with):");
eprintln!(" cargo build -p mikcar --features storage");
eprintln!(" cargo build -p mikcar --features kv");
eprintln!(" cargo build -p mikcar --features secrets");
eprintln!(" cargo build -p mikcar --features sql");
eprintln!(" cargo build -p mikcar --features queue");
eprintln!(" cargo build -p mikcar --features all");
std::process::exit(1);
}
builder.serve_multi(services).await?;
Ok(())
}
#[allow(unused_variables)]
fn init_tracing(cli: &Cli) {
#[cfg(feature = "otlp")]
if let Some(endpoint) = &cli.otlp_endpoint {
use mikcar::otlp::{OtlpConfig, init_with_otlp};
let config = OtlpConfig::new(endpoint).with_service_name(&cli.service_name);
if let Err(e) = init_with_otlp(&config) {
eprintln!("Warning: Failed to initialize OTLP tracing: {e}");
eprintln!("Falling back to stdout logging");
init_stdout_tracing();
}
return;
}
init_stdout_tracing();
}
fn init_stdout_tracing() {
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "mikcar=info,tower_http=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
}