finance_query/models/lookup/
response.rs

1//! Lookup Response Model
2//!
3//! Top-level wrapper for symbol lookup results
4
5use super::LookupQuote;
6use serde::{Deserialize, Serialize};
7
8/// Raw response wrapper from Yahoo Finance lookup endpoint
9#[derive(Debug, Clone, Deserialize)]
10#[serde(rename_all = "camelCase")]
11struct RawLookupResponse {
12    finance: Option<RawFinanceResult>,
13}
14
15#[derive(Debug, Clone, Deserialize)]
16#[serde(rename_all = "camelCase")]
17struct RawFinanceResult {
18    result: Option<Vec<RawLookupResult>>,
19    #[allow(dead_code)]
20    error: Option<serde_json::Value>,
21}
22
23#[derive(Debug, Clone, Deserialize)]
24#[serde(rename_all = "camelCase")]
25struct RawLookupResult {
26    documents: Option<Vec<LookupQuote>>,
27    start: Option<i32>,
28    count: Option<i32>,
29}
30
31/// Response wrapper for lookup endpoint
32#[derive(Debug, Clone, Serialize, Deserialize)]
33#[non_exhaustive]
34#[serde(rename_all = "camelCase")]
35pub struct LookupResults {
36    /// Quote/document results
37    #[serde(default)]
38    pub quotes: Vec<LookupQuote>,
39    /// Starting index
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub start: Option<i32>,
42    /// Total result count
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub count: Option<i32>,
45}
46
47impl LookupResults {
48    /// Parse LookupResults from raw JSON value
49    ///
50    /// # Example
51    /// ```no_run
52    /// # use finance_query::LookupResults;
53    /// let json = serde_json::json!({
54    ///     "finance": {
55    ///         "result": [{
56    ///             "documents": [],
57    ///             "start": 0,
58    ///             "count": 0
59    ///         }]
60    ///     }
61    /// });
62    /// let results = LookupResults::from_json(json)?;
63    /// # Ok::<(), serde_json::Error>(())
64    /// ```
65    pub fn from_json(value: serde_json::Value) -> Result<Self, serde_json::Error> {
66        let raw: RawLookupResponse = serde_json::from_value(value)?;
67
68        let (quotes, start, count) = raw
69            .finance
70            .and_then(|f| f.result)
71            .and_then(|r| r.into_iter().next())
72            .map(|result| {
73                (
74                    result.documents.unwrap_or_default(),
75                    result.start,
76                    result.count,
77                )
78            })
79            .unwrap_or_default();
80
81        Ok(LookupResults {
82            quotes,
83            start,
84            count,
85        })
86    }
87
88    /// Get all quote results
89    pub fn quotes(&self) -> &[LookupQuote] {
90        &self.quotes
91    }
92
93    /// Get total result count
94    pub fn result_count(&self) -> i32 {
95        self.count.unwrap_or(0)
96    }
97
98    /// Check if any results were found
99    pub fn is_empty(&self) -> bool {
100        self.quotes.is_empty()
101    }
102}
103
104#[cfg(feature = "dataframe")]
105impl LookupResults {
106    /// Converts the quotes to a polars DataFrame.
107    pub fn to_dataframe(&self) -> ::polars::prelude::PolarsResult<::polars::prelude::DataFrame> {
108        LookupQuote::vec_to_dataframe(&self.quotes)
109    }
110}