1use crate::alloc_prelude::*;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7pub enum Casing {
8 #[default]
10 #[cfg_attr(feature = "serde", serde(rename = "camelCase"))]
11 CamelCase,
12 #[cfg_attr(feature = "serde", serde(rename = "snake_case"))]
14 SnakeCase,
15}
16
17impl Casing {
18 #[must_use]
20 pub const fn as_str(self) -> &'static str {
21 match self {
22 Self::CamelCase => "camelCase",
23 Self::SnakeCase => "snake_case",
24 }
25 }
26}
27
28impl core::fmt::Display for Casing {
29 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
30 f.write_str(self.as_str())
31 }
32}
33
34#[cfg(any(feature = "std", feature = "alloc"))]
35impl core::str::FromStr for Casing {
36 type Err = crate::alloc_prelude::String;
37
38 fn from_str(s: &str) -> Result<Self, Self::Err> {
39 match s {
40 "camelCase" | "camel" => Ok(Self::CamelCase),
41 "snake_case" | "snake" => Ok(Self::SnakeCase),
42 _ => Err(format!(
43 "invalid casing '{s}', expected 'camelCase' or 'snake_case'"
44 )),
45 }
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Hash)]
64pub struct MigrationTracking {
65 pub table: Cow<'static, str>,
67 pub schema: Option<Cow<'static, str>>,
69}
70
71impl MigrationTracking {
72 pub const SQLITE: Self = Self {
74 table: Cow::Borrowed("__drizzle_migrations"),
75 schema: None,
76 };
77
78 pub const POSTGRES: Self = Self {
80 table: Cow::Borrowed("__drizzle_migrations"),
81 schema: Some(Cow::Borrowed("drizzle")),
82 };
83
84 pub const MYSQL: Self = Self {
86 table: Cow::Borrowed("__drizzle_migrations"),
87 schema: None,
88 };
89
90 pub fn new(
92 table: impl Into<Cow<'static, str>>,
93 schema: Option<impl Into<Cow<'static, str>>>,
94 ) -> Self {
95 Self {
96 table: table.into(),
97 schema: schema.map(Into::into),
98 }
99 }
100
101 #[must_use]
103 pub fn table(mut self, table: impl Into<Cow<'static, str>>) -> Self {
104 self.table = table.into();
105 self
106 }
107
108 #[must_use]
110 pub fn schema(mut self, schema: impl Into<Cow<'static, str>>) -> Self {
111 self.schema = Some(schema.into());
112 self
113 }
114
115 #[must_use]
117 pub fn without_schema(mut self) -> Self {
118 self.schema = None;
119 self
120 }
121}
122impl Default for MigrationTracking {
123 fn default() -> Self {
124 Self::SQLITE
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
143pub enum ConfigValue {
144 Inline(String),
146 Env(String),
148}
149
150#[cfg(feature = "std")]
151impl ConfigValue {
152 pub fn resolve(&self) -> Result<String, ConfigValueError> {
160 match self {
161 Self::Inline(v) => Ok(v.clone()),
162 Self::Env(var) => match std::env::var(var) {
163 Ok(v) => Ok(v),
164 Err(std::env::VarError::NotPresent) => {
165 Err(ConfigValueError::NotPresent(var.clone()))
166 }
167 Err(std::env::VarError::NotUnicode(_)) => {
168 Err(ConfigValueError::NotUnicode(var.clone()))
169 }
170 },
171 }
172 }
173
174 pub fn resolve_optional(&self) -> Result<Option<String>, ConfigValueError> {
181 match self {
182 Self::Inline(v) => Ok(Some(v.clone())),
183 Self::Env(var) => match std::env::var(var) {
184 Ok(v) => Ok(Some(v)),
185 Err(std::env::VarError::NotPresent) => Ok(None),
186 Err(std::env::VarError::NotUnicode(_)) => {
187 Err(ConfigValueError::NotUnicode(var.clone()))
188 }
189 },
190 }
191 }
192}
193
194#[cfg(feature = "std")]
196#[derive(Debug, Clone, PartialEq, Eq)]
197pub enum ConfigValueError {
198 NotPresent(String),
200 NotUnicode(String),
202}
203
204#[cfg(feature = "std")]
205impl core::fmt::Display for ConfigValueError {
206 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
207 match self {
208 Self::NotPresent(var) => write!(f, "env var `{var}` not set"),
209 Self::NotUnicode(var) => write!(f, "env var `{var}` contains invalid unicode"),
210 }
211 }
212}
213
214#[cfg(feature = "std")]
215impl std::error::Error for ConfigValueError {}
216
217#[cfg(feature = "serde")]
218impl<'de> serde::Deserialize<'de> for ConfigValue {
219 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
220 where
221 D: serde::Deserializer<'de>,
222 {
223 use serde::de::{self, MapAccess, Visitor};
224
225 struct ConfigValueVisitor;
226
227 impl<'de> Visitor<'de> for ConfigValueVisitor {
228 type Value = ConfigValue;
229
230 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
231 formatter.write_str("a string or { env = \"VAR_NAME\" }")
232 }
233
234 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
235 where
236 E: de::Error,
237 {
238 Ok(ConfigValue::Inline(value.to_string()))
239 }
240
241 fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
242 where
243 M: MapAccess<'de>,
244 {
245 let mut env_var: Option<String> = None;
246
247 while let Some(key) = map.next_key::<String>()? {
248 if key == "env" {
249 env_var = Some(map.next_value()?);
250 } else {
251 return Err(de::Error::unknown_field(&key, &["env"]));
252 }
253 }
254
255 env_var
256 .map(ConfigValue::Env)
257 .ok_or_else(|| de::Error::missing_field("env"))
258 }
259 }
260
261 deserializer.deserialize_any(ConfigValueVisitor)
262 }
263}
264
265#[cfg(feature = "schemars")]
266impl schemars::JsonSchema for ConfigValue {
267 fn schema_name() -> Cow<'static, str> {
268 "ConfigValue".into()
269 }
270
271 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
272 use schemars::json_schema;
273
274 json_schema!({
276 "oneOf": [
277 generator.subschema_for::<String>(),
278 {
279 "type": "object",
280 "properties": {
281 "env": { "type": "string" }
282 },
283 "required": ["env"],
284 "additionalProperties": false
285 }
286 ]
287 })
288 }
289}