db_dsl 0.3.3

A simple DSL for creating database objects.
Documentation
use crate::{DataType, SqlUp};
use log::warn;
use serde_value::Value;

#[derive(Clone, Debug, Default)]
pub struct Column {
    pub name: String,
    pub data_type: Option<DataType>,
    pub options: Option<ColumnOptions>,
}

#[derive(Clone, Debug, Default)]
pub enum ColumnDefault {
    #[default]
    Value,
    Raw,
}

impl Column {
    pub fn new(name: &str) -> Self {
        Column {
            name: name.to_string(),
            data_type: Some(DataType::default()),
            options: None,
        }
    }

    pub fn name(&mut self, name: &str) -> &mut Self {
        self.name = name.to_string();
        self
    }

    pub fn default(&mut self, default: Value) -> &mut Self {
        self.options = Some(ColumnOptions {
            default: Some(default),
            default_format: Some(ColumnDefault::Value),
            ..self.options.clone().unwrap_or_default()
        });
        self
    }

    pub fn default_raw(&mut self, default: String) -> &mut Self {
        self.options(ColumnOptions {
            default: Some(Value::String(default)),
            default_format: Some(ColumnDefault::Raw),
            ..self.options.clone().unwrap_or_default()
        })
    }

    pub fn not_null(&mut self) -> &mut Self {
        let options = self.options.clone().unwrap_or_default();
        self.options = Some(ColumnOptions {
            not_null: true,
            ..options
        });
        self
    }

    pub fn options(&mut self, options: ColumnOptions) -> &mut Self {
        self.options = Some(options);
        self
    }
}

impl SqlUp for Column {
    fn sqlite_up(&self) -> Option<String> {
        let data_type = if self.data_type.is_none() {
            DataType::default()
        } else {
            self.data_type.clone().unwrap()
        };
        let datatype_sql = data_type.clone().sqlite_up();
        datatype_sql.as_ref()?;
        let mut sql = format!("{} {}", self.name, data_type.sqlite_up().unwrap());
        if let Some(options) = &self.options {
            let options = options.sqlite_up();
            if let Some(options) = options {
                sql.push_str(&options);
            } else {
                warn!("Failed to generate column options");
                return None;
            }
        }
        Some(sql)
    }
}

#[derive(Clone, Debug, Default)]
pub struct ColumnOptions {
    pub not_null: bool,
    pub unique: bool,
    pub default: Option<Value>,
    pub default_format: Option<ColumnDefault>,
}

fn value_to_default(value: &Value) -> String {
    match value {
        Value::Bool(b) => if *b { "TRUE" } else { "FALSE" }.to_string(),
        Value::U8(u) => u.to_string(),
        Value::U16(u) => u.to_string(),
        Value::U32(u) => u.to_string(),
        Value::U64(u) => u.to_string(),
        Value::I8(u) => u.to_string(),
        Value::I16(u) => u.to_string(),
        Value::I32(u) => u.to_string(),
        Value::I64(u) => u.to_string(),
        Value::F32(u) => u.to_string(),
        Value::F64(u) => u.to_string(),
        Value::Char(c) => {
            let mut s = String::new();
            s.push('\'');
            s.push(*c);
            s.push('\'');
            s
        }
        Value::String(str) => {
            let mut s = String::new();
            s.push('\'');
            s.push_str(str);
            s.push('\'');
            s
        }
        Value::Unit => "".to_string(),
        Value::Option(o) => {
            if let Some(o) = o {
                value_to_default(o)
            } else {
                "".to_string()
            }
        }
        Value::Newtype(n) => {
            let mut s = String::new();
            s.push('\'');
            s.push_str(&serde_json::to_string(n).unwrap());
            s.push('\'');
            s
        }
        Value::Seq(seq) => {
            let mut s = String::new();
            s.push('\'');
            s.push_str(&serde_json::to_string(seq).unwrap());
            s.push('\'');
            s
        }
        Value::Map(m) => {
            let mut s = String::new();
            s.push('\'');
            s.push_str(&serde_json::to_string(m).unwrap());
            s.push('\'');
            s
        }
        Value::Bytes(b) => {
            let mut s = String::new();
            s.push('\'');
            s.push_str(&String::from_utf8_lossy(b));
            s.push('\'');
            s
        }
    }
}

impl SqlUp for ColumnOptions {
    fn sqlite_up(&self) -> Option<String> {
        let mut sql = String::new();
        if self.not_null {
            sql.push_str(" NOT NULL");
        }
        if self.unique {
            sql.push_str(" UNIQUE");
        }
        if let Some(default) = &self.default {
            let default_format = self.default_format.clone().unwrap_or(ColumnDefault::Value);
            let default = match default_format {
                ColumnDefault::Value => value_to_default(default),
                ColumnDefault::Raw => {
                    let default = default.clone();
                    if let Value::String(s) = default {
                        s
                    } else {
                        serde_json::to_string(&default).unwrap()
                    }
                }
            };
            sql.push_str(&format!(" DEFAULT {}", default));
        }
        Some(sql)
    }
}