use crate::client::Client;
use crate::error::Error;
use nordnet_model::models::main_search::MainSearchResponse;
fn build_search_query(
query: &str,
instrument_group: Option<&[&str]>,
limit: Option<i32>,
offset: Option<i32>,
search_space: Option<&str>,
) -> String {
let mut url = match reqwest::Url::parse("http://_/") {
Ok(u) => u,
Err(_) => return format!("query={}", urlencoding_minimal(query)),
};
{
let mut pairs = url.query_pairs_mut();
pairs.append_pair("query", query);
if let Some(groups) = instrument_group {
for g in groups {
pairs.append_pair("instrument_group", g);
}
}
if let Some(l) = limit {
pairs.append_pair("limit", &l.to_string());
}
if let Some(o) = offset {
pairs.append_pair("offset", &o.to_string());
}
if let Some(s) = search_space {
pairs.append_pair("search_space", s);
}
}
url.query().unwrap_or("").to_owned()
}
fn urlencoding_minimal(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for byte in s.bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
out.push(byte as char);
}
_ => out.push_str(&format!("%{:02X}", byte)),
}
}
out
}
impl Client {
#[doc(alias = "GET /main_search")]
pub async fn search(
&self,
query: &str,
instrument_group: Option<&[&str]>,
limit: Option<i32>,
offset: Option<i32>,
search_space: Option<&str>,
) -> Result<Vec<MainSearchResponse>, Error> {
let qs = build_search_query(query, instrument_group, limit, offset, search_space);
let path = format!("/main_search?{qs}");
match self.get::<Vec<MainSearchResponse>>(&path).await {
Ok(v) => Ok(v),
Err(Error::Decode { ref body, .. }) if body.trim().is_empty() => Ok(vec![]),
Err(e) => Err(e),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_query_required_only() {
let qs = build_search_query("Volvo", None, None, None, None);
assert_eq!(qs, "query=Volvo");
}
#[test]
fn build_query_all_params() {
let qs = build_search_query(
"Volvo B",
Some(&["EQUITY", "ETF"]),
Some(10),
Some(5),
Some("INSTRUMENTS"),
);
assert_eq!(
qs,
"query=Volvo+B&instrument_group=EQUITY&instrument_group=ETF&limit=10&offset=5&search_space=INSTRUMENTS"
);
}
#[test]
fn build_query_percent_encodes_special_chars() {
let qs = build_search_query("a&b=c", None, None, None, None);
assert_eq!(qs, "query=a%26b%3Dc");
}
#[test]
fn build_query_empty_instrument_group_omits_param() {
let qs = build_search_query("x", Some(&[]), None, None, None);
assert_eq!(qs, "query=x");
}
}