serde_sql/
lib.rs

1//! Stream `serde` structs into SQLite statements and infer table DDL.
2
3pub mod insert;
4pub mod ddl;
5
6pub use ddl::infer_table_ddl;
7pub use insert::serialize_insert;
8
9use std::fmt::{self, Display};
10
11use serde_json::Value as JsonValue;
12
13#[derive(Debug)]
14pub enum SqlSerializeError {
15    InvalidRoot,
16    Message(String),
17}
18
19impl Display for SqlSerializeError {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            SqlSerializeError::InvalidRoot => {
23                write!(f, "expected struct root for SQL serialization")
24            }
25            SqlSerializeError::Message(msg) => f.write_str(msg),
26        }
27    }
28}
29
30impl std::error::Error for SqlSerializeError {}
31
32impl serde::ser::Error for SqlSerializeError {
33    fn custom<T: Display>(msg: T) -> Self {
34        SqlSerializeError::Message(msg.to_string())
35    }
36}
37
38#[derive(Debug, Clone)]
39pub(crate) struct ColumnDefinition {
40    pub name: String,
41    pub column_type: ColumnType,
42    pub nullable: bool,
43}
44
45#[derive(Debug, Clone, Copy)]
46pub(crate) enum ColumnType {
47    Integer,
48    Real,
49    Text,
50    Json,
51}
52
53impl ColumnType {
54    pub fn sql_type(self) -> &'static str {
55        match self {
56            ColumnType::Integer => "INTEGER",
57            ColumnType::Real => "REAL",
58            ColumnType::Text => "TEXT",
59            ColumnType::Json => "TEXT",
60        }
61    }
62}
63
64pub(crate) fn quote_identifier(value: &str) -> String {
65    let mut quoted = String::with_capacity(value.len() + 2);
66    quoted.push('"');
67    for ch in value.chars() {
68        if ch == '"' {
69            quoted.push('"');
70        }
71        quoted.push(ch);
72    }
73    quoted.push('"');
74    quoted
75}
76
77pub(crate) fn infer_column_type(value: &JsonValue) -> (ColumnType, bool) {
78    match value {
79        JsonValue::Null => (ColumnType::Text, true),
80        JsonValue::Bool(_) => (ColumnType::Integer, false),
81        JsonValue::Number(number) => {
82            if number.is_i64() || number.is_u64() {
83                (ColumnType::Integer, false)
84            } else {
85                (ColumnType::Real, false)
86            }
87        }
88        JsonValue::String(_) => (ColumnType::Text, false),
89        JsonValue::Array(_) | JsonValue::Object(_) => (ColumnType::Json, false),
90    }
91}