use super::{TableId, Type, table};
use crate::stmt;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Column {
pub id: ColumnId,
pub name: String,
pub ty: stmt::Type,
pub storage_ty: Type,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
pub nullable: bool,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
pub primary_key: bool,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
pub auto_increment: bool,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
pub versionable: bool,
}
#[cfg(feature = "serde")]
fn is_false(b: &bool) -> bool {
!*b
}
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ColumnId {
pub table: TableId,
pub index: usize,
}
impl ColumnId {
pub(crate) fn placeholder() -> Self {
Self {
table: table::TableId::placeholder(),
index: usize::MAX,
}
}
}
impl From<&Column> for ColumnId {
fn from(value: &Column) -> Self {
value.id
}
}
impl fmt::Debug for ColumnId {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "ColumnId({}/{})", self.table.0, self.index)
}
}
#[cfg(all(test, feature = "serde"))]
mod serde_tests {
use crate::schema::db::{Column, ColumnId, TableId, Type};
use crate::stmt;
fn base_column() -> Column {
Column {
id: ColumnId {
table: TableId(0),
index: 0,
},
name: "test".to_string(),
ty: stmt::Type::String,
storage_ty: Type::Text,
nullable: false,
primary_key: false,
auto_increment: false,
versionable: false,
}
}
#[test]
fn false_booleans_are_omitted() {
let toml = toml::to_string(&base_column()).unwrap();
assert!(!toml.contains("nullable"), "toml: {toml}");
assert!(!toml.contains("primary_key"), "toml: {toml}");
assert!(!toml.contains("auto_increment"), "toml: {toml}");
assert!(!toml.contains("versionable"), "toml: {toml}");
}
#[test]
fn nullable_true_is_included() {
let col = Column {
nullable: true,
..base_column()
};
let toml = toml::to_string(&col).unwrap();
assert!(toml.contains("nullable = true"), "toml: {toml}");
}
#[test]
fn primary_key_true_is_included() {
let col = Column {
primary_key: true,
..base_column()
};
let toml = toml::to_string(&col).unwrap();
assert!(toml.contains("primary_key = true"), "toml: {toml}");
}
#[test]
fn auto_increment_true_is_included() {
let col = Column {
auto_increment: true,
..base_column()
};
let toml = toml::to_string(&col).unwrap();
assert!(toml.contains("auto_increment = true"), "toml: {toml}");
}
#[test]
fn missing_bool_fields_deserialize_as_false() {
let toml = "name = \"test\"\nty = \"String\"\nstorage_ty = \"Text\"\n\n[id]\ntable = 0\nindex = 0\n";
let col: Column = toml::from_str(toml).unwrap();
assert!(!col.nullable);
assert!(!col.primary_key);
assert!(!col.auto_increment);
assert!(!col.versionable);
}
#[test]
fn round_trip_all_true() {
let original = Column {
nullable: true,
primary_key: true,
auto_increment: true,
..base_column()
};
let decoded: Column = toml::from_str(&toml::to_string(&original).unwrap()).unwrap();
assert_eq!(original, decoded);
}
}