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