taxy_api/
log.rs

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}