Skip to main content

hal_sdk/
response.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::doc::HalDoc;
6
7/// The top-level payload returned by the HAL search endpoint.
8#[derive(Clone, Debug, Serialize, Deserialize)]
9pub struct SearchResponse {
10    /// The main response body (result count, offset and documents).
11    pub response: ResponseBody,
12    /// Facet counts, present only when the query requested facets.
13    #[serde(default)]
14    pub facet_counts: Option<FacetCounts>,
15}
16
17impl SearchResponse {
18    /// Total number of documents matching the query (across all pages).
19    pub fn num_found(&self) -> i64 {
20        self.response.num_found
21    }
22
23    /// The offset (`start`) of the current page.
24    pub fn start(&self) -> i64 {
25        self.response.start
26    }
27
28    /// The documents on the current page.
29    pub fn docs(&self) -> &[HalDoc] {
30        &self.response.docs
31    }
32
33    /// The facet counts, if any were requested.
34    pub fn facets(&self) -> Option<&FacetCounts> {
35        self.facet_counts.as_ref()
36    }
37}
38
39/// The `response` object of a HAL search payload.
40#[derive(Clone, Debug, Serialize, Deserialize)]
41pub struct ResponseBody {
42    /// Total number of matching documents.
43    #[serde(rename = "numFound")]
44    pub num_found: i64,
45    /// Offset of the first returned document (pagination cursor).
46    pub start: i64,
47    /// The documents returned for this page.
48    pub docs: Vec<HalDoc>,
49}
50
51/// Solr-style facet counts.
52///
53/// HAL returns facet fields as flat arrays alternating value and count, e.g.
54/// `["ART", 1234, "COMM", 567]`. [`FacetCounts::field`] turns them into pairs.
55#[derive(Clone, Debug, Default, Serialize, Deserialize)]
56pub struct FacetCounts {
57    /// Raw facet fields keyed by field name.
58    #[serde(default)]
59    pub facet_fields: HashMap<String, Vec<serde_json::Value>>,
60}
61
62impl FacetCounts {
63    /// Return the `(value, count)` pairs for a facet field, in the order HAL sent them.
64    pub fn field(&self, name: &str) -> Vec<(String, i64)> {
65        let Some(raw) = self.facet_fields.get(name) else {
66            return Vec::new();
67        };
68        raw.chunks_exact(2)
69            .filter_map(|pair| {
70                let value = pair[0].as_str()?.to_owned();
71                let count = pair[1].as_i64()?;
72                Some((value, count))
73            })
74            .collect()
75    }
76}