cima_rs/endpoints/
medications.rs1use crate::api_client::CimaClient;
2use crate::models::{Medication, MedicationSummary};
3use anyhow::{Context, Result};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Default, Clone)]
8pub struct SearchMedicationsParams {
9 pub name: Option<String>,
11 pub laboratory: Option<String>,
13 pub active_ingredient_1: Option<String>,
15 pub active_ingredient_2: Option<String>,
17 pub active_ingredient_1_id: Option<i32>,
19 pub active_ingredient_2_id: Option<i32>,
21 pub national_code: Option<String>,
23 pub atc: Option<String>,
25 pub registration_number: Option<String>,
27 pub active_ingredient_count: Option<i32>,
29 pub black_triangle: Option<u8>,
31 pub orphan: Option<u8>,
33 pub biosimilar: Option<u8>,
35 pub substitutable_type: Option<u8>,
37 pub vmp: Option<String>,
39 pub commercialized: Option<u8>,
41 pub authorized: Option<u8>,
43 pub prescription: Option<u8>,
45 pub narcotic: Option<u8>,
47 pub psychotropic: Option<u8>,
49 pub narcotic_or_psychotropic: Option<u8>,
51 pub page: Option<u32>,
53}
54
55impl SearchMedicationsParams {
56 pub fn new() -> Self {
57 Self::default()
58 }
59
60 pub(crate) fn to_query_params(&self) -> Vec<(&str, String)> {
62 let mut params = Vec::new();
63
64 if let Some(ref v) = self.name {
65 params.push(("nombre", v.clone()));
66 }
67 if let Some(ref v) = self.laboratory {
68 params.push(("laboratorio", v.clone()));
69 }
70 if let Some(ref v) = self.active_ingredient_1 {
71 params.push(("practiv1", v.clone()));
72 }
73 if let Some(ref v) = self.active_ingredient_2 {
74 params.push(("practiv2", v.clone()));
75 }
76 if let Some(v) = self.active_ingredient_1_id {
77 params.push(("idpractiv1", v.to_string()));
78 }
79 if let Some(v) = self.active_ingredient_2_id {
80 params.push(("idpractiv2", v.to_string()));
81 }
82 if let Some(ref v) = self.national_code {
83 params.push(("cn", v.clone()));
84 }
85 if let Some(ref v) = self.atc {
86 params.push(("atc", v.clone()));
87 }
88 if let Some(ref v) = self.registration_number {
89 params.push(("nregistro", v.clone()));
90 }
91 if let Some(v) = self.active_ingredient_count {
92 params.push(("npactiv", v.to_string()));
93 }
94 if let Some(v) = self.black_triangle {
95 params.push(("triangulo", v.to_string()));
96 }
97 if let Some(v) = self.orphan {
98 params.push(("huerfano", v.to_string()));
99 }
100 if let Some(v) = self.biosimilar {
101 params.push(("biosimilar", v.to_string()));
102 }
103 if let Some(v) = self.substitutable_type {
104 params.push(("sust", v.to_string()));
105 }
106 if let Some(ref v) = self.vmp {
107 params.push(("vmp", v.clone()));
108 }
109 if let Some(v) = self.commercialized {
110 params.push(("comerc", v.to_string()));
111 }
112 if let Some(v) = self.authorized {
113 params.push(("autorizados", v.to_string()));
114 }
115 if let Some(v) = self.prescription {
116 params.push(("receta", v.to_string()));
117 }
118 if let Some(v) = self.narcotic {
119 params.push(("estupefaciente", v.to_string()));
120 }
121 if let Some(v) = self.psychotropic {
122 params.push(("psicotropo", v.to_string()));
123 }
124 if let Some(v) = self.narcotic_or_psychotropic {
125 params.push(("estuopsico", v.to_string()));
126 }
127 if let Some(v) = self.page {
128 params.push(("pagina", v.to_string()));
129 }
130
131 params
132 }
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct TechnicalSheetQuery {
138 #[serde(rename = "seccion")]
140 pub section: String,
141 #[serde(rename = "texto")]
143 pub text: String,
144 #[serde(rename = "contiene")]
146 pub contains: u8,
147}
148
149impl CimaClient {
150 pub async fn get_medication(
152 &self,
153 registration_number: Option<&str>,
154 national_code: Option<&str>,
155 ) -> Result<Medication> {
156 let mut params = Vec::new();
157
158 if let Some(nr) = registration_number {
159 params.push(("nregistro", nr.to_string()));
160 }
161 if let Some(cn) = national_code {
162 params.push(("cn", cn.to_string()));
163 }
164
165 if params.is_empty() {
166 anyhow::bail!("Must provide either registration_number or national_code");
167 }
168
169 self.get_with_params("medicamento", ¶ms)
170 .await
171 .context("Failed to get medication")
172 }
173
174 pub async fn search_medications(
178 &self,
179 params: &SearchMedicationsParams,
180 ) -> Result<crate::models::PaginatedResponse<MedicationSummary>> {
181 let query_params = params.to_query_params();
182
183 self.get_with_params("medicamentos", &query_params)
184 .await
185 .context("Failed to search medications")
186 }
187
188 pub async fn search_in_technical_sheet(
190 &self,
191 queries: &[TechnicalSheetQuery],
192 ) -> Result<Vec<MedicationSummary>> {
193 self.post("buscarEnFichaTecnica", queries)
194 .await
195 .context("Failed to search in technical sheet")
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn test_search_params_to_query() {
205 let params = SearchMedicationsParams {
206 name: Some("Paracetamol".to_string()),
207 black_triangle: Some(1),
208 page: Some(2),
209 ..Default::default()
210 };
211
212 let query = params.to_query_params();
213 assert_eq!(query.len(), 3);
214 assert!(
215 query
216 .iter()
217 .any(|(k, v)| k == &"nombre" && v == "Paracetamol")
218 );
219 assert!(query.iter().any(|(k, v)| k == &"triangulo" && v == "1"));
220 assert!(query.iter().any(|(k, v)| k == &"pagina" && v == "2"));
221 }
222
223 #[test]
224 fn test_technical_sheet_query_serialization() {
225 let query = TechnicalSheetQuery {
226 section: "4.1".to_string(),
227 text: "cáncer".to_string(),
228 contains: 1,
229 };
230
231 let json = serde_json::to_string(&query).unwrap();
232 assert!(json.contains("4.1"));
233 assert!(json.contains("cáncer"));
234 }
235}