lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

utils.rs (4872B)


      1 use chrono::{SecondsFormat, Utc};
      2 use serde::{Deserialize, Serialize};
      3 use serde_json::{Map, Value};
      4 use uuid::Uuid;
      5 
      6 use crate::error::SqlError;
      7 
      8 pub fn parse_json<T: for<'de> Deserialize<'de>>(s: &str) -> Result<T, SqlError> {
      9     serde_json::from_str::<T>(s).map_err(SqlError::from)
     10 }
     11 
     12 pub fn uuidv4() -> String {
     13     Uuid::new_v4().to_string()
     14 }
     15 
     16 pub fn time_created_on() -> String {
     17     Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true)
     18 }
     19 
     20 pub fn to_object_map<T: Serialize>(opts: T) -> Result<Map<String, Value>, SqlError> {
     21     let v = serde_json::to_value(opts).map_err(SqlError::from)?;
     22     let obj = v
     23         .as_object()
     24         .ok_or_else(|| SqlError::SerializationError(String::from("Expected an object")))?;
     25     Ok(obj.clone())
     26 }
     27 
     28 pub fn to_partial_object_map<T: Serialize>(opts: T) -> Result<Map<String, Value>, SqlError> {
     29     let v = serde_json::to_value(opts).map_err(SqlError::from)?;
     30     let obj = v
     31         .as_object()
     32         .ok_or_else(|| SqlError::SerializationError(String::from("Expected an object")))?;
     33     let mut filtered = Map::new();
     34     for (k, v) in obj.iter() {
     35         if !v.is_null() {
     36             filtered.insert(k.clone(), v.clone());
     37         }
     38     }
     39     Ok(filtered)
     40 }
     41 
     42 pub fn to_db_bind_value(value: &Value) -> Value {
     43     match value {
     44         Value::Bool(b) => Value::from(i64::from(*b)),
     45         Value::Number(n) => {
     46             if let Some(u) = n.as_u64() {
     47                 if u <= u32::MAX as u64 {
     48                     Value::from(u as u32)
     49                 } else {
     50                     Value::from(u)
     51                 }
     52             } else if let Some(i) = n.as_i64() {
     53                 Value::from(i)
     54             } else {
     55                 Value::from(
     56                     n.as_f64()
     57                         .expect("json number should map to u64, i64, or f64"),
     58                 )
     59             }
     60         }
     61         Value::String(s) => Value::from(s.clone()),
     62         _ => Value::Null,
     63     }
     64 }
     65 
     66 pub fn build_where_clause_eq<T: Serialize>(filter: &T) -> Result<(String, Vec<Value>), SqlError> {
     67     let obj = to_partial_object_map(filter)?;
     68     if obj.is_empty() {
     69         return Ok((String::new(), Vec::new()));
     70     }
     71     let mut clauses = Vec::with_capacity(obj.len());
     72     let mut binds = Vec::with_capacity(obj.len());
     73     for (k, v) in obj {
     74         clauses.push(format!("{k} = ?"));
     75         binds.push(to_db_bind_value(&v));
     76     }
     77     Ok((format!(" WHERE {}", clauses.join(" AND ")), binds))
     78 }
     79 
     80 pub fn build_insert_query_with_meta(
     81     table: &str,
     82     meta: &[(&str, Value)],
     83     fields: &Map<String, Value>,
     84 ) -> (String, Vec<Value>) {
     85     let mut cols: Vec<String> = meta.iter().map(|(k, _)| k.to_string()).collect();
     86     cols.extend(fields.keys().cloned());
     87     let meta_binds: Vec<Value> = meta.iter().map(|(_, v)| to_db_bind_value(v)).collect();
     88     let field_binds: Vec<Value> = fields.values().map(to_db_bind_value).collect();
     89     let placeholders = (0..cols.len())
     90         .map(|_| "?")
     91         .collect::<Vec<&str>>()
     92         .join(",");
     93     let sql = format!(
     94         "INSERT INTO {table} ({}) VALUES ({});",
     95         cols.join(","),
     96         placeholders
     97     );
     98     let mut binds = Vec::with_capacity(cols.len());
     99     binds.extend(meta_binds);
    100     binds.extend(field_binds);
    101     (sql, binds)
    102 }
    103 
    104 pub fn build_select_query_with_meta<T: Serialize>(
    105     table: &str,
    106     filter: Option<&T>,
    107 ) -> (String, Vec<Value>) {
    108     let (where_clause, binds) = match filter {
    109         Some(f) => build_where_clause_eq(f).unwrap_or_default(),
    110         None => (String::new(), Vec::new()),
    111     };
    112     let sql = format!("SELECT * FROM {table}{where_clause};");
    113     (sql, binds)
    114 }
    115 
    116 pub fn parse_query_value(v: &Value) -> Result<Value, SqlError> {
    117     Ok(match v {
    118         Value::Bool(b) => {
    119             if *b {
    120                 serde_json::json!(1)
    121             } else {
    122                 serde_json::json!(0)
    123             }
    124         }
    125         Value::Null => Value::Null,
    126         Value::Number(_) | Value::String(_) => v.clone(),
    127         other => {
    128             return Err(SqlError::InvalidArgument(other.to_string()));
    129         }
    130     })
    131 }
    132 
    133 pub fn to_params_json<T: Serialize>(v: T) -> Result<String, SqlError> {
    134     serde_json::to_string(&v).map_err(SqlError::from)
    135 }
    136 
    137 fn with_transaction_inner<E, T>(
    138     exec: &E,
    139     f: &mut dyn FnMut() -> Result<T, SqlError>,
    140 ) -> Result<T, SqlError>
    141 where
    142     E: crate::SqlExecutor,
    143 {
    144     exec.begin()?;
    145     match f() {
    146         Ok(v) => {
    147             exec.commit()?;
    148             Ok(v)
    149         }
    150         Err(e) => {
    151             let _ = exec.rollback();
    152             Err(e)
    153         }
    154     }
    155 }
    156 
    157 pub fn with_transaction<E, F, T>(exec: &E, f: F) -> Result<T, SqlError>
    158 where
    159     E: crate::SqlExecutor,
    160     F: FnOnce() -> Result<T, SqlError>,
    161 {
    162     let mut f = Some(f);
    163     with_transaction_inner(exec, &mut || {
    164         let f = f.take().expect("transaction closure already used");
    165         f()
    166     })
    167 }