1mod adapter;
9mod rows;
10
11pub mod error;
12
13pub use crate::error::{AdapterErrorKind, DactylError};
14pub use crate::rows::{Parameter, Row, Rows};
15
16use crate::adapter::Adapter;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum Datastore {
21 Sqlite,
22 Neon,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct DatastoreRoute {
28 datastore: Datastore,
29 route: String,
30 token: Option<String>,
31}
32
33impl DatastoreRoute {
34 pub fn sqlite(path: impl Into<String>) -> Self {
35 Self {
36 datastore: Datastore::Sqlite,
37 route: path.into(),
38 token: None,
39 }
40 }
41
42 pub fn neon(endpoint: impl Into<String>, token: Option<String>) -> Self {
43 Self {
44 datastore: Datastore::Neon,
45 route: endpoint.into(),
46 token,
47 }
48 }
49
50 pub fn datastore(&self) -> Datastore {
51 self.datastore
52 }
53
54 pub fn route(&self) -> &str {
55 &self.route
56 }
57
58 pub fn token(&self) -> Option<&str> {
59 self.token.as_deref()
60 }
61
62 pub fn from_env() -> Result<Self, DactylError> {
64 let datastore = std::env::var("DATASTORE")
65 .map_err(|_| DactylError::Config("DATASTORE is not set: use sqlite or neon".into()))?;
66 let route = std::env::var("DATASTORE_ROUTE")
67 .map_err(|_| DactylError::Config("DATASTORE_ROUTE is not set".into()))?;
68 match datastore.as_str() {
69 "sqlite" => Ok(Self::sqlite(route)),
70 "neon" => Ok(Self::neon(route, std::env::var("DATASTORE_TOKEN").ok())),
71 other => Err(DactylError::Config(format!(
72 "invalid DATASTORE value {other:?}: use sqlite or neon"
73 ))),
74 }
75 }
76}
77
78pub struct Connection {
80 adapter: Box<dyn Adapter>,
81 route: DatastoreRoute,
82}
83
84impl Connection {
85 pub fn open(route: DatastoreRoute) -> Result<Self, DactylError> {
86 let adapter = build_adapter(&route)?;
87 Ok(Self { adapter, route })
88 }
89
90 pub fn from_env() -> Result<Self, DactylError> {
91 Self::open(DatastoreRoute::from_env()?)
92 }
93
94 pub fn datastore(&self) -> Datastore {
95 self.route.datastore
96 }
97
98 pub fn route(&self) -> &DatastoreRoute {
99 &self.route
100 }
101
102 pub fn read(&self, sql: &str, params: &[Parameter]) -> Result<Rows, DactylError> {
104 self.adapter.read(sql, params)
105 }
106
107 pub fn write(&self, sql: &str, params: &[Parameter]) -> Result<u64, DactylError> {
109 self.adapter.write(sql, params)
110 }
111}
112
113pub type Driver = Connection;
115
116pub fn read(sql: &str, params: &[Parameter]) -> Result<Rows, DactylError> {
117 Connection::from_env()?.read(sql, params)
118}
119
120pub fn write(sql: &str, params: &[Parameter]) -> Result<u64, DactylError> {
121 Connection::from_env()?.write(sql, params)
122}
123
124#[deprecated(note = "use dactyl_db::read")]
125pub fn query(sql: &str, params: &[Parameter]) -> Result<Rows, DactylError> {
126 read(sql, params)
127}
128
129#[deprecated(note = "use dactyl_db::write")]
130pub fn execute(sql: &str, params: &[Parameter]) -> Result<u64, DactylError> {
131 write(sql, params)
132}
133
134fn build_adapter(route: &DatastoreRoute) -> Result<Box<dyn Adapter>, DactylError> {
135 match route.datastore {
136 Datastore::Sqlite => {
137 #[cfg(feature = "sqlite")]
138 {
139 Ok(Box::new(crate::adapter::sqlite::SqliteAdapter::open(
140 &route.route,
141 )?))
142 }
143 #[cfg(not(feature = "sqlite"))]
144 {
145 Err(DactylError::Config(
146 "sqlite support is disabled; enable the `sqlite` feature".into(),
147 ))
148 }
149 }
150 Datastore::Neon => {
151 #[cfg(feature = "neon")]
152 {
153 Ok(Box::new(crate::adapter::neon::NeonAdapter::new(
154 &route.route,
155 route.token.clone(),
156 )))
157 }
158 #[cfg(not(feature = "neon"))]
159 {
160 Err(DactylError::Config(
161 "neon support is disabled; enable the `neon` feature".into(),
162 ))
163 }
164 }
165 }
166}