use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum FieldRef {
FieldId {
#[serde(rename = "field-id")]
id: i64,
},
FieldName {
#[serde(rename = "field-literal")]
name: String,
#[serde(rename = "type")]
field_type: FieldType,
},
ForeignKey {
#[serde(rename = "fk->")]
source_field: Box<FieldRef>,
target_field: Box<FieldRef>,
},
Expression {
#[serde(rename = "expression")]
name: String,
},
}
impl FieldRef {
pub fn field_id(id: i64) -> Self {
FieldRef::FieldId { id }
}
pub fn field_name(name: impl Into<String>) -> Self {
FieldRef::FieldName {
name: name.into(),
field_type: FieldType::Any,
}
}
pub fn field_name_typed(name: impl Into<String>, field_type: FieldType) -> Self {
FieldRef::FieldName {
name: name.into(),
field_type,
}
}
pub fn foreign_key(source: FieldRef, target: FieldRef) -> Self {
FieldRef::ForeignKey {
source_field: Box::new(source),
target_field: Box::new(target),
}
}
pub fn expression(name: impl Into<String>) -> Self {
FieldRef::Expression { name: name.into() }
}
pub fn to_json(&self) -> Value {
match self {
FieldRef::FieldId { id } => json!(["field-id", id]),
FieldRef::FieldName { name, field_type } => {
json!(["field-literal", name, field_type.to_string()])
}
FieldRef::ForeignKey {
source_field,
target_field,
} => {
json!(["fk->", source_field.to_json(), target_field.to_json()])
}
FieldRef::Expression { name } => json!(["expression", name]),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum FieldType {
#[serde(rename = "type/*")]
Any,
#[serde(rename = "type/Text")]
Text,
#[serde(rename = "type/Number")]
Number,
#[serde(rename = "type/Integer")]
Integer,
#[serde(rename = "type/Float")]
Float,
#[serde(rename = "type/Boolean")]
Boolean,
#[serde(rename = "type/Date")]
Date,
#[serde(rename = "type/DateTime")]
DateTime,
#[serde(rename = "type/Time")]
Time,
}
impl FieldType {
pub fn to_string(&self) -> &'static str {
match self {
FieldType::Any => "type/*",
FieldType::Text => "type/Text",
FieldType::Number => "type/Number",
FieldType::Integer => "type/Integer",
FieldType::Float => "type/Float",
FieldType::Boolean => "type/Boolean",
FieldType::Date => "type/Date",
FieldType::DateTime => "type/DateTime",
FieldType::Time => "type/Time",
}
}
}
impl Default for FieldType {
fn default() -> Self {
FieldType::Any
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_field_id() {
let field = FieldRef::field_id(10);
assert_eq!(field.to_json(), json!(["field-id", 10]));
}
#[test]
fn test_field_name() {
let field = FieldRef::field_name("created_at");
assert_eq!(
field.to_json(),
json!(["field-literal", "created_at", "type/*"])
);
}
#[test]
fn test_field_name_typed() {
let field = FieldRef::field_name_typed("created_at", FieldType::DateTime);
assert_eq!(
field.to_json(),
json!(["field-literal", "created_at", "type/DateTime"])
);
}
#[test]
fn test_foreign_key() {
let source = FieldRef::field_id(10);
let target = FieldRef::field_id(20);
let fk = FieldRef::foreign_key(source, target);
assert_eq!(
fk.to_json(),
json!(["fk->", ["field-id", 10], ["field-id", 20]])
);
}
#[test]
fn test_expression() {
let expr = FieldRef::expression("calculated_field");
assert_eq!(expr.to_json(), json!(["expression", "calculated_field"]));
}
}