use serde::{Serialize, Deserialize};
use serde_json::Value;
use std::collections::{HashMap, HashSet};
use crate::IdKind;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Row {
#[serde(skip)]
pub table: Option<String>,
#[serde(skip)]
pub primary_keys: Vec<String>,
pub data: HashMap<String, Value>,
#[serde(skip)]
pub changes: HashSet<String>,
}
impl Row {
pub fn table(name: impl Into<String>) -> Self {
Self {
table: Some(name.into()),
primary_keys: vec!["id".to_string()],
..Default::default()
}
}
pub fn primary_key(mut self, key: impl Into<String>) -> Self {
self.primary_keys = vec![key.into()];
self
}
pub fn primary_keys(mut self, keys: &[&str]) -> Self {
self.primary_keys = keys.iter().map(|k| k.to_string()).collect();
self
}
pub fn id<V: Into<Value>>(mut self, id: V) -> Self {
let pk = self.primary_keys.first()
.cloned()
.unwrap_or_else(|| "id".to_string());
self.set(&pk, id);
self
}
pub fn set<V: Into<Value>>(&mut self, key: &str, value: V) -> &mut Self {
self.data.insert(key.to_string(), value.into());
self.changes.insert(key.to_string());
self
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.data.get(key)
}
pub fn get_as<'a, T: Deserialize<'a>>(&'a self, key: &str) -> Option<T> {
self.data.get(key)
.and_then(|v| T::deserialize(v).ok())
}
pub fn get_id(&self) -> Option<&Value> {
self.primary_keys
.first()
.and_then(|pk| self.data.get(pk))
}
pub fn mark_all_changed(&mut self) {
self.changes = self.data.keys().cloned().collect();
}
pub fn has(&self, key: &str) -> bool {
self.data.contains_key(key)
}
pub fn to_json(&self) -> serde_json::Result<String> {
serde_json::to_string(self)
}
pub fn from_json(json: &str) -> serde_json::Result<Self> {
serde_json::from_str(json)
}
pub fn detect_id_kind(&self) -> Option<IdKind> {
self.get_id().map(|v| IdKind::detect(v))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_row_builder() {
let mut row = Row::table("user")
.primary_key("uid")
.id("u-123");
row.set("name", "Alice")
.set("age", 25);
assert_eq!(row.table.as_deref(), Some("user"));
assert_eq!(row.primary_keys, vec!["uid"]);
assert_eq!(row.get("name").and_then(|v| v.as_str()), Some("Alice"));
assert!(row.changes.contains("name"));
assert!(row.changes.contains("age"));
}
#[test]
fn test_get_as() {
let mut row = Row::table("user").id(42);
row.set("name", "Bob");
let name: Option<String> = row.get_as("name");
assert_eq!(name, Some("Bob".into()));
let id: Option<i64> = row.get_as("id");
assert_eq!(id, Some(42));
}
}