use chrono::{DateTime, Utc}; use serde::{Deserialize, Deserializer, Serialize}; use super::issue::TrackerType; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum ScanTrigger { Scheduled, Webhook, Manual, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TrackedRepository { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option, #[serde(default)] pub name: String, #[serde(default)] pub git_url: String, #[serde(default = "default_branch")] pub default_branch: String, pub local_path: Option, pub scan_schedule: Option, #[serde(default)] pub webhook_enabled: bool, pub tracker_type: Option, pub tracker_owner: Option, pub tracker_repo: Option, pub last_scanned_commit: Option, #[serde(default, deserialize_with = "deserialize_findings_count")] pub findings_count: u32, #[serde( default = "chrono::Utc::now", deserialize_with = "deserialize_datetime" )] pub created_at: DateTime, #[serde( default = "chrono::Utc::now", deserialize_with = "deserialize_datetime" )] pub updated_at: DateTime, } fn default_branch() -> String { "main".to_string() } /// Handles findings_count stored as either a plain integer or a BSON Int64 /// which the driver may present as a map `{"low": N, "high": N, "unsigned": bool}`. /// Handles datetime stored as either a BSON DateTime or an RFC 3339 string. fn deserialize_datetime<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { let bson = bson::Bson::deserialize(deserializer)?; match bson { bson::Bson::DateTime(dt) => Ok(dt.into()), bson::Bson::String(s) => s.parse::>().map_err(serde::de::Error::custom), other => Err(serde::de::Error::custom(format!( "expected DateTime or string, got: {other:?}" ))), } } fn deserialize_findings_count<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { let bson = bson::Bson::deserialize(deserializer)?; match &bson { bson::Bson::Int32(n) => Ok(*n as u32), bson::Bson::Int64(n) => Ok(*n as u32), bson::Bson::Double(n) => Ok(*n as u32), _ => Ok(0), } } impl TrackedRepository { pub fn new(name: String, git_url: String) -> Self { let now = Utc::now(); Self { id: None, name, git_url, default_branch: "main".to_string(), local_path: None, scan_schedule: None, webhook_enabled: false, tracker_type: None, tracker_owner: None, tracker_repo: None, last_scanned_commit: None, findings_count: 0, created_at: now, updated_at: now, } } }