Skip to main content

matomo/
request.rs

1use crate::params::{IdSite, Limit, Period, Segment};
2
3/// A reusable, standalone bag of API call parameters.
4///
5/// It owns the `(key, value)` form fields for a single Matomo API call,
6/// excluding `module`/`format`/auth which the client injects. Serializing it
7/// to its key-value pairs is reused both by the live dispatch path and by
8/// `API.getBulkRequest` composition.
9#[derive(Debug, Clone, Default, PartialEq, Eq)]
10pub struct Params {
11    fields: Vec<(String, String)>,
12}
13
14impl Params {
15    pub fn new() -> Self {
16        Params::default()
17    }
18
19    /// Set an arbitrary param. Replaces any existing value for `key`.
20    pub fn set(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
21        let key = key.into();
22        self.fields.retain(|(k, _)| k != &key);
23        self.fields.push((key, value.into()));
24        self
25    }
26
27    pub fn id_site(self, id_site: impl Into<IdSite>) -> Self {
28        self.set("idSite", id_site.into().to_param())
29    }
30
31    pub fn period(self, period: Period) -> Self {
32        let (p, date) = period.to_params();
33        self.set("period", p).set("date", date)
34    }
35
36    pub fn segment(self, segment: impl Into<Segment>) -> Self {
37        self.set("segment", segment.into().as_str().to_string())
38    }
39
40    pub fn limit(self, limit: Limit) -> Self {
41        self.set("filter_limit", limit.to_param())
42    }
43
44    pub fn offset(self, offset: u32) -> Self {
45        self.set("filter_offset", offset.to_string())
46    }
47
48    /// The raw form fields for this call (without module/format/auth).
49    pub fn fields(&self) -> &[(String, String)] {
50        &self.fields
51    }
52
53    /// Encode as a query string for embedding inside `API.getBulkRequest`'s
54    /// `urls[]` parameters (e.g. `method=Foo.bar&idSite=1`).
55    pub fn to_bulk_query(&self, method: &str) -> String {
56        let mut pairs: Vec<(&str, &str)> = vec![("method", method)];
57        for (k, v) in &self.fields {
58            pairs.push((k.as_str(), v.as_str()));
59        }
60        let encoded: Vec<String> = pairs
61            .iter()
62            .map(|(k, v)| format!("{}={}", urlencode(k), urlencode(v)))
63            .collect();
64        encoded.join("&")
65    }
66}
67
68fn urlencode(s: &str) -> String {
69    url::form_urlencoded::byte_serialize(s.as_bytes()).collect()
70}