Skip to main content

finance_query_core/models/
search.rs

1use 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    // **Feature: crate-extraction, Property 1: Model Serialization Round-Trip**
30    // **Validates: Requirements 2.2**
31    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}