finance-query 3.2.0

A Rust library for querying financial data
Documentation
use crate::adapters::fmp::blank_as_none;
use crate::adapters::fmp::client::ResponseLimits;
use crate::{FinanceError, Result, SymbolMatch};
use serde::Deserialize;
use std::time::Duration;

/// Directories take about 20 s to arrive even compressed, beyond the default timeout.
const DIRECTORY_LIMITS: ResponseLimits =
    ResponseLimits::slow(32 * 1024 * 1024, Duration::from_secs(120));

#[derive(Deserialize)]
struct DirectoryRow {
    symbol: String,
    #[serde(alias = "companyName", default, deserialize_with = "blank_as_none")]
    name: Option<String>,
    #[serde(default, deserialize_with = "blank_as_none")]
    exchange: Option<String>,
    #[serde(rename = "type", default, deserialize_with = "blank_as_none")]
    stock_type: Option<String>,
}

pub(crate) async fn fetch_stock_list(active_only: bool) -> Result<Vec<SymbolMatch>> {
    let client = super::super::build_client()?;
    let path = if active_only {
        "/stable/actively-trading-list"
    } else {
        "/stable/stock-list"
    };
    let rows: Vec<DirectoryRow> = client
        .get_limited(path, &[], Some(DIRECTORY_LIMITS))
        .await?;
    rows.into_iter()
        .map(|row| {
            if row.symbol.trim().is_empty() {
                return Err(FinanceError::ResponseStructureError {
                    field: "symbol".into(),
                    context: "FMP directory contains a blank symbol".into(),
                });
            }
            Ok(SymbolMatch {
                symbol: row.symbol,
                name: row.name,
                exchange: row.exchange,
                asset_type: row.stock_type,
                active: active_only.then_some(true),
                ..Default::default()
            })
        })
        .collect()
}