1use time::OffsetDateTime;
4
5#[derive(Debug, Default, Clone)]
7pub struct ReadingQuery {
8 pub device_id: Option<String>,
10 pub since: Option<OffsetDateTime>,
12 pub until: Option<OffsetDateTime>,
14 pub limit: Option<u32>,
16 pub offset: Option<u32>,
18 pub newest_first: bool,
20}
21
22impl ReadingQuery {
23 pub fn new() -> Self {
25 Self {
26 newest_first: true,
27 ..Default::default()
28 }
29 }
30
31 pub fn device(mut self, device_id: &str) -> Self {
33 self.device_id = Some(device_id.to_string());
34 self
35 }
36
37 pub fn since(mut self, time: OffsetDateTime) -> Self {
39 self.since = Some(time);
40 self
41 }
42
43 pub fn until(mut self, time: OffsetDateTime) -> Self {
45 self.until = Some(time);
46 self
47 }
48
49 pub fn limit(mut self, limit: u32) -> Self {
51 self.limit = Some(limit);
52 self
53 }
54
55 pub fn offset(mut self, offset: u32) -> Self {
57 self.offset = Some(offset);
58 self
59 }
60
61 pub fn oldest_first(mut self) -> Self {
63 self.newest_first = false;
64 self
65 }
66
67 pub(crate) fn build_where(&self) -> (String, Vec<Box<dyn rusqlite::ToSql>>) {
69 let mut conditions = Vec::new();
70 let mut params: Vec<Box<dyn rusqlite::ToSql>> = Vec::new();
71
72 if let Some(ref device_id) = self.device_id {
73 conditions.push("device_id = ?");
74 params.push(Box::new(device_id.clone()));
75 }
76
77 if let Some(since) = self.since {
78 conditions.push("captured_at >= ?");
79 params.push(Box::new(since.unix_timestamp()));
80 }
81
82 if let Some(until) = self.until {
83 conditions.push("captured_at <= ?");
84 params.push(Box::new(until.unix_timestamp()));
85 }
86
87 let where_clause = if conditions.is_empty() {
88 String::new()
89 } else {
90 format!("WHERE {}", conditions.join(" AND "))
91 };
92
93 (where_clause, params)
94 }
95
96 pub(crate) fn build_sql(&self) -> String {
98 let (where_clause, _) = self.build_where();
99 let order = if self.newest_first { "DESC" } else { "ASC" };
100
101 let mut sql = format!(
102 "SELECT id, device_id, captured_at, co2, temperature, pressure, humidity, \
103 battery, status, radon, radiation_rate, radiation_total \
104 FROM readings {} ORDER BY captured_at {}",
105 where_clause, order
106 );
107
108 if let Some(limit) = self.limit {
109 sql.push_str(&format!(" LIMIT {}", limit));
110 }
111
112 if let Some(offset) = self.offset {
113 sql.push_str(&format!(" OFFSET {}", offset));
114 }
115
116 sql
117 }
118}
119
120#[derive(Debug, Default, Clone)]
122pub struct HistoryQuery {
123 pub device_id: Option<String>,
125 pub since: Option<OffsetDateTime>,
127 pub until: Option<OffsetDateTime>,
129 pub limit: Option<u32>,
131 pub offset: Option<u32>,
133 pub newest_first: bool,
135}
136
137impl HistoryQuery {
138 pub fn new() -> Self {
140 Self {
141 newest_first: true,
142 ..Default::default()
143 }
144 }
145
146 pub fn device(mut self, device_id: &str) -> Self {
148 self.device_id = Some(device_id.to_string());
149 self
150 }
151
152 pub fn since(mut self, time: OffsetDateTime) -> Self {
154 self.since = Some(time);
155 self
156 }
157
158 pub fn until(mut self, time: OffsetDateTime) -> Self {
160 self.until = Some(time);
161 self
162 }
163
164 pub fn limit(mut self, limit: u32) -> Self {
166 self.limit = Some(limit);
167 self
168 }
169
170 pub fn offset(mut self, offset: u32) -> Self {
172 self.offset = Some(offset);
173 self
174 }
175
176 pub fn oldest_first(mut self) -> Self {
178 self.newest_first = false;
179 self
180 }
181}