finance_query_core/models/
search.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
4#[serde(rename_all = "snake_case")]
5pub struct SearchResult {
6 pub symbol: String,
7 pub name: String,
8 #[serde(skip_serializing_if = "Option::is_none")]
9 pub exchange: Option<String>,
10 #[serde(skip_serializing_if = "Option::is_none")]
11 pub quote_type: Option<String>,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case")]
16pub struct SearchResponse {
17 pub results: Vec<SearchResult>,
18}
19
20#[cfg(test)]
21mod tests {
22 use super::*;
23 use proptest::prelude::*;
24
25 fn optional_string() -> impl Strategy<Value = Option<String>> {
26 proptest::option::of("[A-Za-z0-9 ]{1,20}")
27 }
28
29 proptest! {
32 #![proptest_config(ProptestConfig::with_cases(100))]
33
34 #[test]
35 fn search_result_roundtrip(
36 symbol in "[A-Z]{1,5}",
37 name in "[A-Za-z ]{1,50}",
38 exchange in optional_string(),
39 quote_type in optional_string(),
40 ) {
41 let result = SearchResult {
42 symbol: symbol.clone(),
43 name: name.clone(),
44 exchange: exchange.clone(),
45 quote_type: quote_type.clone(),
46 };
47
48 let json = serde_json::to_string(&result).unwrap();
49 let parsed: SearchResult = serde_json::from_str(&json).unwrap();
50
51 prop_assert_eq!(result.symbol, parsed.symbol);
52 prop_assert_eq!(result.name, parsed.name);
53 prop_assert_eq!(result.exchange, parsed.exchange);
54 prop_assert_eq!(result.quote_type, parsed.quote_type);
55 }
56 }
57}