mockforge_http/
database.rs1#[cfg(feature = "database")]
7use anyhow::Result as AnyhowResult;
8#[cfg(feature = "database")]
9use sqlx::{postgres::PgPoolOptions, PgPool};
10#[cfg(feature = "database")]
11use std::sync::Arc;
12
13#[derive(Clone)]
15pub struct Database {
16 #[cfg(feature = "database")]
17 pool: Option<Arc<PgPool>>,
18 #[cfg(not(feature = "database"))]
19 _phantom: std::marker::PhantomData<()>,
20}
21
22impl Database {
23 #[cfg(feature = "database")]
29 pub async fn connect_optional(database_url: Option<&str>) -> AnyhowResult<Self> {
30 let pool = if let Some(url) = database_url {
31 if url.is_empty() {
32 None
33 } else {
34 let pool = PgPoolOptions::new().max_connections(10).connect(url).await?;
35 Some(Arc::new(pool))
36 }
37 } else {
38 None
39 };
40
41 Ok(Self { pool })
42 }
43
44 #[cfg(not(feature = "database"))]
46 pub async fn connect_optional(_database_url: Option<&str>) -> anyhow::Result<Self> {
47 Ok(Self {
48 _phantom: std::marker::PhantomData,
49 })
50 }
51
52 #[cfg(feature = "database")]
54 pub async fn migrate_if_connected(&self) -> AnyhowResult<()> {
55 if let Some(ref pool) = self.pool {
56 match sqlx::migrate!("./migrations").run(pool.as_ref()).await {
59 Ok(_) => {
60 tracing::info!("Database migrations completed successfully");
61 Ok(())
62 }
63 Err(e) => {
64 if e.to_string().contains("previously applied but is missing") {
66 tracing::warn!(
67 "Migration tracking issue (manually applied migration): {:?}",
68 e
69 );
70 tracing::info!(
71 "Continuing despite migration tracking issue - database is up to date"
72 );
73 Ok(())
74 } else {
75 Err(e.into())
76 }
77 }
78 }
79 } else {
80 tracing::debug!("No database connection, skipping migrations");
81 Ok(())
82 }
83 }
84
85 #[cfg(not(feature = "database"))]
87 pub async fn migrate_if_connected(&self) -> anyhow::Result<()> {
88 tracing::debug!("Database feature not enabled, skipping migrations");
89 Ok(())
90 }
91
92 #[cfg(feature = "database")]
94 pub fn pool(&self) -> Option<&PgPool> {
95 self.pool.as_deref()
96 }
97
98 #[cfg(not(feature = "database"))]
100 pub fn pool(&self) -> Option<()> {
101 None
102 }
103
104 pub fn is_connected(&self) -> bool {
106 #[cfg(feature = "database")]
107 {
108 self.pool.is_some()
109 }
110 #[cfg(not(feature = "database"))]
111 {
112 false
113 }
114 }
115}