pub mod accession;
pub mod edit;
pub mod fast_path;
pub mod position;
pub mod variant;
use crate::error::FerroError;
use crate::error_handling::{ErrorConfig, InputPreprocessor, ParseResultWithWarnings};
use crate::hgvs::HgvsVariant;
pub fn parse_hgvs(input: &str) -> Result<HgvsVariant, FerroError> {
variant::parse_variant(input)
}
#[inline]
pub fn parse_hgvs_fast(input: &str) -> Result<HgvsVariant, FerroError> {
let trimmed = input.trim();
match fast_path::try_fast_path(trimmed) {
fast_path::FastPathResult::Success(variant) => Ok(variant),
fast_path::FastPathResult::Fallback => variant::parse_variant(input),
}
}
pub fn parse_hgvs_with_config(
input: &str,
config: ErrorConfig,
) -> Result<ParseResultWithWarnings<HgvsVariant>, FerroError> {
let preprocessor = InputPreprocessor::new(config);
let preprocess_result = preprocessor.preprocess(input);
if !preprocess_result.success {
return Err(preprocess_result
.error
.unwrap_or_else(|| FerroError::Parse {
pos: 0,
msg: "Preprocessing failed without error details".to_string(),
diagnostic: None,
}));
}
let variant = variant::parse_variant(&preprocess_result.preprocessed)?;
Ok(ParseResultWithWarnings::new(
variant,
preprocess_result.warnings,
preprocess_result.original,
preprocess_result.preprocessed,
))
}
pub fn parse_hgvs_lenient(input: &str) -> Result<ParseResultWithWarnings<HgvsVariant>, FerroError> {
parse_hgvs_with_config(input, ErrorConfig::lenient())
}
pub fn parse_hgvs_silent(input: &str) -> Result<ParseResultWithWarnings<HgvsVariant>, FerroError> {
parse_hgvs_with_config(input, ErrorConfig::silent())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error_handling::{ErrorOverride, ErrorType};
#[test]
fn test_parse_simple_substitution() {
let result = parse_hgvs("NC_000001.11:g.12345A>G");
assert!(result.is_ok());
}
#[test]
fn test_parse_deletion() {
let result = parse_hgvs("NM_000088.3:c.459del");
assert!(result.is_ok());
}
#[test]
fn test_parse_with_config_strict_valid() {
let config = ErrorConfig::strict();
let result = parse_hgvs_with_config("NM_000088.3:c.459del", config);
assert!(result.is_ok());
let parsed = result.unwrap();
assert!(!parsed.had_corrections());
assert!(!parsed.has_warnings());
}
#[test]
fn test_parse_with_config_strict_rejects_en_dash() {
let config = ErrorConfig::strict();
let result = parse_hgvs_with_config("NM_000088.3:c.100\u{2013}200del", config);
assert!(result.is_err());
}
#[test]
fn test_parse_with_config_strict_rejects_whitespace() {
let config = ErrorConfig::strict();
let result = parse_hgvs_with_config(" NM_000088.3:c.459del ", config);
assert!(result.is_err());
}
#[test]
fn test_parse_with_config_lenient_corrects_whitespace() {
let config = ErrorConfig::lenient();
let result = parse_hgvs_with_config(" NM_000088.3:c.459del ", config);
assert!(result.is_ok());
let parsed = result.unwrap();
assert!(parsed.had_corrections());
assert!(parsed.has_warnings());
}
#[test]
fn test_parse_with_config_silent_no_warnings() {
let config = ErrorConfig::silent();
let result = parse_hgvs_with_config(" NM_000088.3:c.459del ", config);
assert!(result.is_ok());
let parsed = result.unwrap();
assert!(parsed.had_corrections());
assert!(!parsed.has_warnings()); }
#[test]
fn test_parse_with_config_override() {
let config =
ErrorConfig::lenient().with_override(ErrorType::ExtraWhitespace, ErrorOverride::Reject);
let result = parse_hgvs_with_config(" NM_000088.3:c.459del ", config);
assert!(result.is_err());
}
#[test]
fn test_parse_lenient() {
let result = parse_hgvs_lenient(" NM_000088.3:c.459del ");
assert!(result.is_ok());
assert!(result.unwrap().had_corrections());
}
#[test]
fn test_parse_silent() {
let result = parse_hgvs_silent(" NM_000088.3:c.459del ");
assert!(result.is_ok());
let parsed = result.unwrap();
assert!(parsed.had_corrections());
assert!(!parsed.has_warnings());
}
#[test]
fn test_parse_lowercase_accession_lenient() {
let result = parse_hgvs_lenient("nm_000088.3:c.459del");
assert!(result.is_ok());
let parsed = result.unwrap();
assert!(parsed.had_corrections());
assert_eq!(parsed.preprocessed_input, "NM_000088.3:c.459del");
}
}