use std::collections::HashMap;
use std::path::PathBuf;
use std::process::ExitCode;
use std::sync::Arc;
use clap::Parser;
use epics_bridge_rs::qsrv::{BridgeProvider, QsrvPvStore};
use epics_pva_rs::server::{PvaServer, PvaServerBuilder};
use epics_pva_rs::server_native::ChannelSource;
#[derive(Parser, Debug)]
#[command(
name = "qsrv-rs",
about = "Rust port of EPICS QSRV: serves records as pvAccess channels",
version
)]
struct Args {
#[arg(long)]
db_file: Option<PathBuf>,
#[arg(long)]
group_file: Option<PathBuf>,
#[arg(long = "macro", value_parser = parse_macro)]
macros: Vec<(String, String)>,
#[arg(long, default_value_t = 0)]
port: u16,
}
fn parse_macro(raw: &str) -> Result<(String, String), String> {
let (k, v) = raw
.split_once('=')
.ok_or_else(|| format!("expected KEY=VAL, got {raw:?}"))?;
Ok((k.to_string(), v.to_string()))
}
#[tokio::main]
async fn main() -> ExitCode {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
let args = Args::parse();
match run(args).await {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("qsrv-rs: {e}");
ExitCode::FAILURE
}
}
}
async fn run(args: Args) -> Result<(), String> {
let mut builder: PvaServerBuilder = PvaServer::builder();
if args.port != 0 {
builder = builder.port(args.port);
}
if let Some(path) = args.db_file.as_ref() {
let macros: HashMap<String, String> = args.macros.iter().cloned().collect();
builder = builder
.db_file(path.to_string_lossy().as_ref(), ¯os)
.map_err(|e| format!("loading db file {}: {e}", path.display()))?;
}
let server = builder.build().await.map_err(|e| e.to_string())?;
let db = server.database().clone();
let provider = BridgeProvider::new(db);
if let Some(path) = args.group_file.as_ref() {
provider
.load_group_file(path.to_string_lossy().as_ref())
.map_err(|e| format!("loading group file {}: {e}", path.display()))?;
}
let store = Arc::new(QsrvPvStore::new(Arc::new(provider)));
let pv_count = store.list_pvs().await.len();
let group_count = store.provider().groups().len();
tracing::info!(
"qsrv-rs: serving {pv_count} PV(s) ({group_count} group) — starting PVA listener"
);
server
.run_with_source(store)
.await
.map_err(|e| e.to_string())
}