use crate::benchmark::types::ParseResult;
#[cfg(feature = "hgvs-rs")]
use std::sync::Arc;
#[cfg(feature = "hgvs-rs")]
use crate::benchmark::hgvs_rs::{
HgvsRsConfig as BenchmarkHgvsRsConfig, HgvsRsNormalizer, HgvsRsResult,
};
use crate::service::{
config::HgvsRsConfig,
tools::HgvsToolService,
types::{health_check::HealthCheckResult, ServiceError, ToolName},
};
pub struct HgvsRsService {
_config: HgvsRsConfig,
#[cfg(feature = "hgvs-rs")]
normalizer: Arc<HgvsRsNormalizer>,
}
impl HgvsRsService {
pub fn new(config: &HgvsRsConfig) -> Result<Self, ServiceError> {
#[cfg(feature = "hgvs-rs")]
{
let benchmark_config = BenchmarkHgvsRsConfig {
uta_db_url: config.uta_url.clone(),
uta_db_schema: config.uta_schema.clone(),
seqrepo_path: config.seqrepo_path.to_string_lossy().to_string(),
lrg_mapping_file: config
.lrg_mapping_file
.as_ref()
.map(|p| p.to_string_lossy().to_string()),
in_memory: false,
};
let normalizer = HgvsRsNormalizer::new(&benchmark_config).map_err(|e| {
ServiceError::ConfigError(format!("Failed to initialize HGVS-RS: {}", e))
})?;
Ok(Self {
_config: config.clone(),
normalizer: Arc::new(normalizer),
})
}
#[cfg(not(feature = "hgvs-rs"))]
{
let _ = config; Err(ServiceError::ConfigError(
"HGVS-RS integration requires the 'hgvs-rs' feature to be enabled".to_string(),
))
}
}
#[cfg(feature = "hgvs-rs")]
async fn run_hgvs_rs(
&self,
hgvs: &str,
_is_normalize: bool,
) -> Result<ParseResult, ServiceError> {
let hgvs = hgvs.to_string();
let normalizer = Arc::clone(&self.normalizer);
let hgvs_result = tokio::task::spawn_blocking(move || normalizer.normalize(&hgvs))
.await
.map_err(|e| ServiceError::InternalError(format!("Task join error: {}", e)))?;
Ok(convert_hgvs_rs_result_to_parse_result(hgvs_result))
}
#[cfg(not(feature = "hgvs-rs"))]
async fn run_hgvs_rs(
&self,
hgvs: &str,
_is_normalize: bool,
) -> Result<ParseResult, ServiceError> {
Ok(ParseResult {
input: hgvs.to_string(),
success: false,
output: None,
error: Some("HGVS-RS feature not enabled".to_string()),
error_category: Some("feature_disabled".to_string()),
ref_mismatch: None,
details: None,
})
}
}
#[async_trait::async_trait]
impl HgvsToolService for HgvsRsService {
async fn parse(&self, hgvs: &str) -> Result<ParseResult, ServiceError> {
self.run_hgvs_rs(hgvs, false).await
}
async fn normalize(&self, hgvs: &str) -> Result<ParseResult, ServiceError> {
self.run_hgvs_rs(hgvs, true).await
}
async fn health_check(&self) -> HealthCheckResult {
#[cfg(feature = "hgvs-rs")]
{
let test_variant = "NM_000088.4:c.589G>T";
match self.run_hgvs_rs(test_variant, false).await {
Ok(result) => {
if result.success {
HealthCheckResult::Healthy
} else if let Some(error) = &result.error {
if error.contains("not found")
|| error.contains("transcript")
|| error.contains("validation")
{
HealthCheckResult::Degraded {
reason: "Tool working but reference data may be incomplete"
.to_string(),
}
} else {
HealthCheckResult::Unhealthy {
reason: format!("HGVS-RS health check failed: {}", error),
}
}
} else {
HealthCheckResult::Unhealthy {
reason: "Unknown error".to_string(),
}
}
}
Err(e) => HealthCheckResult::Unhealthy {
reason: format!("HGVS-RS health check failed: {}", e),
},
}
}
#[cfg(not(feature = "hgvs-rs"))]
{
HealthCheckResult::Unhealthy {
reason: "HGVS-RS feature not enabled".to_string(),
}
}
}
fn tool_name(&self) -> ToolName {
ToolName::HgvsRs
}
}
#[cfg(feature = "hgvs-rs")]
fn convert_hgvs_rs_result_to_parse_result(result: HgvsRsResult) -> ParseResult {
ParseResult {
input: result.input,
success: result.success,
output: result.output,
error: result.error,
error_category: None, ref_mismatch: None, details: None, }
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_hgvs_rs_service_creation_without_feature() {
let config = HgvsRsConfig {
enabled: true,
uta_url: "postgresql://anonymous:anonymous@localhost:5432/uta/uta_20210129b"
.to_string(),
uta_schema: "uta_20210129b".to_string(),
seqrepo_path: PathBuf::from("/tmp"),
lrg_mapping_file: None,
parallel_workers: Some(1),
};
#[cfg(not(feature = "hgvs-rs"))]
{
let result = HgvsRsService::new(&config);
assert!(result.is_err());
if let Err(ServiceError::ConfigError(msg)) = result {
assert!(msg.contains("hgvs-rs"));
}
}
#[cfg(feature = "hgvs-rs")]
{
let result = HgvsRsService::new(&config);
}
}
#[tokio::test]
async fn test_hgvs_rs_tool_name() {
let _config = HgvsRsConfig {
enabled: true,
uta_url: "postgresql://test@localhost:5432/uta/uta_20210129b".to_string(),
uta_schema: "uta_20210129b".to_string(),
seqrepo_path: PathBuf::from("/tmp"),
lrg_mapping_file: None,
parallel_workers: Some(1),
};
assert_eq!("hgvs-rs", "hgvs-rs"); }
}