1mod adapter;
8mod contract;
9mod rows;
10
11pub mod error;
12
13pub use crate::contract::{
14 AccessMode, AtomicResult, GeneratedKey, OpenOptions, Operation, OperationKind, OperationResult,
15 WriteResult,
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}
87
88impl Connection {
89 pub fn open(route: DatastoreRoute) -> Result<Self, DactylError> {
90 Self::open_with_options(route, OpenOptions::default())
91 }
92
93 pub fn open_with_options(
94 route: DatastoreRoute,
95 options: OpenOptions,
96 ) -> Result<Self, DactylError> {
97 let adapter = build_adapter(&route, options)?;
98 Ok(Self { adapter, route })
99 }
100
101 pub fn from_env() -> Result<Self, DactylError> {
102 Self::open(DatastoreRoute::from_env()?)
103 }
104
105 pub fn datastore(&self) -> Datastore {
106 self.route.datastore
107 }
108
109 pub fn route(&self) -> &DatastoreRoute {
110 &self.route
111 }
112
113 pub fn read(&self, sql: &str, params: &[Parameter]) -> Result<Rows, DactylError> {
115 self.adapter.read(sql, params)
116 }
117
118 pub fn write_result(
120 &self,
121 sql: &str,
122 params: &[Parameter],
123 ) -> Result<WriteResult, DactylError> {
124 self.adapter.write(sql, params)
125 }
126
127 pub fn write(&self, sql: &str, params: &[Parameter]) -> Result<u64, DactylError> {
129 Ok(self.write_result(sql, params)?.affected_rows)
130 }
131
132 pub fn atomic(&self, operations: &[Operation]) -> Result<AtomicResult, DactylError> {
133 self.adapter.atomic(operations)
134 }
135
136 pub fn access_mode(&self) -> AccessMode {
137 self.adapter.access_mode()
138 }
139}
140
141pub type Driver = Connection;
143
144pub fn read(sql: &str, params: &[Parameter]) -> Result<Rows, DactylError> {
145 Connection::from_env()?.read(sql, params)
146}
147
148pub fn write(sql: &str, params: &[Parameter]) -> Result<u64, DactylError> {
149 Connection::from_env()?.write(sql, params)
150}
151
152#[deprecated(note = "use dactyl_db::read")]
153pub fn query(sql: &str, params: &[Parameter]) -> Result<Rows, DactylError> {
154 read(sql, params)
155}
156
157#[deprecated(note = "use dactyl_db::write")]
158pub fn execute(sql: &str, params: &[Parameter]) -> Result<u64, DactylError> {
159 write(sql, params)
160}
161
162fn build_adapter(
163 route: &DatastoreRoute,
164 _options: OpenOptions,
165) -> Result<Box<dyn Adapter>, DactylError> {
166 match route.datastore {
167 Datastore::Sqlite => {
168 #[cfg(feature = "sqlite")]
169 {
170 Ok(Box::new(
171 crate::adapter::sqlite::SqliteAdapter::open_with_options(
172 &route.route,
173 _options,
174 )?,
175 ))
176 }
177 #[cfg(not(feature = "sqlite"))]
178 {
179 Err(DactylError::Config(
180 "sqlite support is disabled; enable the `sqlite` feature".into(),
181 ))
182 }
183 }
184 Datastore::Neon => {
185 #[cfg(feature = "neon")]
186 {
187 Ok(Box::new(
188 crate::adapter::neon::NeonAdapter::new_with_options(
189 &route.route,
190 route.token.clone(),
191 _options,
192 ),
193 ))
194 }
195 #[cfg(not(feature = "neon"))]
196 {
197 Err(DactylError::Config(
198 "neon support is disabled; enable the `neon` feature".into(),
199 ))
200 }
201 }
202 }
203}