use std::sync::OnceLock;
use super::parser::{parse_lino, LinoNode};
use super::MARKET_PRICE_REFERENCES_LINO;
#[derive(Debug, Clone, PartialEq)]
pub struct MarketPriceAsset {
pub ticker: String,
pub label: String,
pub grounded_in: String,
pub quote_currency: String,
pub aliases: Vec<String>,
pub references: Vec<MarketPricePeriod>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MarketPricePeriod {
pub period: String,
pub source_id: String,
pub source_label: String,
pub source_url: String,
pub observed_min_price: f64,
pub observed_min_date: String,
pub observed_max_price: f64,
pub observed_max_date: String,
}
#[must_use]
pub fn market_price_assets() -> &'static [MarketPriceAsset] {
static REGISTRY: OnceLock<Vec<MarketPriceAsset>> = OnceLock::new();
REGISTRY.get_or_init(parse_market_price_assets).as_slice()
}
fn parse_market_price_assets() -> Vec<MarketPriceAsset> {
let tree = parse_lino(MARKET_PRICE_REFERENCES_LINO);
let root = tree
.children
.iter()
.find(|node| node.name == "market_price_references")
.expect("data/seed/market-price-references.lino must declare market_price_references");
root.children
.iter()
.filter(|node| node.name == "asset")
.map(parse_asset)
.collect()
}
fn parse_asset(node: &LinoNode) -> MarketPriceAsset {
MarketPriceAsset {
ticker: node.id.clone(),
label: node.find_child_value("label").to_owned(),
grounded_in: node.find_child_value("grounded-in").to_owned(),
quote_currency: node.find_child_value("quote-currency").to_owned(),
aliases: parse_asset_aliases(node),
references: node
.children
.iter()
.filter(|child| child.name == "reference")
.map(parse_reference)
.collect(),
}
}
fn parse_asset_aliases(node: &LinoNode) -> Vec<String> {
let mut aliases = Vec::new();
for lexeme in node.children.iter().filter(|child| child.name == "lexeme") {
for surface in lexeme
.children
.iter()
.filter(|child| child.name == "surface")
{
let text = surface.find_child_value("text");
if !text.is_empty() && !aliases.iter().any(|existing| existing == text) {
aliases.push(text.to_owned());
}
}
}
aliases
}
fn parse_reference(node: &LinoNode) -> MarketPricePeriod {
MarketPricePeriod {
period: node.id.clone(),
source_id: node.find_child_value("source-id").to_owned(),
source_label: node.find_child_value("source-label").to_owned(),
source_url: node.find_child_value("source-url").to_owned(),
observed_min_price: parse_price(node.find_child_value("observed-min-price")),
observed_min_date: node.find_child_value("observed-min-date").to_owned(),
observed_max_price: parse_price(node.find_child_value("observed-max-price")),
observed_max_date: node.find_child_value("observed-max-date").to_owned(),
}
}
fn parse_price(raw: &str) -> f64 {
raw.trim().parse::<f64>().unwrap_or_default()
}