use rustlavel_core::Json;
#[derive(Debug, Clone, PartialEq)]
pub struct Entry {
pub id: i64,
pub user_id: Option<i64>,
pub user_name: Option<String>,
pub event: String,
pub model_type: Option<String>,
pub model_id: Option<String>,
pub description: Option<String>,
pub properties: Json,
pub ip_address: Option<String>,
pub user_agent: Option<String>,
pub created_at: String,
}
impl Default for Entry {
fn default() -> Entry {
Entry {
id: 0,
user_id: None,
user_name: None,
event: String::new(),
model_type: None,
model_id: None,
description: None,
properties: Json::Null,
ip_address: None,
user_agent: None,
created_at: String::new(),
}
}
}
impl Entry {
pub fn new(event: impl Into<String>) -> Entry {
Entry { event: event.into(), ..Default::default() }
}
pub fn summary(&self) -> String {
if let Some(description) = self.description.as_deref().filter(|d| !d.is_empty()) {
return description.to_string();
}
let who = self.user_name.as_deref().unwrap_or("Somebody");
match (&self.model_type, &self.model_id) {
(Some(kind), Some(id)) => format!("{who} performed {} on {kind} #{id}", self.event),
_ => format!("{who} performed {}", self.event),
}
}
}
pub struct Builder {
pub(crate) trail: crate::store::Trail,
pub(crate) entry: Entry,
}
impl Builder {
pub fn by(mut self, id: i64, name: impl Into<String>) -> Builder {
self.entry.user_id = Some(id);
self.entry.user_name = Some(name.into());
self
}
pub fn named(mut self, name: impl Into<String>) -> Builder {
self.entry.user_name = Some(name.into());
self
}
pub fn on(mut self, kind: impl Into<String>, id: impl std::fmt::Display) -> Builder {
self.entry.model_type = Some(kind.into());
self.entry.model_id = Some(id.to_string());
self
}
pub fn describe(mut self, description: impl Into<String>) -> Builder {
self.entry.description = Some(description.into());
self
}
pub fn from_ip(mut self, ip: impl Into<String>) -> Builder {
self.entry.ip_address = Some(ip.into());
self
}
pub fn user_agent(mut self, agent: impl Into<String>) -> Builder {
self.entry.user_agent = Some(agent.into());
self
}
pub fn with(mut self, key: &str, value: impl Into<Json>) -> Builder {
let mut properties = match std::mem::replace(&mut self.entry.properties, Json::Null) {
Json::Object(map) => map,
_ => Default::default(),
};
properties.insert(key.to_string(), value.into());
self.entry.properties = Json::Object(properties);
self
}
pub fn changed(self, from: impl Into<Json>, to: impl Into<Json>) -> Builder {
self.with("from", from).with("to", to)
}
pub fn entry(&self) -> &Entry {
&self.entry
}
pub async fn save(self) -> rustlavel_core::Result<i64> {
self.trail.write(self.entry).await
}
pub async fn record(self) {
let event = self.entry.event.clone();
if let Err(error) = self.save().await {
rustlavel_core::warn!("could not write the audit entry for {event}: {error}");
}
}
}