Skip to main content

oxide_framework_mongodb/
lib.rs

1use async_trait::async_trait;
2use mongodb::{Client, Database};
3use oxide_framework_core::{App, FrameworkError, ReadinessCheck};
4
5#[derive(Clone, Debug)]
6pub struct MongoConfig {
7    pub uri: String,
8    pub database: String,
9    pub strict: bool,
10}
11
12impl MongoConfig {
13    pub fn new(uri: impl Into<String>, database: impl Into<String>) -> Self {
14        Self {
15            uri: uri.into(),
16            database: database.into(),
17            strict: false,
18        }
19    }
20
21    pub fn strict(mut self, strict: bool) -> Self {
22        self.strict = strict;
23        self
24    }
25}
26
27#[derive(Clone)]
28pub struct MongoHandle {
29    pub client: Client,
30    pub db: Database,
31}
32
33impl MongoHandle {
34    pub async fn connect(config: &MongoConfig) -> Result<Self, FrameworkError> {
35        let client = Client::with_uri_str(&config.uri)
36            .await
37            .map_err(|e| FrameworkError::Internal(format!("mongodb connect failed: {e}")))?;
38        let db = client.database(&config.database);
39        Ok(Self { client, db })
40    }
41
42    pub async fn ping(&self) -> Result<(), FrameworkError> {
43        self.db
44            .run_command(mongodb::bson::doc! { "ping": 1 })
45            .await
46            .map(|_| ())
47            .map_err(|e| FrameworkError::ReadinessFailed {
48                check: "mongodb",
49                message: e.to_string(),
50            })
51    }
52}
53
54#[derive(Clone)]
55struct MongoReady(MongoHandle);
56
57#[async_trait]
58impl ReadinessCheck for MongoReady {
59    fn name(&self) -> &'static str {
60        "mongodb"
61    }
62
63    async fn check(&self) -> Result<(), FrameworkError> {
64        self.0.ping().await
65    }
66}
67
68pub trait AppMongoExt {
69    fn mongodb(self, config: MongoConfig) -> Self;
70}
71
72impl AppMongoExt for App {
73    fn mongodb(self, config: MongoConfig) -> Self {
74        let strict = config.strict;
75        let cfg_for_init = config.clone();
76        let handle = std::thread::spawn(move || {
77            let rt = tokio::runtime::Runtime::new()
78                .expect("failed to create runtime for mongodb init");
79            rt.block_on(MongoHandle::connect(&cfg_for_init))
80        })
81            .join()
82            .expect("failed to join mongodb init thread")
83            .expect("failed to initialize mongodb client");
84
85        let app = self.state(handle.clone());
86        if strict {
87            app.readiness_check(MongoReady(handle))
88        } else {
89            app
90        }
91    }
92}