Skip to main content

matomo/
params.rs

1use std::num::NonZeroU32;
2
3/// One or more site ids, or all sites.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum IdSite {
6    Single(u32),
7    Multiple(Vec<u32>),
8    All,
9}
10
11impl IdSite {
12    pub(crate) fn to_param(&self) -> String {
13        match self {
14            IdSite::Single(id) => id.to_string(),
15            IdSite::Multiple(ids) => ids.iter().map(u32::to_string).collect::<Vec<_>>().join(","),
16            IdSite::All => "all".to_string(),
17        }
18    }
19}
20
21impl From<u32> for IdSite {
22    fn from(id: u32) -> Self {
23        IdSite::Single(id)
24    }
25}
26
27/// A Matomo date in its query grammar.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum Date {
30    Today,
31    Yesterday,
32    /// `year, month, day` → `YYYY-MM-DD`.
33    Ymd(u16, u8, u8),
34    /// `lastN` rolling window.
35    LastN(u32),
36    /// `previousN` rolling window.
37    PreviousN(u32),
38}
39
40impl Date {
41    pub(crate) fn to_param(self) -> String {
42        match self {
43            Date::Today => "today".to_string(),
44            Date::Yesterday => "yesterday".to_string(),
45            Date::Ymd(y, m, d) => format!("{y:04}-{m:02}-{d:02}"),
46            Date::LastN(n) => format!("last{n}"),
47            Date::PreviousN(n) => format!("previous{n}"),
48        }
49    }
50}
51
52/// The end of an absolute-start range. Matomo accepts an absolute date or the
53/// `today`/`yesterday` keywords here, but not `lastN`-style keywords.
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum RangeEnd {
56    Ymd(u16, u8, u8),
57    Today,
58    Yesterday,
59}
60
61impl RangeEnd {
62    fn to_param(self) -> String {
63        match self {
64            RangeEnd::Ymd(y, m, d) => format!("{y:04}-{m:02}-{d:02}"),
65            RangeEnd::Today => "today".to_string(),
66            RangeEnd::Yesterday => "yesterday".to_string(),
67        }
68    }
69}
70
71/// A date range used with `Period::Range`. Matomo only accepts a single rolling
72/// keyword (`lastN`/`previousN`) or an absolute start paired with an absolute or
73/// `today`/`yesterday` end — a keyword start like `last7,today` is rejected, so
74/// it is unrepresentable here.
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum DateRange {
77    LastN(u32),
78    PreviousN(u32),
79    Between { from: (u16, u8, u8), to: RangeEnd },
80}
81
82impl DateRange {
83    /// Convenience constructor for an absolute `YYYY-MM-DD,YYYY-MM-DD` range.
84    pub fn ymd(from: (u16, u8, u8), to: (u16, u8, u8)) -> Self {
85        DateRange::Between {
86            from,
87            to: RangeEnd::Ymd(to.0, to.1, to.2),
88        }
89    }
90
91    pub(crate) fn to_param(self) -> String {
92        match self {
93            DateRange::LastN(n) => format!("last{n}"),
94            DateRange::PreviousN(n) => format!("previous{n}"),
95            DateRange::Between {
96                from: (y, m, d),
97                to,
98            } => format!("{y:04}-{m:02}-{d:02},{}", to.to_param()),
99        }
100    }
101}
102
103/// A period paired with the date(s) it applies to. Illegal (period, date)
104/// combinations are unrepresentable: `Range` carries its own `DateRange`,
105/// the others carry a single `Date`.
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum Period {
108    Day(Date),
109    Week(Date),
110    Month(Date),
111    Year(Date),
112    Range(DateRange),
113}
114
115impl Period {
116    /// Returns the `(period, date)` form-field pair.
117    pub(crate) fn to_params(self) -> (&'static str, String) {
118        match self {
119            Period::Day(d) => ("day", d.to_param()),
120            Period::Week(d) => ("week", d.to_param()),
121            Period::Month(d) => ("month", d.to_param()),
122            Period::Year(d) => ("year", d.to_param()),
123            Period::Range(r) => ("range", r.to_param()),
124        }
125    }
126}
127
128/// A raw segment expression. Passed verbatim in the POST body; reqwest
129/// url-encodes it for us, so do not pre-encode.
130#[derive(Debug, Clone, PartialEq, Eq)]
131pub struct Segment(pub String);
132
133impl Segment {
134    pub fn new(expr: impl Into<String>) -> Self {
135        Segment(expr.into())
136    }
137
138    pub(crate) fn as_str(&self) -> &str {
139        &self.0
140    }
141}
142
143impl From<&str> for Segment {
144    fn from(s: &str) -> Self {
145        Segment(s.to_string())
146    }
147}
148
149impl From<String> for Segment {
150    fn from(s: String) -> Self {
151        Segment(s)
152    }
153}
154
155/// `filter_limit`. `All` maps to `-1`.
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum Limit {
158    All,
159    Count(NonZeroU32),
160}
161
162impl Limit {
163    pub fn count(n: u32) -> Option<Self> {
164        NonZeroU32::new(n).map(Limit::Count)
165    }
166
167    pub(crate) fn to_param(self) -> String {
168        match self {
169            Limit::All => "-1".to_string(),
170            Limit::Count(n) => n.to_string(),
171        }
172    }
173
174    #[cfg_attr(not(feature = "reqwest"), allow(dead_code))]
175    pub(crate) fn is_all(self) -> bool {
176        matches!(self, Limit::All)
177    }
178}