vibe_ready/store/db/tables/
key_val.rs1use serde::{Deserialize, Serialize};
2
3#[cfg(feature = "store-diesel-sqlite")]
4pub static TABLE_NAME_KEY_VAL: &str = "vibe_ready_key_val";
5#[cfg(feature = "store-diesel-sqlite")]
6pub static TABLE_NAME_KV_META: &str = "vibe_ready_kv_meta";
7
8pub const DEFAULT_BUCKET: &str = "default";
9
10pub(crate) const VALUE_TYPE_STR: i16 = 1;
11pub(crate) const VALUE_TYPE_BOOL: i16 = 2;
12pub(crate) const VALUE_TYPE_I32: i16 = 3;
13pub(crate) const VALUE_TYPE_I64: i16 = 4;
14pub(crate) const VALUE_TYPE_F64: i16 = 5;
15pub(crate) const VALUE_TYPE_BYTES: i16 = 6;
16pub(crate) const VALUE_TYPE_JSON: i16 = 7;
17
18pub(crate) const EXPIRES_AT_NEVER: i64 = 0;
20
21#[cfg(feature = "store-diesel-sqlite")]
22diesel::table! {
23 vibe_ready_key_val (user_id, bucket, key) {
24 user_id -> Text,
25 bucket -> Text,
26 key -> Text,
27 value_type -> SmallInt,
28 value_str -> Text,
29 value_bool -> Bool,
30 value_i32 -> Integer,
31 value_i64 -> BigInt,
32 value_f64 -> Double,
33 value_bytes -> Binary,
34 value_json -> Text,
35 expires_at_ms -> BigInt,
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41pub enum VibeKvValue {
42 String(String),
44 Bool(bool),
46 I32(i32),
48 I64(i64),
50 F64(f64),
52 Bytes(Vec<u8>),
54 Json(serde_json::Value),
56}
57
58impl VibeKvValue {
59 pub fn as_str(&self) -> Option<&str> {
73 match self {
74 Self::String(value) => Some(value.as_str()),
75 _ => None,
76 }
77 }
78
79 pub fn as_bool(&self) -> Option<bool> {
85 match self {
86 Self::Bool(value) => Some(*value),
87 _ => None,
88 }
89 }
90
91 pub fn as_i32(&self) -> Option<i32> {
97 match self {
98 Self::I32(value) => Some(*value),
99 _ => None,
100 }
101 }
102
103 pub fn as_i64(&self) -> Option<i64> {
109 match self {
110 Self::I64(value) => Some(*value),
111 _ => None,
112 }
113 }
114
115 pub fn as_f64(&self) -> Option<f64> {
121 match self {
122 Self::F64(value) => Some(*value),
123 _ => None,
124 }
125 }
126
127 pub fn as_bytes(&self) -> Option<&[u8]> {
133 match self {
134 Self::Bytes(value) => Some(value.as_slice()),
135 _ => None,
136 }
137 }
138
139 pub fn as_json(&self) -> Option<&serde_json::Value> {
145 match self {
146 Self::Json(value) => Some(value),
147 _ => None,
148 }
149 }
150}
151
152impl From<String> for VibeKvValue {
153 fn from(value: String) -> Self {
154 Self::String(value)
155 }
156}
157
158impl From<&str> for VibeKvValue {
159 fn from(value: &str) -> Self {
160 Self::String(value.to_string())
161 }
162}
163
164impl From<bool> for VibeKvValue {
165 fn from(value: bool) -> Self {
166 Self::Bool(value)
167 }
168}
169
170impl From<i32> for VibeKvValue {
171 fn from(value: i32) -> Self {
172 Self::I32(value)
173 }
174}
175
176impl From<i64> for VibeKvValue {
177 fn from(value: i64) -> Self {
178 Self::I64(value)
179 }
180}
181
182impl From<f64> for VibeKvValue {
183 fn from(value: f64) -> Self {
184 Self::F64(value)
185 }
186}
187
188impl From<Vec<u8>> for VibeKvValue {
189 fn from(value: Vec<u8>) -> Self {
190 Self::Bytes(value)
191 }
192}
193
194impl From<&[u8]> for VibeKvValue {
195 fn from(value: &[u8]) -> Self {
196 Self::Bytes(value.to_vec())
197 }
198}
199
200impl From<serde_json::Value> for VibeKvValue {
201 fn from(value: serde_json::Value) -> Self {
202 Self::Json(value)
203 }
204}
205
206#[cfg_attr(
207 feature = "store-diesel-sqlite",
208 derive(diesel::Queryable, diesel::Selectable, diesel::Insertable)
209)]
210#[cfg_attr(feature = "store-diesel-sqlite", diesel(table_name = vibe_ready_key_val))]
211#[cfg_attr(
212 feature = "store-diesel-sqlite",
213 diesel(primary_key(user_id, bucket, key))
214)]
215#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
216pub struct VibeTableKeyVal {
221 pub(crate) user_id: String,
222 pub(crate) bucket: String,
223 pub(crate) key: String,
224 pub(crate) value_type: i16,
225 pub(crate) value_str: String,
226 pub(crate) value_bool: bool,
227 pub(crate) value_i32: i32,
228 pub(crate) value_i64: i64,
229 pub(crate) value_f64: f64,
230 pub(crate) value_bytes: Vec<u8>,
231 pub(crate) value_json: String,
232 pub(crate) expires_at_ms: i64,
233}
234
235impl VibeTableKeyVal {
236 pub fn new(user_id: &str, key: &str, value: VibeKvValue) -> Self {
242 Self::new_in_bucket(user_id, DEFAULT_BUCKET, key, value, EXPIRES_AT_NEVER)
243 }
244
245 pub fn new_in_bucket(
252 user_id: &str,
253 bucket: &str,
254 key: &str,
255 value: VibeKvValue,
256 expires_at_ms: i64,
257 ) -> Self {
258 let mut row = Self::empty(user_id, bucket, key);
259 row.expires_at_ms = expires_at_ms;
260 match value {
261 VibeKvValue::String(v) => {
262 row.value_type = VALUE_TYPE_STR;
263 row.value_str = v;
264 }
265 VibeKvValue::Bool(v) => {
266 row.value_type = VALUE_TYPE_BOOL;
267 row.value_bool = v;
268 }
269 VibeKvValue::I32(v) => {
270 row.value_type = VALUE_TYPE_I32;
271 row.value_i32 = v;
272 }
273 VibeKvValue::I64(v) => {
274 row.value_type = VALUE_TYPE_I64;
275 row.value_i64 = v;
276 }
277 VibeKvValue::F64(v) => {
278 row.value_type = VALUE_TYPE_F64;
279 row.value_f64 = v;
280 }
281 VibeKvValue::Bytes(v) => {
282 row.value_type = VALUE_TYPE_BYTES;
283 row.value_bytes = v;
284 }
285 VibeKvValue::Json(v) => {
286 row.value_type = VALUE_TYPE_JSON;
287 row.value_json = v.to_string();
288 }
289 }
290 row
291 }
292
293 fn empty(user_id: &str, bucket: &str, key: &str) -> Self {
294 Self {
295 user_id: user_id.to_string(),
296 bucket: bucket.to_string(),
297 key: key.to_string(),
298 value_type: 0,
299 value_str: String::new(),
300 value_bool: false,
301 value_i32: 0,
302 value_i64: 0,
303 value_f64: 0.0,
304 value_bytes: Vec::new(),
305 value_json: String::new(),
306 expires_at_ms: EXPIRES_AT_NEVER,
307 }
308 }
309
310 pub fn new_with_str(user_id: &str, key: &str, val: &str) -> Self {
316 Self::new(user_id, key, VibeKvValue::String(val.to_string()))
317 }
318
319 pub fn new_with_bool(user_id: &str, key: &str, val: bool) -> Self {
325 Self::new(user_id, key, VibeKvValue::Bool(val))
326 }
327
328 pub fn new_with_i32(user_id: &str, key: &str, val: i32) -> Self {
334 Self::new(user_id, key, VibeKvValue::I32(val))
335 }
336
337 pub fn key(&self) -> &str {
343 self.key.as_str()
344 }
345
346 pub fn user_id(&self) -> &str {
352 self.user_id.as_str()
353 }
354
355 pub fn bucket(&self) -> &str {
361 self.bucket.as_str()
362 }
363
364 pub fn expires_at_ms(&self) -> i64 {
370 self.expires_at_ms
371 }
372
373 pub fn is_expired(&self, now_ms: i64) -> bool {
379 self.expires_at_ms != EXPIRES_AT_NEVER && now_ms >= self.expires_at_ms
380 }
381
382 pub fn value(&self) -> Option<VibeKvValue> {
388 match self.value_type {
389 VALUE_TYPE_STR => Some(VibeKvValue::String(self.value_str.clone())),
390 VALUE_TYPE_BOOL => Some(VibeKvValue::Bool(self.value_bool)),
391 VALUE_TYPE_I32 => Some(VibeKvValue::I32(self.value_i32)),
392 VALUE_TYPE_I64 => Some(VibeKvValue::I64(self.value_i64)),
393 VALUE_TYPE_F64 => Some(VibeKvValue::F64(self.value_f64)),
394 VALUE_TYPE_BYTES => Some(VibeKvValue::Bytes(self.value_bytes.clone())),
395 VALUE_TYPE_JSON => serde_json::from_str(&self.value_json)
396 .ok()
397 .map(VibeKvValue::Json),
398 _ => None,
399 }
400 }
401
402 pub fn get_value_str(&self) -> Option<&str> {
408 if self.value_type == VALUE_TYPE_STR {
409 Some(self.value_str.as_str())
410 } else {
411 None
412 }
413 }
414
415 pub fn get_value_bool(&self) -> Option<bool> {
421 if self.value_type == VALUE_TYPE_BOOL {
422 Some(self.value_bool)
423 } else {
424 None
425 }
426 }
427
428 pub fn get_value_i32(&self) -> Option<i32> {
434 if self.value_type == VALUE_TYPE_I32 {
435 Some(self.value_i32)
436 } else {
437 None
438 }
439 }
440}
441
442#[cfg(test)]
443mod strict_tests {
444 use super::*;
445 include!(concat!(
446 env!("CARGO_MANIFEST_DIR"),
447 "/test/unit/store/key_val_tests.rs"
448 ));
449}