use bon::Builder;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
pub enum CodeType {
#[serde(rename = "CPT")]
Cpt,
#[serde(rename = "NDC")]
Ndc,
#[serde(rename = "HCPCS")]
Hcpcs,
#[serde(rename = "RC")]
Rc,
#[serde(rename = "ICD")]
Icd,
#[serde(rename = "MS-DRG")]
MsDrg,
#[serde(rename = "R-DRG")]
RDrg,
#[serde(rename = "S-DRG")]
SDrg,
#[serde(rename = "APS-DRG")]
ApsDrg,
#[serde(rename = "AP-DRG")]
ApDrg,
#[serde(rename = "APR-DRG")]
AprDrg,
#[serde(rename = "APC")]
Apc,
#[serde(rename = "LOCAL")]
Local,
#[serde(rename = "EAPG")]
Eapg,
#[serde(rename = "HIPPS")]
Hipps,
#[serde(rename = "CDT")]
Cdt,
#[serde(rename = "CSTM-ALL")]
CstmAll,
}
impl Default for CodeType {
fn default() -> Self {
Self::Cpt
}
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct PricingRequest {
#[builder(into)]
pub npis: Vec<String>,
#[builder(into)]
pub condition_code: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(into)]
pub plan_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code_type: Option<CodeType>,
}
#[derive(Debug, Clone, Serialize, Builder)]
#[serde(rename_all = "camelCase")]
pub struct LikelihoodRequest {
#[builder(into)]
pub npis: Vec<String>,
#[builder(into)]
pub condition_code: String,
#[builder(into)]
pub code_type: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PricingResponse {
pub data: HashMap<String, Vec<RateData>>,
pub meta: PricingMeta,
}
#[derive(Debug, Clone, Deserialize)]
pub struct LikelihoodResponse {
pub data: HashMap<String, LikelihoodData>,
pub meta: LikelihoodMeta,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RateData {
pub code: String,
pub code_type: String,
pub negotiated_type: String,
pub min_rate: f64,
pub max_rate: f64,
pub avg_rate: f64,
pub instances: u32,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LikelihoodData {
pub code: String,
pub code_type: String,
pub likelihood: f64,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PricingMeta {
pub plan_id: String,
pub payer: String,
pub request_id: String,
pub timestamp: DateTime<Utc>,
pub processing_time_ms: u32,
pub in_network_records_count: u32,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LikelihoodMeta {
pub request_id: String,
pub timestamp: DateTime<Utc>,
pub processing_time_ms: u32,
pub out_of_network_records_count: u32,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorResponse {
pub error: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub details: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<DateTime<Utc>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pricing_request_builder() {
let request = PricingRequest::builder()
.npis(vec![String::from("1043566623"), String::from("1972767655")])
.condition_code("99214")
.plan_id("942404110")
.code_type(CodeType::Cpt)
.build();
assert_eq!(request.npis.len(), 2);
assert_eq!(request.condition_code, "99214");
assert_eq!(request.plan_id, Some("942404110".to_string()));
assert_eq!(request.code_type, Some(CodeType::Cpt));
}
#[test]
fn test_likelihood_request_builder() {
let request = LikelihoodRequest::builder()
.npis(vec!["1487648176".to_string()])
.condition_code("99214")
.code_type("CPT")
.build();
assert_eq!(request.npis.len(), 1);
assert_eq!(request.condition_code, "99214");
assert_eq!(request.code_type, "CPT");
}
#[test]
fn test_code_type_serialization() {
let code_type = CodeType::Cpt;
let json = serde_json::to_string(&code_type).unwrap();
assert_eq!(json, r#""CPT""#);
let deserialized: CodeType = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, CodeType::Cpt);
}
}