use ddd_cqrs_es::{Projection, EventEnvelope};
use spin_sdk::sqlite::{Connection, Value};
use crate::domain::{CounterEvent, CounterId};
pub struct CounterProjection {
connection_name: String,
}
impl CounterProjection {
pub fn new(connection_name: impl Into<String>) -> Self {
Self {
connection_name: connection_name.into(),
}
}
fn get_connection(&self) -> Connection {
Connection::open(&self.connection_name).unwrap()
}
pub fn initialize_schema(&self) {
let conn = self.get_connection();
conn.execute(
"CREATE TABLE IF NOT EXISTS counter_read_model (counter_id TEXT PRIMARY KEY, current_value INTEGER NOT NULL)",
&[]
).expect("Failed to initialize counter read model table");
}
}
impl Projection<CounterEvent, CounterId> for CounterProjection {
type Error = String;
fn name(&self) -> &'static str {
"counter_projection"
}
fn apply(&mut self, event: &EventEnvelope<CounterEvent, CounterId>) -> Result<(), Self::Error> {
let conn = self.get_connection();
let id_str = serde_json::to_string(&event.aggregate_id).unwrap();
match &event.payload {
CounterEvent::Incremented { amount } => {
let query = "INSERT INTO counter_read_model (counter_id, current_value) VALUES (?, ?) \
ON CONFLICT(counter_id) DO UPDATE SET current_value = current_value + ?";
conn.execute(query, &[
Value::Text(id_str),
Value::Integer(*amount as i64),
Value::Integer(*amount as i64),
]).map_err(|e| format!("{:?}", e))?;
}
CounterEvent::Decremented { amount } => {
let query = "INSERT INTO counter_read_model (counter_id, current_value) VALUES (?, ?) \
ON CONFLICT(counter_id) DO UPDATE SET current_value = current_value - ?";
conn.execute(query, &[
Value::Text(id_str),
Value::Integer(-(*amount) as i64),
Value::Integer(*amount as i64),
]).map_err(|e| format!("{:?}", e))?;
}
CounterEvent::ResetPerformed { value } => {
let query = "INSERT INTO counter_read_model (counter_id, current_value) VALUES (?, ?) \
ON CONFLICT(counter_id) DO UPDATE SET current_value = ?";
conn.execute(query, &[
Value::Text(id_str),
Value::Integer(*value as i64),
]).map_err(|e| format!("{:?}", e))?;
}
}
Ok(())
}
}