Skip to main content

neug_rust/
database.rs

1use crate::connection::Connection;
2use crate::error::{Error, Result};
3use crate::worker::WorkerClient;
4use neug_protocol::{RequestPayload, ResponsePayload};
5use std::path::Path;
6use std::sync::Arc;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Mode {
10    ReadOnly,
11    ReadWrite,
12}
13
14impl Mode {
15    pub fn as_str(&self) -> &'static str {
16        match self {
17            Mode::ReadOnly => "read-only",
18            Mode::ReadWrite => "read-write",
19        }
20    }
21}
22
23pub struct DatabaseOptions {
24    pub db_path: String,
25    pub mode: Mode,
26    pub max_thread_num: usize,
27    pub checkpoint_on_close: bool,
28}
29
30impl Default for DatabaseOptions {
31    fn default() -> Self {
32        Self {
33            db_path: String::new(),
34            mode: Mode::ReadWrite,
35            max_thread_num: 0,
36            checkpoint_on_close: true,
37        }
38    }
39}
40
41pub struct Database {
42    db_id: u64,
43    worker: Arc<WorkerClient>,
44    options: DatabaseOptions,
45}
46
47impl Database {
48    /// Opens a database at the specified path.
49    /// If `db_path` is empty or ":memory:", the database is opened in memory.
50    pub fn open<P: AsRef<Path>>(db_path: P, mode: Mode) -> Result<Self> {
51        let options = DatabaseOptions {
52            db_path: db_path.as_ref().to_string_lossy().into_owned(),
53            mode,
54            ..Default::default()
55        };
56        Self::with_options(options)
57    }
58
59    /// Opens a database with full options.
60    pub fn with_options(options: DatabaseOptions) -> Result<Self> {
61        let worker = Arc::new(WorkerClient::spawn()?);
62
63        let res = worker.send_request(RequestPayload::OpenDb {
64            path: options.db_path.clone(),
65            mode: options.mode.as_str().to_string(),
66            max_thread_num: options.max_thread_num,
67            checkpoint_on_close: options.checkpoint_on_close,
68        })?;
69
70        match res {
71            ResponsePayload::OkDb { db_id } => Ok(Self {
72                db_id,
73                worker,
74                options,
75            }),
76            ResponsePayload::Error(msg) => Err(Error::InitializationFailed(msg)),
77            _ => Err(Error::InitializationFailed("Unexpected response".into())),
78        }
79    }
80
81    /// Get the mode of the database.
82    pub fn mode(&self) -> Mode {
83        self.options.mode
84    }
85
86    /// Creates a new connection to the database.
87    pub fn connect(&self) -> Result<Connection> {
88        let res = self
89            .worker
90            .send_request(RequestPayload::Connect { db_id: self.db_id })?;
91
92        match res {
93            ResponsePayload::OkConn { conn_id } => {
94                Ok(Connection::new(conn_id, self.worker.clone()))
95            }
96            ResponsePayload::Error(msg) => Err(Error::InitializationFailed(msg)),
97            _ => Err(Error::InitializationFailed("Unexpected response".into())),
98        }
99    }
100
101    /// Close the database.
102    pub fn close(&mut self) {
103        if self.db_id != 0 {
104            let _ = self
105                .worker
106                .send_request(RequestPayload::CloseDb { db_id: self.db_id });
107            self.db_id = 0;
108        }
109    }
110}
111
112impl Drop for Database {
113    fn drop(&mut self) {
114        self.close();
115    }
116}