Skip to main content

cortex/models/
messages.rs

1#![allow(clippy::implicit_hasher, clippy::extra_unused_lifetimes)]
2use std::fmt;
3
4use diesel::result::Error;
5use diesel::*;
6
7use crate::concerns::CortexInsertable;
8use crate::schema::log_errors;
9use crate::schema::log_fatals;
10use crate::schema::log_infos;
11use crate::schema::log_invalids;
12use crate::schema::log_warnings;
13use crate::schema::task_runtimes;
14
15use super::tasks::Task;
16
17// Log Messages
18
19#[derive(Identifiable, Queryable, AsChangeset, Associations, Clone, Debug)]
20#[diesel(belongs_to(Task))]
21/// An info/debug message, as per the `LaTeXML` convention
22pub struct LogInfo {
23  /// task primary key, auto-incremented by postgresql
24  pub id: i64,
25  /// owner task's id
26  pub task_id: i64,
27  /// mid-level description (open set; the column is nullable, hence `Option`)
28  pub category: Option<String>,
29  /// low-level description (open set; the column is nullable, hence `Option`)
30  pub what: Option<String>,
31  /// technical details of the message, e.g. localization info (nullable column)
32  pub details: Option<String>,
33}
34#[derive(Insertable, Clone, Debug)]
35#[diesel(table_name = log_infos)]
36/// A new, insertable, info/debug message
37pub struct NewLogInfo {
38  /// owner task's id
39  pub task_id: i64,
40  /// mid-level description (open set)
41  pub category: String,
42  /// low-level description (open set)
43  pub what: String,
44  /// technical details of the message (e.g. localization info)
45  pub details: String,
46}
47
48#[derive(Insertable, Clone, Debug)]
49#[diesel(table_name = task_runtimes)]
50/// A new, insertable per-task conversion runtime — the denormalized twin of the
51/// `Info:runtime_ms:<N>` log row, with the task's `service_id` inlined so the per-service
52/// runtime report aggregates without a `tasks` join. Written on the finalize path (`mark_done`).
53pub struct NewTaskRuntime {
54  /// owner task's id (primary key — one current runtime per task)
55  pub task_id: i64,
56  /// the task's service, inlined from `tasks.service_id` so the report filters/aggregates
57  /// index-only
58  pub service_id: i32,
59  /// wall-clock conversion time in milliseconds
60  pub runtime_ms: i32,
61}
62
63#[derive(Identifiable, Queryable, AsChangeset, Associations, Clone, Debug)]
64#[diesel(belongs_to(Task))]
65/// A warning message, as per the `LaTeXML` convention
66pub struct LogWarning {
67  /// task primary key, auto-incremented by postgresql
68  pub id: i64,
69  /// owner task's id
70  pub task_id: i64,
71  /// mid-level description (open set; the column is nullable, hence `Option`)
72  pub category: Option<String>,
73  /// low-level description (open set; the column is nullable, hence `Option`)
74  pub what: Option<String>,
75  /// technical details of the message, e.g. localization info (nullable column)
76  pub details: Option<String>,
77  /// when the message was recorded (server clock); auto-stamped by the column DEFAULT on insert,
78  /// so the dispatcher's `Insertable` (which omits it) is unaffected. `None` for pre-migration
79  /// rows.
80  pub recorded_at: Option<chrono::DateTime<chrono::Utc>>,
81}
82#[derive(Insertable, Clone, Debug)]
83#[diesel(table_name = log_warnings)]
84/// A new, insertable, warning message
85pub struct NewLogWarning {
86  /// owner task's id
87  pub task_id: i64,
88  /// mid-level description (open set)
89  pub category: String,
90  /// low-level description (open set)
91  pub what: String,
92  /// technical details of the message (e.g. localization info)
93  pub details: String,
94}
95
96#[derive(Identifiable, Queryable, AsChangeset, Associations, Clone, Debug)]
97#[diesel(belongs_to(Task))]
98/// An error message, as per the `LaTeXML` convention
99pub struct LogError {
100  /// task primary key, auto-incremented by postgresql
101  pub id: i64,
102  /// owner task's id
103  pub task_id: i64,
104  /// mid-level description (open set; the column is nullable, hence `Option`)
105  pub category: Option<String>,
106  /// low-level description (open set; the column is nullable, hence `Option`)
107  pub what: Option<String>,
108  /// technical details of the message, e.g. localization info (nullable column)
109  pub details: Option<String>,
110  /// when the message was recorded (server clock); auto-stamped by the column DEFAULT on insert,
111  /// so the dispatcher's `Insertable` (which omits it) is unaffected. `None` for pre-migration
112  /// rows.
113  pub recorded_at: Option<chrono::DateTime<chrono::Utc>>,
114}
115#[derive(Insertable, Clone, Debug)]
116#[diesel(table_name = log_errors)]
117/// A new, insertable, error message
118pub struct NewLogError {
119  /// owner task's id
120  pub task_id: i64,
121  /// mid-level description (open set)
122  pub category: String,
123  /// low-level description (open set)
124  pub what: String,
125  /// technical details of the message (e.g. localization info)
126  pub details: String,
127}
128
129#[derive(Identifiable, Queryable, AsChangeset, Associations, Clone, Debug)]
130#[diesel(belongs_to(Task))]
131/// A fatal message, as per the `LaTeXML` convention
132pub struct LogFatal {
133  /// task primary key, auto-incremented by postgresql
134  pub id: i64,
135  /// owner task's id
136  pub task_id: i64,
137  /// mid-level description (open set; the column is nullable, hence `Option`)
138  pub category: Option<String>,
139  /// low-level description (open set; the column is nullable, hence `Option`)
140  pub what: Option<String>,
141  /// technical details of the message, e.g. localization info (nullable column)
142  pub details: Option<String>,
143  /// when the message was recorded (server clock); auto-stamped by the column DEFAULT on insert,
144  /// so the dispatcher's `Insertable` (which omits it) is unaffected. `None` for pre-migration
145  /// rows.
146  pub recorded_at: Option<chrono::DateTime<chrono::Utc>>,
147}
148#[derive(Insertable, Clone, Debug)]
149#[diesel(table_name = log_fatals)]
150/// A new, insertable, fatal message
151pub struct NewLogFatal {
152  /// mid-level description (open set)
153  pub category: String,
154  /// owner task's id
155  pub task_id: i64,
156  /// low-level description (open set)
157  pub what: String,
158  /// technical details of the message (e.g. localization info)
159  pub details: String,
160}
161
162#[derive(Identifiable, Queryable, AsChangeset, Clone, Associations, Debug)]
163#[diesel(belongs_to(Task))]
164/// An invalid message, as per the `LaTeXML` convention
165pub struct LogInvalid {
166  /// task primary key, auto-incremented by postgresql
167  pub id: i64,
168  /// owner task's id
169  pub task_id: i64,
170  /// mid-level description (open set; the column is nullable, hence `Option`)
171  pub category: Option<String>,
172  /// low-level description (open set; the column is nullable, hence `Option`)
173  pub what: Option<String>,
174  /// technical details of the message, e.g. localization info (nullable column)
175  pub details: Option<String>,
176}
177#[derive(Insertable, Clone, Debug)]
178#[diesel(table_name = log_invalids)]
179/// A new, insertable, invalid message
180pub struct NewLogInvalid {
181  /// owner task's id
182  pub task_id: i64,
183  /// mid-level description (open set)
184  pub category: String,
185  /// low-level description (open set)
186  pub what: String,
187  /// technical details of the message (e.g. localization info)
188  pub details: String,
189}
190
191/// Log actor trait, assumes already Identifiable (for id())
192pub trait LogRecord {
193  /// Owner Task's id accessor
194  fn task_id(&self) -> i64;
195  /// Category accessor
196  fn category(&self) -> &str;
197  /// What accessor
198  fn what(&self) -> &str;
199  /// Details accessor
200  fn details(&self) -> &str;
201  /// Details setter
202  fn set_details(&mut self, new_details: String);
203  /// Severity accessor
204  fn severity(&self) -> &str;
205  /// Implements the fmt::Debug fmt
206  fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
207    writeln!(
208      f,
209      "{}(category: {},\n\twhat: {},\n\tdetails: {})",
210      self.severity(),
211      self.category(),
212      self.what(),
213      self.details()
214    )
215  }
216}
217impl fmt::Debug for dyn LogRecord {
218  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.debug(f) }
219}
220impl fmt::Display for dyn LogRecord {
221  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.debug(f) }
222}
223
224impl LogRecord for LogInfo {
225  fn task_id(&self) -> i64 { self.task_id }
226  fn category(&self) -> &str { self.category.as_deref().unwrap_or_default() }
227  fn what(&self) -> &str { self.what.as_deref().unwrap_or_default() }
228  fn details(&self) -> &str { self.details.as_deref().unwrap_or_default() }
229  fn set_details(&mut self, new_details: String) { self.details = Some(new_details); }
230  fn severity(&self) -> &str { "info" }
231}
232impl LogRecord for NewLogInfo {
233  fn task_id(&self) -> i64 { self.task_id }
234  fn category(&self) -> &str { &self.category }
235  fn what(&self) -> &str { &self.what }
236  fn details(&self) -> &str { &self.details }
237  fn set_details(&mut self, new_details: String) { self.details = new_details; }
238  fn severity(&self) -> &str { "info" }
239}
240impl CortexInsertable for NewLogInfo {
241  fn create(&self, connection: &mut PgConnection) -> Result<usize, Error> {
242    insert_into(log_infos::table)
243      .values(self)
244      .execute(connection)
245  }
246}
247impl LogRecord for LogWarning {
248  fn task_id(&self) -> i64 { self.task_id }
249  fn category(&self) -> &str { self.category.as_deref().unwrap_or_default() }
250  fn what(&self) -> &str { self.what.as_deref().unwrap_or_default() }
251  fn details(&self) -> &str { self.details.as_deref().unwrap_or_default() }
252  fn set_details(&mut self, new_details: String) { self.details = Some(new_details); }
253  fn severity(&self) -> &str { "warning" }
254}
255impl LogRecord for NewLogWarning {
256  fn task_id(&self) -> i64 { self.task_id }
257  fn category(&self) -> &str { &self.category }
258  fn what(&self) -> &str { &self.what }
259  fn details(&self) -> &str { &self.details }
260  fn set_details(&mut self, new_details: String) { self.details = new_details; }
261  fn severity(&self) -> &str { "warning" }
262}
263impl CortexInsertable for NewLogWarning {
264  fn create(&self, connection: &mut PgConnection) -> Result<usize, Error> {
265    insert_into(log_warnings::table)
266      .values(self)
267      .execute(connection)
268  }
269}
270impl LogRecord for LogError {
271  fn task_id(&self) -> i64 { self.task_id }
272  fn category(&self) -> &str { self.category.as_deref().unwrap_or_default() }
273  fn what(&self) -> &str { self.what.as_deref().unwrap_or_default() }
274  fn details(&self) -> &str { self.details.as_deref().unwrap_or_default() }
275  fn set_details(&mut self, new_details: String) { self.details = Some(new_details); }
276  fn severity(&self) -> &str { "error" }
277}
278impl LogRecord for NewLogError {
279  fn task_id(&self) -> i64 { self.task_id }
280  fn category(&self) -> &str { &self.category }
281  fn what(&self) -> &str { &self.what }
282  fn details(&self) -> &str { &self.details }
283  fn set_details(&mut self, new_details: String) { self.details = new_details; }
284  fn severity(&self) -> &str { "error" }
285}
286impl CortexInsertable for NewLogError {
287  fn create(&self, connection: &mut PgConnection) -> Result<usize, Error> {
288    insert_into(log_errors::table)
289      .values(self)
290      .execute(connection)
291  }
292}
293impl LogRecord for LogFatal {
294  fn task_id(&self) -> i64 { self.task_id }
295  fn category(&self) -> &str { self.category.as_deref().unwrap_or_default() }
296  fn what(&self) -> &str { self.what.as_deref().unwrap_or_default() }
297  fn details(&self) -> &str { self.details.as_deref().unwrap_or_default() }
298  fn set_details(&mut self, new_details: String) { self.details = Some(new_details); }
299  fn severity(&self) -> &str { "fatal" }
300}
301impl LogRecord for NewLogFatal {
302  fn task_id(&self) -> i64 { self.task_id }
303  fn category(&self) -> &str { &self.category }
304  fn what(&self) -> &str { &self.what }
305  fn details(&self) -> &str { &self.details }
306  fn set_details(&mut self, new_details: String) { self.details = new_details; }
307  fn severity(&self) -> &str { "fatal" }
308}
309impl CortexInsertable for NewLogFatal {
310  fn create(&self, connection: &mut PgConnection) -> Result<usize, Error> {
311    insert_into(log_fatals::table)
312      .values(self)
313      .execute(connection)
314  }
315}
316impl LogRecord for LogInvalid {
317  fn task_id(&self) -> i64 { self.task_id }
318  fn category(&self) -> &str { self.category.as_deref().unwrap_or_default() }
319  fn what(&self) -> &str { self.what.as_deref().unwrap_or_default() }
320  fn details(&self) -> &str { self.details.as_deref().unwrap_or_default() }
321  fn set_details(&mut self, new_details: String) { self.details = Some(new_details); }
322  fn severity(&self) -> &str { "invalid" }
323}
324impl LogRecord for NewLogInvalid {
325  fn task_id(&self) -> i64 { self.task_id }
326  fn category(&self) -> &str { &self.category }
327  fn what(&self) -> &str { &self.what }
328  fn details(&self) -> &str { &self.details }
329  fn set_details(&mut self, new_details: String) { self.details = new_details; }
330  fn severity(&self) -> &str { "invalid" }
331}
332impl CortexInsertable for NewLogInvalid {
333  fn create(&self, connection: &mut PgConnection) -> Result<usize, Error> {
334    insert_into(log_invalids::table)
335      .values(self)
336      .execute(connection)
337  }
338}