1mod adapter;
8mod contract;
9mod rows;
10
11pub mod error;
12
13pub use crate::contract::{
14 AccessMode, AtomicResult, GeneratedKey, OpenOptions, Operation, OperationKind, OperationResult,
15 StorageContext, WriteResult, STORAGE_CONTEXT_VERSION,
16};
17pub use crate::error::{AdapterErrorKind, DactylError};
18pub use crate::rows::{Parameter, Row, Rows};
19
20use crate::adapter::Adapter;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum Datastore {
25 Sqlite,
26 Neon,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct DatastoreRoute {
32 datastore: Datastore,
33 route: String,
34 token: Option<String>,
35}
36
37impl DatastoreRoute {
38 pub fn sqlite(path: impl Into<String>) -> Self {
39 Self {
40 datastore: Datastore::Sqlite,
41 route: path.into(),
42 token: None,
43 }
44 }
45
46 pub fn neon(endpoint: impl Into<String>, token: Option<String>) -> Self {
47 Self {
48 datastore: Datastore::Neon,
49 route: endpoint.into(),
50 token,
51 }
52 }
53
54 pub fn datastore(&self) -> Datastore {
55 self.datastore
56 }
57
58 pub fn route(&self) -> &str {
59 &self.route
60 }
61
62 pub fn token(&self) -> Option<&str> {
63 self.token.as_deref()
64 }
65
66 pub fn from_env() -> Result<Self, DactylError> {
68 let datastore = std::env::var("DATASTORE")
69 .map_err(|_| DactylError::Config("DATASTORE is not set: use sqlite or neon".into()))?;
70 let route = std::env::var("DATASTORE_ROUTE")
71 .map_err(|_| DactylError::Config("DATASTORE_ROUTE is not set".into()))?;
72 match datastore.as_str() {
73 "sqlite" => Ok(Self::sqlite(route)),
74 "neon" => Ok(Self::neon(route, std::env::var("DATASTORE_TOKEN").ok())),
75 other => Err(DactylError::Config(format!(
76 "invalid DATASTORE value {other:?}: use sqlite or neon"
77 ))),
78 }
79 }
80}
81
82pub struct Connection {
84 adapter: Box<dyn Adapter>,
85 route: DatastoreRoute,
86 context: Option<StorageContext>,
87}
88
89impl Connection {
90 pub fn open(route: DatastoreRoute) -> Result<Self, DactylError> {
91 Self::open_with_options_and_context(route, OpenOptions::default(), None)
92 }
93
94 pub fn open_with_options(
95 route: DatastoreRoute,
96 options: OpenOptions,
97 ) -> Result<Self, DactylError> {
98 Self::open_with_options_and_context(route, options, None)
99 }
100
101 pub fn open_with_context(
106 route: DatastoreRoute,
107 context: Option<StorageContext>,
108 ) -> Result<Self, DactylError> {
109 Self::open_with_options_and_context(route, OpenOptions::default(), context)
110 }
111
112 pub fn open_with_options_and_context(
113 route: DatastoreRoute,
114 options: OpenOptions,
115 context: Option<StorageContext>,
116 ) -> Result<Self, DactylError> {
117 if let Some(context) = &context {
118 context.validate()?;
119 }
120 let adapter = build_adapter(&route, options, context.clone())?;
121 Ok(Self {
122 adapter,
123 route,
124 context,
125 })
126 }
127
128 pub fn from_env() -> Result<Self, DactylError> {
129 Self::open(DatastoreRoute::from_env()?)
130 }
131
132 pub fn datastore(&self) -> Datastore {
133 self.route.datastore
134 }
135
136 pub fn route(&self) -> &DatastoreRoute {
137 &self.route
138 }
139
140 pub fn context(&self) -> Option<&StorageContext> {
141 self.context.as_ref()
142 }
143
144 pub fn read(&self, sql: &str, params: &[Parameter]) -> Result<Rows, DactylError> {
146 self.adapter.read(sql, params)
147 }
148
149 pub fn write_result(
151 &self,
152 sql: &str,
153 params: &[Parameter],
154 ) -> Result<WriteResult, DactylError> {
155 self.adapter.write(sql, params)
156 }
157
158 pub fn write(&self, sql: &str, params: &[Parameter]) -> Result<u64, DactylError> {
160 Ok(self.write_result(sql, params)?.affected_rows)
161 }
162
163 pub fn atomic(&self, operations: &[Operation]) -> Result<AtomicResult, DactylError> {
164 self.adapter.atomic(operations)
165 }
166
167 pub fn access_mode(&self) -> AccessMode {
168 self.adapter.access_mode()
169 }
170}
171
172pub type Driver = Connection;
174
175pub fn read(sql: &str, params: &[Parameter]) -> Result<Rows, DactylError> {
176 Connection::from_env()?.read(sql, params)
177}
178
179pub fn read_with_context(
180 context: Option<StorageContext>,
181 sql: &str,
182 params: &[Parameter],
183) -> Result<Rows, DactylError> {
184 Connection::open_with_context(DatastoreRoute::from_env()?, context)?.read(sql, params)
185}
186
187pub fn write(sql: &str, params: &[Parameter]) -> Result<u64, DactylError> {
188 Connection::from_env()?.write(sql, params)
189}
190
191pub fn write_with_context(
192 context: Option<StorageContext>,
193 sql: &str,
194 params: &[Parameter],
195) -> Result<u64, DactylError> {
196 Connection::open_with_context(DatastoreRoute::from_env()?, context)?.write(sql, params)
197}
198
199#[deprecated(note = "use dactyl_db::read")]
200pub fn query(sql: &str, params: &[Parameter]) -> Result<Rows, DactylError> {
201 read(sql, params)
202}
203
204#[deprecated(note = "use dactyl_db::write")]
205pub fn execute(sql: &str, params: &[Parameter]) -> Result<u64, DactylError> {
206 write(sql, params)
207}
208
209fn build_adapter(
210 route: &DatastoreRoute,
211 _options: OpenOptions,
212 _context: Option<StorageContext>,
213) -> Result<Box<dyn Adapter>, DactylError> {
214 match route.datastore {
215 Datastore::Sqlite => {
216 #[cfg(feature = "sqlite")]
217 {
218 Ok(Box::new(
219 crate::adapter::sqlite::SqliteAdapter::open_with_options(
220 &route.route,
221 _options,
222 )?,
223 ))
224 }
225 #[cfg(not(feature = "sqlite"))]
226 {
227 Err(DactylError::Config(
228 "sqlite support is disabled; enable the `sqlite` feature".into(),
229 ))
230 }
231 }
232 Datastore::Neon => {
233 #[cfg(feature = "neon")]
234 {
235 Ok(Box::new(
236 crate::adapter::neon::NeonAdapter::new_with_options(
237 &route.route,
238 route.token.clone(),
239 _options,
240 _context,
241 ),
242 ))
243 }
244 #[cfg(not(feature = "neon"))]
245 {
246 Err(DactylError::Config(
247 "neon support is disabled; enable the `neon` feature".into(),
248 ))
249 }
250 }
251 }
252}