use serde::{Deserialize, Serialize};
use crate::identifier::QualifiedName;
use crate::ir::column_type::ColumnType;
use crate::ir::difference::Difference;
use crate::ir::eq::{Equiv, field_difference};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Cast {
pub source: QualifiedName,
pub target: QualifiedName,
pub method: CastMethod,
pub context: CastContext,
pub comment: Option<String>,
}
impl Equiv for Cast {
fn differences(&self, other: &Self) -> Vec<Difference> {
let Self {
source: _,
target: _,
method: _,
context: _,
comment: _,
} = self;
let mut out = Vec::new();
out.extend(field_difference("source", &self.source, &other.source));
out.extend(field_difference("target", &self.target, &other.target));
out.extend(field_difference(
"method",
&format!("{:?}", self.method),
&format!("{:?}", other.method),
));
out.extend(field_difference(
"context",
&format!("{:?}", self.context),
&format!("{:?}", other.context),
));
out.extend(field_difference(
"comment",
&format!("{:?}", self.comment),
&format!("{:?}", other.comment),
));
out
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CastMethod {
Function {
name: QualifiedName,
arg_types: Vec<ColumnType>,
},
Inout,
Binary,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CastContext {
Explicit,
Assignment,
Implicit,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::identifier::{Identifier, QualifiedName};
fn id(s: &str) -> Identifier {
Identifier::from_unquoted(s).unwrap()
}
fn qname(schema: &str, name: &str) -> QualifiedName {
QualifiedName::new(id(schema), id(name))
}
fn cast_function() -> Cast {
Cast {
source: qname("app", "my_type"),
target: qname("pg_catalog", "text"),
method: CastMethod::Function {
name: qname("app", "my_type_to_text"),
arg_types: vec![ColumnType::Integer],
},
context: CastContext::Explicit,
comment: Some("Converts my_type to text.".to_string()),
}
}
fn cast_inout() -> Cast {
Cast {
source: qname("app", "domain_a"),
target: qname("app", "domain_b"),
method: CastMethod::Inout,
context: CastContext::Assignment,
comment: None,
}
}
fn cast_binary() -> Cast {
Cast {
source: qname("app", "type_x"),
target: qname("app", "type_y"),
method: CastMethod::Binary,
context: CastContext::Implicit,
comment: None,
}
}
#[test]
fn cast_function_serde_round_trip() {
let c = cast_function();
let json = serde_json::to_string(&c).unwrap();
let back: Cast = serde_json::from_str(&json).unwrap();
assert_eq!(c, back);
}
#[test]
fn cast_inout_serde_round_trip() {
let c = cast_inout();
let json = serde_json::to_string(&c).unwrap();
let back: Cast = serde_json::from_str(&json).unwrap();
assert_eq!(c, back);
}
#[test]
fn cast_binary_serde_round_trip() {
let c = cast_binary();
let json = serde_json::to_string(&c).unwrap();
let back: Cast = serde_json::from_str(&json).unwrap();
assert_eq!(c, back);
}
#[test]
fn cast_context_serde_variants() {
let explicit = serde_json::to_string(&CastContext::Explicit).unwrap();
let assignment = serde_json::to_string(&CastContext::Assignment).unwrap();
let implicit = serde_json::to_string(&CastContext::Implicit).unwrap();
assert_eq!(explicit, "\"explicit\"");
assert_eq!(assignment, "\"assignment\"");
assert_eq!(implicit, "\"implicit\"");
let back_e: CastContext = serde_json::from_str(&explicit).unwrap();
let back_a: CastContext = serde_json::from_str(&assignment).unwrap();
let back_i: CastContext = serde_json::from_str(&implicit).unwrap();
assert_eq!(back_e, CastContext::Explicit);
assert_eq!(back_a, CastContext::Assignment);
assert_eq!(back_i, CastContext::Implicit);
}
}