1use chrono::{DateTime, Duration, Utc};
29use serde::{Deserialize, Serialize};
30
31#[cfg(feature = "database")]
32use reinhardt_db::orm::Model;
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct SessionModel {
58 session_key: String,
60 session_data: serde_json::Value,
62 expire_date: DateTime<Utc>,
64}
65
66impl SessionModel {
67 pub fn new(session_key: String, session_data: serde_json::Value, ttl_seconds: i64) -> Self {
88 let expire_date = Utc::now() + Duration::seconds(ttl_seconds);
89 Self {
90 session_key,
91 session_data,
92 expire_date,
93 }
94 }
95
96 pub fn with_expire_date(
113 session_key: String,
114 session_data: serde_json::Value,
115 expire_date: DateTime<Utc>,
116 ) -> Self {
117 Self {
118 session_key,
119 session_data,
120 expire_date,
121 }
122 }
123
124 pub fn session_key(&self) -> &str {
136 &self.session_key
137 }
138
139 pub fn session_data(&self) -> &serde_json::Value {
152 &self.session_data
153 }
154
155 pub fn expire_date(&self) -> &DateTime<Utc> {
168 &self.expire_date
169 }
170
171 pub fn set_session_data(&mut self, data: serde_json::Value) {
184 self.session_data = data;
185 }
186
187 pub fn set_expire_date(&mut self, expire_date: DateTime<Utc>) {
201 self.expire_date = expire_date;
202 }
203
204 pub fn is_valid(&self) -> bool {
219 self.expire_date > Utc::now()
220 }
221
222 pub fn is_expired(&self) -> bool {
237 self.expire_date <= Utc::now()
238 }
239
240 pub fn extend(&mut self, seconds: i64) {
256 self.expire_date += Duration::seconds(seconds);
257 }
258
259 pub fn refresh(&mut self, ttl_seconds: i64) {
276 self.expire_date = Utc::now() + Duration::seconds(ttl_seconds);
277 }
278}
279
280#[cfg(feature = "database")]
282#[derive(Debug, Clone)]
283pub struct SessionModelFields {
284 pub session_key: reinhardt_db::orm::query_fields::Field<SessionModel, String>,
286 pub session_data: reinhardt_db::orm::query_fields::Field<SessionModel, serde_json::Value>,
288 pub expire_date: reinhardt_db::orm::query_fields::Field<SessionModel, DateTime<Utc>>,
290}
291
292#[cfg(feature = "database")]
293impl Default for SessionModelFields {
294 fn default() -> Self {
295 Self::new()
296 }
297}
298
299#[cfg(feature = "database")]
300impl SessionModelFields {
301 pub fn new() -> Self {
303 Self {
304 session_key: reinhardt_db::orm::query_fields::Field::new(vec!["session_key"]),
305 session_data: reinhardt_db::orm::query_fields::Field::new(vec!["session_data"]),
306 expire_date: reinhardt_db::orm::query_fields::Field::new(vec!["expire_date"]),
307 }
308 }
309}
310
311#[cfg(feature = "database")]
312impl reinhardt_db::orm::FieldSelector for SessionModelFields {
313 fn with_alias(mut self, alias: &str) -> Self {
314 self.session_key = self.session_key.with_alias(alias);
315 self.session_data = self.session_data.with_alias(alias);
316 self.expire_date = self.expire_date.with_alias(alias);
317 self
318 }
319}
320
321#[cfg(feature = "database")]
322impl Model for SessionModel {
323 type PrimaryKey = String;
324 type Fields = SessionModelFields;
325 type Objects = reinhardt_db::orm::Manager<Self>;
326
327 fn table_name() -> &'static str {
328 "sessions"
329 }
330
331 fn new_fields() -> Self::Fields {
332 SessionModelFields::new()
333 }
334
335 fn primary_key_field() -> &'static str {
336 "session_key"
337 }
338
339 fn primary_key(&self) -> Option<Self::PrimaryKey> {
340 Some(self.session_key.clone())
341 }
342
343 fn set_primary_key(&mut self, value: Self::PrimaryKey) {
344 self.session_key = value;
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351 use serde_json::json;
352
353 #[test]
354 fn test_session_model_new() {
355 let session = SessionModel::new("test_key".to_string(), json!({"test": "data"}), 3600);
356
357 assert_eq!(session.session_key(), "test_key");
358 assert_eq!(session.session_data(), &json!({"test": "data"}));
359 assert!(session.expire_date() > &Utc::now());
360 }
361
362 #[test]
363 fn test_session_model_with_expire_date() {
364 let expire_date = Utc::now() + Duration::hours(2);
365 let session = SessionModel::with_expire_date(
366 "custom_key".to_string(),
367 json!({"custom": "data"}),
368 expire_date,
369 );
370
371 assert_eq!(session.session_key(), "custom_key");
372 assert_eq!(session.expire_date(), &expire_date);
373 }
374
375 #[test]
376 fn test_session_model_getters() {
377 let data = json!({"user_id": 42});
378 let session = SessionModel::new("key_123".to_string(), data.clone(), 3600);
379
380 assert_eq!(session.session_key(), "key_123");
381 assert_eq!(session.session_data(), &data);
382 assert!(session.expire_date() > &Utc::now());
383 }
384
385 #[test]
386 fn test_session_model_set_session_data() {
387 let mut session = SessionModel::new("key".to_string(), json!({}), 3600);
388
389 let new_data = json!({"updated": true, "count": 42});
390 session.set_session_data(new_data.clone());
391
392 assert_eq!(session.session_data(), &new_data);
393 }
394
395 #[test]
396 fn test_session_model_set_expire_date() {
397 let mut session = SessionModel::new("key".to_string(), json!({}), 3600);
398
399 let new_expire = Utc::now() + Duration::days(7);
400 session.set_expire_date(new_expire);
401
402 assert_eq!(session.expire_date(), &new_expire);
403 }
404
405 #[test]
406 fn test_session_model_is_valid() {
407 let valid_session = SessionModel::new("valid".to_string(), json!({}), 3600);
408 assert!(valid_session.is_valid());
409
410 let expired_session = SessionModel::new("expired".to_string(), json!({}), -100);
411 assert!(!expired_session.is_valid());
412 }
413
414 #[test]
415 fn test_session_model_is_expired() {
416 let active_session = SessionModel::new("active".to_string(), json!({}), 3600);
417 assert!(!active_session.is_expired());
418
419 let expired_session = SessionModel::new("expired".to_string(), json!({}), -100);
420 assert!(expired_session.is_expired());
421 }
422
423 #[test]
424 fn test_session_model_extend() {
425 let mut session = SessionModel::new("key".to_string(), json!({}), 3600);
426 let original_expire = *session.expire_date();
427
428 session.extend(1800);
429
430 let expected_expire = original_expire + Duration::seconds(1800);
431 assert_eq!(session.expire_date(), &expected_expire);
432 }
433
434 #[test]
435 fn test_session_model_refresh() {
436 let mut session = SessionModel::new("key".to_string(), json!({}), 3600);
437
438 std::thread::sleep(std::time::Duration::from_millis(10));
440
441 session.refresh(7200);
442
443 let expected = Utc::now() + Duration::seconds(7200);
445 let diff = session
446 .expire_date()
447 .signed_duration_since(expected)
448 .num_seconds()
449 .abs();
450 assert!(diff < 2);
451 }
452
453 #[test]
454 #[cfg(feature = "database")]
455 fn test_session_model_implements_model_trait() {
456 let session = SessionModel::new("model_key".to_string(), json!({}), 3600);
457
458 assert_eq!(SessionModel::table_name(), "sessions");
459 assert_eq!(SessionModel::primary_key_field(), "session_key");
460 assert_eq!(session.primary_key(), Some("model_key".to_string()));
461 }
462
463 #[test]
464 #[cfg(feature = "database")]
465 fn test_session_model_set_primary_key() {
466 let mut session = SessionModel::new("old_key".to_string(), json!({}), 3600);
467
468 session.set_primary_key("new_key".to_string());
469 assert_eq!(session.session_key(), "new_key");
470 assert_eq!(session.primary_key(), Some("new_key".to_string()));
471 }
472
473 #[test]
474 fn test_session_model_serialization() {
475 let session =
476 SessionModel::new("serialize_test".to_string(), json!({"data": "value"}), 3600);
477
478 let serialized = serde_json::to_string(&session).unwrap();
480 assert!(serialized.contains("serialize_test"));
481
482 let deserialized: SessionModel = serde_json::from_str(&serialized).unwrap();
484 assert_eq!(deserialized.session_key(), "serialize_test");
485 assert_eq!(deserialized.session_data(), &json!({"data": "value"}));
486 }
487
488 #[test]
489 fn test_session_model_edge_cases() {
490 let short_ttl = SessionModel::new("short".to_string(), json!({}), 1);
492 assert!(short_ttl.is_valid());
493
494 let long_ttl = SessionModel::new("long".to_string(), json!({}), 86400 * 365);
496 assert!(long_ttl.is_valid());
497
498 let zero_ttl = SessionModel::new("zero".to_string(), json!({}), 0);
500 assert!(!zero_ttl.is_valid());
501 }
502}