use bon::Builder;
use crate::{fundamental::entry::FundamentalRegistryEntry, models::FinancialPeriod};
#[inline]
pub fn contains_ignore_ascii_case(haystack: &str, needle: &str) -> bool {
if needle.is_empty() {
return true;
}
let n = needle.len();
if n > haystack.len() {
return false;
}
let needle_bytes = needle.as_bytes();
let haystack_bytes = haystack.as_bytes();
haystack_bytes
.windows(n)
.any(|window| window.eq_ignore_ascii_case(needle_bytes))
}
#[derive(Debug, Default, Clone, PartialEq, Builder)]
pub struct FundamentalRegistryFilter {
pub category: Option<String>,
pub name: Option<String>,
pub fund_id: Option<String>,
pub period: Option<FinancialPeriod>,
pub query: Option<String>,
}
impl FundamentalRegistryFilter {
pub fn matches(&self, entry: &FundamentalRegistryEntry) -> bool {
if let Some(cat) = &self.category {
match &entry.fundamental_category {
Some(entry_cat) => {
if !contains_ignore_ascii_case(entry_cat.as_str(), cat) {
return false;
}
}
None => return false,
}
}
if let Some(name) = &self.name
&& !contains_ignore_ascii_case(entry.script_name.as_str(), name)
{
return false;
}
if let Some(fid) = &self.fund_id
&& !contains_ignore_ascii_case(entry.fund_id.as_str(), fid)
{
return false;
}
if let Some(req_period) = &self.period
&& entry.financial_period.as_ref() != Some(req_period)
{
return false;
}
if let Some(q) = &self.query {
let matches_name = contains_ignore_ascii_case(entry.script_name.as_str(), q);
let matches_fund_id = contains_ignore_ascii_case(entry.fund_id.as_str(), q);
let matches_cat = entry
.fundamental_category
.map(|c| contains_ignore_ascii_case(c.as_str(), q))
.unwrap_or(false);
let matches_desc = entry
.short_description
.map(|d| contains_ignore_ascii_case(d.as_str(), q))
.unwrap_or(false);
if !matches_name && !matches_fund_id && !matches_cat && !matches_desc {
return false;
}
}
true
}
}