pubmed_client/pubmed/client/
egquery.rs1use crate::error::{PubMedError, Result};
4use crate::pubmed::models::{DatabaseCount, GlobalQueryResults};
5use tracing::{debug, info, instrument};
6
7use super::PubMedClient;
8
9impl PubMedClient {
10 #[instrument(skip(self))]
40 pub async fn global_query(&self, term: &str) -> Result<GlobalQueryResults> {
41 let term = term.trim();
42 if term.is_empty() {
43 return Err(PubMedError::InvalidQuery(
44 "Search term cannot be empty".to_string(),
45 ));
46 }
47
48 debug!(term = %term, "Making EGQuery API request");
49 let response = self.get_eutils("egquery.fcgi", &[("term", term)]).await?;
50 let xml_text = response.text().await?;
51
52 let results = Self::parse_egquery_response(&xml_text, term)?;
54
55 info!(
56 term = %term,
57 database_count = results.results.len(),
58 non_zero_count = results.non_zero().len(),
59 "EGQuery completed"
60 );
61
62 Ok(results)
63 }
64
65 pub(crate) fn parse_egquery_response(
67 xml: &str,
68 query_term: &str,
69 ) -> Result<GlobalQueryResults> {
70 use crate::pubmed::client::xml_text::{extract_all_text_between, extract_text_between};
71
72 let term = extract_text_between(xml, "<Term>", "</Term>")
74 .unwrap_or_else(|| query_term.to_string());
75
76 let result_items = extract_all_text_between(xml, "<ResultItem>", "</ResultItem>");
78
79 let mut results = Vec::new();
80 for item in &result_items {
81 let db_name = extract_text_between(item, "<DbName>", "</DbName>").unwrap_or_default();
82 let menu_name =
83 extract_text_between(item, "<MenuName>", "</MenuName>").unwrap_or_default();
84 let count_str = extract_text_between(item, "<Count>", "</Count>").unwrap_or_default();
85 let status = extract_text_between(item, "<Status>", "</Status>").unwrap_or_default();
86
87 let count = count_str.parse::<u64>().unwrap_or(0);
88
89 results.push(DatabaseCount {
90 db_name,
91 menu_name,
92 count,
93 status,
94 });
95 }
96
97 Ok(GlobalQueryResults { term, results })
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_parse_egquery_response() {
107 let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
108<Result>
109 <Term>asthma</Term>
110 <eGQueryResult>
111 <ResultItem>
112 <DbName>pubmed</DbName>
113 <MenuName>PubMed</MenuName>
114 <Count>234567</Count>
115 <Status>Ok</Status>
116 </ResultItem>
117 <ResultItem>
118 <DbName>pmc</DbName>
119 <MenuName>PMC</MenuName>
120 <Count>89012</Count>
121 <Status>Ok</Status>
122 </ResultItem>
123 <ResultItem>
124 <DbName>mesh</DbName>
125 <MenuName>MeSH</MenuName>
126 <Count>0</Count>
127 <Status>Ok</Status>
128 </ResultItem>
129 </eGQueryResult>
130</Result>"#;
131
132 let result = PubMedClient::parse_egquery_response(xml, "asthma").unwrap();
133 assert_eq!(result.term, "asthma");
134 assert_eq!(result.results.len(), 3);
135
136 assert_eq!(result.results[0].db_name, "pubmed");
137 assert_eq!(result.results[0].menu_name, "PubMed");
138 assert_eq!(result.results[0].count, 234567);
139 assert_eq!(result.results[0].status, "Ok");
140
141 assert_eq!(result.results[1].db_name, "pmc");
142 assert_eq!(result.results[1].count, 89012);
143
144 let non_zero = result.non_zero();
146 assert_eq!(non_zero.len(), 2); assert_eq!(result.count_for("pubmed"), Some(234567));
149 assert_eq!(result.count_for("pmc"), Some(89012));
150 assert_eq!(result.count_for("mesh"), Some(0));
151 assert_eq!(result.count_for("nonexistent"), None);
152 }
153
154 #[test]
155 fn test_parse_egquery_response_empty() {
156 let xml = r#"<Result><Term>test</Term><eGQueryResult></eGQueryResult></Result>"#;
157 let result = PubMedClient::parse_egquery_response(xml, "test").unwrap();
158 assert_eq!(result.term, "test");
159 assert!(result.results.is_empty());
160 }
161
162 #[test]
163 fn test_parse_egquery_response_error_status() {
164 let xml = r#"<Result>
165 <Term>test</Term>
166 <eGQueryResult>
167 <ResultItem>
168 <DbName>pubmed</DbName>
169 <MenuName>PubMed</MenuName>
170 <Count>100</Count>
171 <Status>Ok</Status>
172 </ResultItem>
173 <ResultItem>
174 <DbName>snp</DbName>
175 <MenuName>SNP</MenuName>
176 <Count>0</Count>
177 <Status>Term or Database is not found</Status>
178 </ResultItem>
179 </eGQueryResult>
180</Result>"#;
181 let result = PubMedClient::parse_egquery_response(xml, "test").unwrap();
182 assert_eq!(result.results.len(), 2);
183 assert_eq!(result.results[1].status, "Term or Database is not found");
184 }
185
186 #[test]
187 fn test_global_query_empty_term() {
188 use tokio_test;
189 let client = PubMedClient::new();
190 let result = tokio_test::block_on(client.global_query(""));
191 assert!(result.is_err());
192 }
193
194 #[test]
195 fn test_global_query_whitespace_term() {
196 use tokio_test;
197 let client = PubMedClient::new();
198 let result = tokio_test::block_on(client.global_query(" "));
199 assert!(result.is_err());
200 }
201}