diff options
Diffstat (limited to 'src/db.rs')
| -rw-r--r-- | src/db.rs | 67 |
1 files changed, 47 insertions, 20 deletions
@@ -1,13 +1,41 @@ -use chrono::{DateTime, Utc}; +use std::time::{Duration, UNIX_EPOCH}; use rusqlite::Connection; use rusqlite::OptionalExtension; +use rusqlite::types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; +use crate::time::{Timestamp, secs_to_ymd_hms, ymd_hms_to_secs}; + +impl ToSql for Timestamp { + fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> { + let d = self.0.duration_since(UNIX_EPOCH).unwrap(); + let secs = d.as_secs(); + let millis = d.subsec_millis(); + let (y, mo, day, h, min, s) = secs_to_ymd_hms(secs); + let ts = format!("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}", y, mo, day, h, min, s, millis); + Ok(ToSqlOutput::from(ts)) + } +} + +impl FromSql for Timestamp { + fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { + let ts = value.as_str()?; + let y: u64 = ts[0..4].parse().unwrap(); + let mo: u64 = ts[5..7].parse().unwrap(); + let d: u64 = ts[8..10].parse().unwrap(); + let h: u64 = ts[11..13].parse().unwrap(); + let min: u64 = ts[14..16].parse().unwrap(); + let s: u64 = ts[17..19].parse().unwrap(); + let ms: u64 = if ts.len() >= 23 { ts[20..23].parse().unwrap_or(0) } else { 0 }; + let secs = ymd_hms_to_secs(y, mo, d, h, min, s); + Ok(Timestamp(UNIX_EPOCH + Duration::from_secs(secs) + Duration::from_millis(ms))) + } +} #[derive(Debug, Clone)] pub struct Image { pub id: i64, pub registry: String, pub image: String, - pub expires_at: DateTime<Utc>, + pub expires_at: Timestamp, } #[derive(Debug, Clone)] @@ -20,7 +48,7 @@ pub struct Tag { pub trait Db { fn get_image(&self, registry: &str, image: &str) -> Option<Image>; - fn get_expired_images(&self, now: &DateTime<Utc>) -> Vec<Image>; + fn get_expired_images(&self, now: Timestamp) -> Vec<Image>; fn insert_images(&self, images: &mut [Image]); fn update_images(&self, images: &[Image]); @@ -161,7 +189,7 @@ impl Db for SqliteDb { } } - fn get_expired_images(&self, now: &DateTime<Utc>) -> Vec<Image> { + fn get_expired_images(&self, now: Timestamp) -> Vec<Image> { let _timer = crate::metrics::get().db_query_duration.start_timer(); let mut stmt = self.conn.prepare(" SELECT id, registry, image, expires_at FROM images WHERE expires_at <= ?1 @@ -275,10 +303,10 @@ impl Db for StubDb { } } - fn get_expired_images(&self, now: &DateTime<Utc>) -> Vec<Image> { + fn get_expired_images(&self, now: Timestamp) -> Vec<Image> { let mut result = vec![]; for img in self.images.borrow().iter() { - if img.expires_at <= *now { + if img.expires_at <= now { result.push(Image { id: img.id, registry: img.registry.clone(), @@ -331,10 +359,9 @@ impl Db for StubDb { #[cfg(test)] mod tests { use super::*; - use chrono::TimeZone; fn test_insert_images_returns_incrementing_ids(db: &dyn Db) { - let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let now = Timestamp::ZERO; let mut images = [ Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }, Image { id: 0, registry: "docker.io".into(), image: "redis".into(), expires_at: now }, @@ -348,7 +375,7 @@ mod tests { } fn test_get_image_returns_inserted(db: &dyn Db) { - let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let now = Timestamp::ZERO; let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }]; db.insert_images(&mut images); let got = db.get_image("docker.io", "nginx").unwrap(); @@ -357,14 +384,14 @@ mod tests { } fn test_get_tags_returns_empty_for_no_tags(db: &dyn Db) { - let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let now = Timestamp::ZERO; let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }]; db.insert_images(&mut images); assert!(db.get_tags_sorted(images[0].id).is_empty()); } fn test_get_tags_returns_sorted(db: &dyn Db) { - let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let now = Timestamp::ZERO; let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }]; db.insert_images(&mut images); db.insert_tags(&mut [ @@ -377,7 +404,7 @@ mod tests { } fn test_delete_tags_removes_tags(db: &dyn Db) { - let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let now = Timestamp::ZERO; let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }]; db.insert_images(&mut images); let mut tags = [ @@ -391,8 +418,8 @@ mod tests { } fn test_update_images(db: &dyn Db) { - let t1 = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); - let t2 = Utc.with_ymd_and_hms(2000, 1, 1, 1, 0, 0).unwrap(); + let t1 = Timestamp::ZERO; + let t2 = Timestamp::ZERO + Duration::from_secs(3600); let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: t1 }]; db.insert_images(&mut images); images[0].expires_at = t2; @@ -401,7 +428,7 @@ mod tests { } fn test_tag_digest_none_when_unset(db: &dyn Db) { - let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let now = Timestamp::ZERO; let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }]; db.insert_images(&mut images); db.insert_tags(&mut [Tag { id: 0, image_id: images[0].id, tag: "1.0".into(), digest: None }]); @@ -409,7 +436,7 @@ mod tests { } fn test_update_tags(db: &dyn Db) { - let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let now = Timestamp::ZERO; let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }]; db.insert_images(&mut images); let mut tags = [Tag { id: 0, image_id: images[0].id, tag: "1.0".into(), digest: None }]; @@ -420,15 +447,15 @@ mod tests { } fn test_get_expired_images(db: &dyn Db) { - let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); - let past = now - chrono::Duration::days(1); - let future = now + chrono::Duration::days(1); + let now = Timestamp::ZERO + Duration::from_secs(86400 * 30); + let past = now - Duration::from_secs(86400); + let future = now + Duration::from_secs(86400); let mut images = [ Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: past }, Image { id: 0, registry: "docker.io".into(), image: "redis".into(), expires_at: future }, ]; db.insert_images(&mut images); - let result = db.get_expired_images(&now); + let result = db.get_expired_images(now); assert_eq!(result.len(), 1); assert_eq!(result[0].id, images[0].id); assert_eq!(result[0].image, "nginx"); |
