1use serde::{Deserialize, Serialize};
4
5use crate::identifier::QualifiedName;
6use crate::ir::column_type::ColumnType;
7use crate::ir::difference::Difference;
8use crate::ir::eq::{Equiv, field_difference};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct Cast {
13 pub source: QualifiedName,
15 pub target: QualifiedName,
17 pub method: CastMethod,
19 pub context: CastContext,
21 pub comment: Option<String>,
23}
24
25impl Equiv for Cast {
26 fn differences(&self, other: &Self) -> Vec<Difference> {
27 let Self {
30 source: _,
31 target: _,
32 method: _,
33 context: _,
34 comment: _,
35 } = self;
36 let mut out = Vec::new();
37 out.extend(field_difference("source", &self.source, &other.source));
38 out.extend(field_difference("target", &self.target, &other.target));
39 out.extend(field_difference(
40 "method",
41 &format!("{:?}", self.method),
42 &format!("{:?}", other.method),
43 ));
44 out.extend(field_difference(
45 "context",
46 &format!("{:?}", self.context),
47 &format!("{:?}", other.context),
48 ));
49 out.extend(field_difference(
50 "comment",
51 &format!("{:?}", self.comment),
52 &format!("{:?}", other.comment),
53 ));
54 out
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60pub enum CastMethod {
61 Function {
66 name: QualifiedName,
68 arg_types: Vec<ColumnType>,
70 },
71 Inout,
73 Binary,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
79#[serde(rename_all = "snake_case")]
80pub enum CastContext {
81 Explicit,
83 Assignment,
85 Implicit,
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::identifier::{Identifier, QualifiedName};
93
94 fn id(s: &str) -> Identifier {
95 Identifier::from_unquoted(s).unwrap()
96 }
97
98 fn qname(schema: &str, name: &str) -> QualifiedName {
99 QualifiedName::new(id(schema), id(name))
100 }
101
102 fn cast_function() -> Cast {
103 Cast {
104 source: qname("app", "my_type"),
105 target: qname("pg_catalog", "text"),
106 method: CastMethod::Function {
107 name: qname("app", "my_type_to_text"),
108 arg_types: vec![ColumnType::Integer],
109 },
110 context: CastContext::Explicit,
111 comment: Some("Converts my_type to text.".to_string()),
112 }
113 }
114
115 fn cast_inout() -> Cast {
116 Cast {
117 source: qname("app", "domain_a"),
118 target: qname("app", "domain_b"),
119 method: CastMethod::Inout,
120 context: CastContext::Assignment,
121 comment: None,
122 }
123 }
124
125 fn cast_binary() -> Cast {
126 Cast {
127 source: qname("app", "type_x"),
128 target: qname("app", "type_y"),
129 method: CastMethod::Binary,
130 context: CastContext::Implicit,
131 comment: None,
132 }
133 }
134
135 #[test]
136 fn cast_function_serde_round_trip() {
137 let c = cast_function();
138 let json = serde_json::to_string(&c).unwrap();
139 let back: Cast = serde_json::from_str(&json).unwrap();
140 assert_eq!(c, back);
141 }
142
143 #[test]
144 fn cast_inout_serde_round_trip() {
145 let c = cast_inout();
146 let json = serde_json::to_string(&c).unwrap();
147 let back: Cast = serde_json::from_str(&json).unwrap();
148 assert_eq!(c, back);
149 }
150
151 #[test]
152 fn cast_binary_serde_round_trip() {
153 let c = cast_binary();
154 let json = serde_json::to_string(&c).unwrap();
155 let back: Cast = serde_json::from_str(&json).unwrap();
156 assert_eq!(c, back);
157 }
158
159 #[test]
160 fn cast_context_serde_variants() {
161 let explicit = serde_json::to_string(&CastContext::Explicit).unwrap();
162 let assignment = serde_json::to_string(&CastContext::Assignment).unwrap();
163 let implicit = serde_json::to_string(&CastContext::Implicit).unwrap();
164 assert_eq!(explicit, "\"explicit\"");
165 assert_eq!(assignment, "\"assignment\"");
166 assert_eq!(implicit, "\"implicit\"");
167
168 let back_e: CastContext = serde_json::from_str(&explicit).unwrap();
169 let back_a: CastContext = serde_json::from_str(&assignment).unwrap();
170 let back_i: CastContext = serde_json::from_str(&implicit).unwrap();
171 assert_eq!(back_e, CastContext::Explicit);
172 assert_eq!(back_a, CastContext::Assignment);
173 assert_eq!(back_i, CastContext::Implicit);
174 }
175}