use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum StarAllele {
Star1,
Star2,
Star3,
Star4,
Star5,
Star6,
Star10,
Star17,
Star41,
Unknown,
}
impl StarAllele {
pub fn activity_score(&self) -> f64 {
match self {
StarAllele::Star1 | StarAllele::Star2 => 1.0,
StarAllele::Star10 | StarAllele::Star17 | StarAllele::Star41 => 0.5,
StarAllele::Star3 | StarAllele::Star4 | StarAllele::Star5 | StarAllele::Star6 => 0.0,
StarAllele::Unknown => 0.5,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum MetabolizerPhenotype {
UltraRapid,
Normal,
Intermediate,
Poor,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PharmaVariant {
pub gene: String,
pub position: u64,
pub ref_allele: u8,
pub alt_allele: u8,
pub significance: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Cyp2c19Allele {
Star1,
Star2,
Star3,
Star17,
Unknown,
}
impl Cyp2c19Allele {
pub fn activity_score(&self) -> f64 {
match self {
Cyp2c19Allele::Star1 => 1.0,
Cyp2c19Allele::Star17 => 1.5, Cyp2c19Allele::Star2 | Cyp2c19Allele::Star3 => 0.0,
Cyp2c19Allele::Unknown => 0.5,
}
}
}
pub fn call_cyp2c19_allele(variants: &[(u64, u8, u8)]) -> Cyp2c19Allele {
for &(pos, ref_allele, alt_allele) in variants {
match (pos, ref_allele, alt_allele) {
(96541616, b'G', b'A') => return Cyp2c19Allele::Star2,
(96540410, b'G', b'A') => return Cyp2c19Allele::Star3,
(96522463, b'C', b'T') => return Cyp2c19Allele::Star17,
_ => {}
}
}
Cyp2c19Allele::Star1
}
pub fn predict_cyp2c19_phenotype(
allele1: &Cyp2c19Allele,
allele2: &Cyp2c19Allele,
) -> MetabolizerPhenotype {
let total_activity = allele1.activity_score() + allele2.activity_score();
if total_activity > 2.0 {
MetabolizerPhenotype::UltraRapid
} else if total_activity >= 1.0 {
MetabolizerPhenotype::Normal
} else if total_activity >= 0.5 {
MetabolizerPhenotype::Intermediate
} else {
MetabolizerPhenotype::Poor
}
}
pub fn call_star_allele(variants: &[(u64, u8, u8)]) -> StarAllele {
for &(pos, ref_allele, alt_allele) in variants {
match (pos, ref_allele, alt_allele) {
(42130692, b'G', b'A') => return StarAllele::Star4,
(42126611, b'T', b'-') => return StarAllele::Star5,
(42127941, b'A', b'-') => return StarAllele::Star3,
(42127803, b'T', b'-') => return StarAllele::Star6,
(42126938, b'C', b'T') => return StarAllele::Star10,
_ => {}
}
}
StarAllele::Star1 }
pub fn predict_phenotype(allele1: &StarAllele, allele2: &StarAllele) -> MetabolizerPhenotype {
let total_activity = allele1.activity_score() + allele2.activity_score();
if total_activity > 2.0 {
MetabolizerPhenotype::UltraRapid
} else if total_activity >= 1.0 {
MetabolizerPhenotype::Normal
} else if total_activity >= 0.5 {
MetabolizerPhenotype::Intermediate
} else {
MetabolizerPhenotype::Poor
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DrugRecommendation {
pub drug: String,
pub gene: String,
pub recommendation: String,
pub dose_factor: f64,
}
pub fn get_recommendations(
gene: &str,
phenotype: &MetabolizerPhenotype,
) -> Vec<DrugRecommendation> {
match (gene, phenotype) {
("CYP2D6", MetabolizerPhenotype::Poor) => vec![
DrugRecommendation {
drug: "Codeine".to_string(),
gene: gene.to_string(),
recommendation:
"AVOID codeine; no conversion to morphine. Use alternative analgesic."
.to_string(),
dose_factor: 0.0,
},
DrugRecommendation {
drug: "Tramadol".to_string(),
gene: gene.to_string(),
recommendation: "AVOID tramadol; reduced efficacy. Use alternative analgesic."
.to_string(),
dose_factor: 0.0,
},
DrugRecommendation {
drug: "Tamoxifen".to_string(),
gene: gene.to_string(),
recommendation: "Consider alternative endocrine therapy (aromatase inhibitor)."
.to_string(),
dose_factor: 0.0,
},
DrugRecommendation {
drug: "Ondansetron".to_string(),
gene: gene.to_string(),
recommendation: "Use standard dose; may have increased exposure.".to_string(),
dose_factor: 0.75,
},
],
("CYP2D6", MetabolizerPhenotype::UltraRapid) => vec![
DrugRecommendation {
drug: "Codeine".to_string(),
gene: gene.to_string(),
recommendation:
"AVOID codeine; risk of fatal toxicity from ultra-rapid morphine conversion."
.to_string(),
dose_factor: 0.0,
},
DrugRecommendation {
drug: "Tramadol".to_string(),
gene: gene.to_string(),
recommendation: "AVOID tramadol; risk of respiratory depression.".to_string(),
dose_factor: 0.0,
},
],
("CYP2D6", MetabolizerPhenotype::Intermediate) => vec![
DrugRecommendation {
drug: "Codeine".to_string(),
gene: gene.to_string(),
recommendation: "Use lower dose or alternative analgesic.".to_string(),
dose_factor: 0.5,
},
DrugRecommendation {
drug: "Tamoxifen".to_string(),
gene: gene.to_string(),
recommendation: "Consider higher dose or alternative therapy.".to_string(),
dose_factor: 0.75,
},
],
("CYP2C19", MetabolizerPhenotype::Poor) => vec![
DrugRecommendation {
drug: "Clopidogrel (Plavix)".to_string(),
gene: gene.to_string(),
recommendation: "AVOID clopidogrel; use prasugrel or ticagrelor instead."
.to_string(),
dose_factor: 0.0,
},
DrugRecommendation {
drug: "Voriconazole".to_string(),
gene: gene.to_string(),
recommendation: "Reduce dose by 50%; monitor for toxicity.".to_string(),
dose_factor: 0.5,
},
DrugRecommendation {
drug: "PPIs (omeprazole)".to_string(),
gene: gene.to_string(),
recommendation: "Reduce dose; slower clearance increases exposure.".to_string(),
dose_factor: 0.5,
},
DrugRecommendation {
drug: "Escitalopram".to_string(),
gene: gene.to_string(),
recommendation: "Consider 50% dose reduction.".to_string(),
dose_factor: 0.5,
},
],
("CYP2C19", MetabolizerPhenotype::UltraRapid) => vec![
DrugRecommendation {
drug: "Clopidogrel (Plavix)".to_string(),
gene: gene.to_string(),
recommendation: "Standard dosing (enhanced activation is beneficial).".to_string(),
dose_factor: 1.0,
},
DrugRecommendation {
drug: "Omeprazole".to_string(),
gene: gene.to_string(),
recommendation: "Increase dose; rapid clearance reduces efficacy.".to_string(),
dose_factor: 2.0,
},
DrugRecommendation {
drug: "Voriconazole".to_string(),
gene: gene.to_string(),
recommendation: "Use alternative antifungal.".to_string(),
dose_factor: 0.0,
},
],
("CYP2C19", MetabolizerPhenotype::Intermediate) => vec![
DrugRecommendation {
drug: "Clopidogrel (Plavix)".to_string(),
gene: gene.to_string(),
recommendation: "Consider alternative antiplatelet or increased dose.".to_string(),
dose_factor: 1.5,
},
DrugRecommendation {
drug: "PPIs (omeprazole)".to_string(),
gene: gene.to_string(),
recommendation:
"Standard dose likely adequate; may have slightly increased exposure."
.to_string(),
dose_factor: 1.0,
},
DrugRecommendation {
drug: "Escitalopram".to_string(),
gene: gene.to_string(),
recommendation: "Use standard dose; monitor response.".to_string(),
dose_factor: 1.0,
},
],
_ => vec![DrugRecommendation {
drug: "Standard".to_string(),
gene: gene.to_string(),
recommendation: "Use standard dosing".to_string(),
dose_factor: 1.0,
}],
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_star_allele_calling() {
assert_eq!(call_star_allele(&[]), StarAllele::Star1);
let star4 = call_star_allele(&[(42130692, b'G', b'A')]);
assert_eq!(star4, StarAllele::Star4);
assert_eq!(star4.activity_score(), 0.0);
let star10 = call_star_allele(&[(42126938, b'C', b'T')]);
assert_eq!(star10, StarAllele::Star10);
assert_eq!(star10.activity_score(), 0.5);
}
#[test]
fn test_phenotype_prediction() {
assert_eq!(
predict_phenotype(&StarAllele::Star1, &StarAllele::Star1),
MetabolizerPhenotype::Normal
);
assert_eq!(
predict_phenotype(&StarAllele::Star1, &StarAllele::Star4),
MetabolizerPhenotype::Normal
);
assert_eq!(
predict_phenotype(&StarAllele::Star4, &StarAllele::Star10),
MetabolizerPhenotype::Intermediate
);
assert_eq!(
predict_phenotype(&StarAllele::Star4, &StarAllele::Star4),
MetabolizerPhenotype::Poor
);
}
#[test]
fn test_drug_recommendations() {
let recs = get_recommendations("CYP2D6", &MetabolizerPhenotype::Poor);
assert!(recs.len() >= 1);
assert_eq!(recs[0].dose_factor, 0.0);
let recs_normal = get_recommendations("CYP2D6", &MetabolizerPhenotype::Normal);
assert_eq!(recs_normal[0].dose_factor, 1.0);
}
#[test]
fn test_cyp2c19_allele_calling() {
assert_eq!(call_cyp2c19_allele(&[]), Cyp2c19Allele::Star1);
let star2 = call_cyp2c19_allele(&[(96541616, b'G', b'A')]);
assert_eq!(star2, Cyp2c19Allele::Star2);
assert_eq!(star2.activity_score(), 0.0);
let star17 = call_cyp2c19_allele(&[(96522463, b'C', b'T')]);
assert_eq!(star17, Cyp2c19Allele::Star17);
assert_eq!(star17.activity_score(), 1.5);
}
#[test]
fn test_cyp2c19_phenotype() {
assert_eq!(
predict_cyp2c19_phenotype(&Cyp2c19Allele::Star17, &Cyp2c19Allele::Star17),
MetabolizerPhenotype::UltraRapid
);
assert_eq!(
predict_cyp2c19_phenotype(&Cyp2c19Allele::Star2, &Cyp2c19Allele::Star2),
MetabolizerPhenotype::Poor
);
assert_eq!(
predict_cyp2c19_phenotype(&Cyp2c19Allele::Star1, &Cyp2c19Allele::Star2),
MetabolizerPhenotype::Normal
);
}
#[test]
fn test_cyp2c19_drug_recommendations() {
let recs = get_recommendations("CYP2C19", &MetabolizerPhenotype::Poor);
assert!(recs.len() >= 1);
assert_eq!(recs[0].drug, "Clopidogrel (Plavix)");
assert_eq!(recs[0].dose_factor, 0.0);
let recs_ultra = get_recommendations("CYP2C19", &MetabolizerPhenotype::UltraRapid);
assert!(recs_ultra.len() >= 2);
}
}