1use serde::{Deserialize, Serialize};
2use std::{collections::HashMap, fmt::Display};
3use time::OffsetDateTime;
4use utoipa::{IntoParams, ToSchema};
5
6#[derive(Clone, Copy, Serialize, Deserialize, ToSchema)]
7#[serde(rename_all = "snake_case")]
8pub enum LogLevel {
9 Error,
10 Warn,
11 Info,
12 Debug,
13 Trace,
14}
15
16impl TryFrom<u8> for LogLevel {
17 type Error = ();
18
19 fn try_from(value: u8) -> Result<Self, ()> {
20 match value {
21 1 => Ok(LogLevel::Error),
22 2 => Ok(LogLevel::Warn),
23 3 => Ok(LogLevel::Info),
24 4 => Ok(LogLevel::Debug),
25 5 => Ok(LogLevel::Trace),
26 _ => Err(()),
27 }
28 }
29}
30
31impl Display for LogLevel {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 LogLevel::Error => write!(f, "error"),
35 LogLevel::Warn => write!(f, "warn"),
36 LogLevel::Info => write!(f, "info"),
37 LogLevel::Debug => write!(f, "debug"),
38 LogLevel::Trace => write!(f, "trace"),
39 }
40 }
41}
42
43#[derive(Clone, Serialize, Deserialize, ToSchema)]
44pub struct SystemLogRow {
45 #[serde(
46 serialize_with = "serialize_timestamp",
47 deserialize_with = "deserialize_timestamp"
48 )]
49 #[schema(value_type = u64)]
50 pub timestamp: OffsetDateTime,
51 #[schema(value_type = String, example = "info")]
52 pub level: LogLevel,
53 pub resource_id: String,
54 pub message: String,
55 pub fields: HashMap<String, String>,
56}
57
58fn serialize_timestamp<S>(timestamp: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
59where
60 S: serde::Serializer,
61{
62 serializer.serialize_i64(timestamp.unix_timestamp())
63}
64
65fn deserialize_timestamp<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
66where
67 D: serde::Deserializer<'de>,
68{
69 let timestamp = i64::deserialize(deserializer)?;
70 OffsetDateTime::from_unix_timestamp(timestamp).map_err(serde::de::Error::custom)
71}
72
73#[derive(Deserialize, IntoParams)]
74#[into_params(parameter_in = Query)]
75pub struct LogQuery {
76 #[serde(default, deserialize_with = "deserialize_time")]
77 #[param(value_type = Option<u64>)]
78 pub since: Option<OffsetDateTime>,
79 #[serde(default, deserialize_with = "deserialize_time")]
80 #[param(value_type = Option<u64>)]
81 pub until: Option<OffsetDateTime>,
82 pub limit: Option<u32>,
83}
84
85fn deserialize_time<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
86where
87 D: serde::Deserializer<'de>,
88{
89 let timestamp = Option::<i64>::deserialize(deserializer)?;
90 Ok(timestamp.and_then(|timestamp| OffsetDateTime::from_unix_timestamp(timestamp).ok()))
91}