1use std::collections::HashMap;
28
29use crate::constants::OracleType;
30use crate::row::Value;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum CollectionType {
35 PlsqlIndexTable,
37 NestedTable,
39 Varray,
41}
42
43#[derive(Debug, Clone)]
45pub struct DbObjectAttr {
46 pub name: String,
48 pub oracle_type: OracleType,
50 pub max_size: u32,
52 pub precision: u8,
54 pub scale: i8,
56 pub nullable: bool,
58 pub object_type_name: Option<String>,
60}
61
62impl DbObjectAttr {
63 pub fn new(name: impl Into<String>, oracle_type: OracleType) -> Self {
65 Self {
66 name: name.into(),
67 oracle_type,
68 max_size: 0,
69 precision: 0,
70 scale: 0,
71 nullable: true,
72 object_type_name: None,
73 }
74 }
75
76 pub fn with_max_size(mut self, size: u32) -> Self {
78 self.max_size = size;
79 self
80 }
81
82 pub fn with_precision(mut self, precision: u8, scale: i8) -> Self {
84 self.precision = precision;
85 self.scale = scale;
86 self
87 }
88
89 pub fn not_null(mut self) -> Self {
91 self.nullable = false;
92 self
93 }
94
95 pub fn with_object_type(mut self, type_name: impl Into<String>) -> Self {
97 self.object_type_name = Some(type_name.into());
98 self
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct DbObjectType {
105 pub schema: String,
107 pub name: String,
109 pub package_name: Option<String>,
111 pub is_collection: bool,
113 pub collection_type: Option<CollectionType>,
115 pub element_type: Option<OracleType>,
117 pub element_type_name: Option<String>,
119 pub attributes: Vec<DbObjectAttr>,
121 pub oid: Option<Vec<u8>>,
123}
124
125impl DbObjectType {
126 pub fn new(schema: impl Into<String>, name: impl Into<String>) -> Self {
128 Self {
129 schema: schema.into(),
130 name: name.into(),
131 package_name: None,
132 is_collection: false,
133 collection_type: None,
134 element_type: None,
135 element_type_name: None,
136 attributes: Vec::new(),
137 oid: None,
138 }
139 }
140
141 pub fn collection(
143 schema: impl Into<String>,
144 name: impl Into<String>,
145 collection_type: CollectionType,
146 element_type: OracleType,
147 ) -> Self {
148 Self {
149 schema: schema.into(),
150 name: name.into(),
151 package_name: None,
152 is_collection: true,
153 collection_type: Some(collection_type),
154 element_type: Some(element_type),
155 element_type_name: None,
156 attributes: Vec::new(),
157 oid: None,
158 }
159 }
160
161 pub fn full_name(&self) -> String {
163 if let Some(ref pkg) = self.package_name {
164 format!("{}.{}.{}", self.schema, pkg, self.name)
165 } else {
166 format!("{}.{}", self.schema, self.name)
167 }
168 }
169
170 pub fn add_attribute(&mut self, attr: DbObjectAttr) {
172 self.attributes.push(attr);
173 }
174
175 pub fn attribute(&self, name: &str) -> Option<&DbObjectAttr> {
177 self.attributes.iter().find(|a| a.name.eq_ignore_ascii_case(name))
178 }
179
180 pub fn attribute_count(&self) -> usize {
182 self.attributes.len()
183 }
184}
185
186#[derive(Debug, Clone)]
188pub struct DbObject {
189 pub type_name: String,
191 pub values: HashMap<String, Value>,
193 pub elements: Vec<Value>,
195 pub is_collection: bool,
197}
198
199impl DbObject {
200 pub fn new(type_name: impl Into<String>) -> Self {
202 Self {
203 type_name: type_name.into(),
204 values: HashMap::new(),
205 elements: Vec::new(),
206 is_collection: false,
207 }
208 }
209
210 pub fn collection(type_name: impl Into<String>) -> Self {
212 Self {
213 type_name: type_name.into(),
214 values: HashMap::new(),
215 elements: Vec::new(),
216 is_collection: true,
217 }
218 }
219
220 pub fn set(&mut self, name: impl Into<String>, value: impl Into<Value>) {
222 self.values.insert(name.into().to_uppercase(), value.into());
223 }
224
225 pub fn get(&self, name: &str) -> Option<&Value> {
227 self.values.get(&name.to_uppercase())
228 }
229
230 pub fn has(&self, name: &str) -> bool {
232 self.values.contains_key(&name.to_uppercase())
233 }
234
235 pub fn append(&mut self, value: impl Into<Value>) {
237 self.elements.push(value.into());
238 }
239
240 pub fn get_elements(&self) -> &[Value] {
242 &self.elements
243 }
244
245 pub fn len(&self) -> usize {
247 if self.is_collection {
248 self.elements.len()
249 } else {
250 self.values.len()
251 }
252 }
253
254 pub fn is_empty(&self) -> bool {
256 if self.is_collection {
257 self.elements.is_empty()
258 } else {
259 self.values.is_empty()
260 }
261 }
262}
263
264impl From<i64> for Value {
265 fn from(v: i64) -> Self {
266 Value::Integer(v)
267 }
268}
269
270impl From<f64> for Value {
271 fn from(v: f64) -> Self {
272 Value::Float(v)
273 }
274}
275
276impl From<String> for Value {
277 fn from(v: String) -> Self {
278 Value::String(v)
279 }
280}
281
282impl From<&str> for Value {
283 fn from(v: &str) -> Self {
284 Value::String(v.to_string())
285 }
286}
287
288impl From<bool> for Value {
289 fn from(v: bool) -> Self {
290 Value::Boolean(v)
291 }
292}
293
294impl From<Vec<u8>> for Value {
295 fn from(v: Vec<u8>) -> Self {
296 Value::Bytes(v)
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
305 fn test_object_type_creation() {
306 let obj_type = DbObjectType::new("HR", "EMPLOYEE_TYPE");
307 assert_eq!(obj_type.schema, "HR");
308 assert_eq!(obj_type.name, "EMPLOYEE_TYPE");
309 assert_eq!(obj_type.full_name(), "HR.EMPLOYEE_TYPE");
310 assert!(!obj_type.is_collection);
311 }
312
313 #[test]
314 fn test_object_type_with_attributes() {
315 let mut obj_type = DbObjectType::new("HR", "EMPLOYEE_TYPE");
316 obj_type.add_attribute(DbObjectAttr::new("ID", OracleType::Number));
317 obj_type.add_attribute(
318 DbObjectAttr::new("NAME", OracleType::Varchar).with_max_size(100),
319 );
320
321 assert_eq!(obj_type.attribute_count(), 2);
322 assert!(obj_type.attribute("ID").is_some());
323 assert!(obj_type.attribute("name").is_some()); }
325
326 #[test]
327 fn test_collection_type() {
328 let col_type = DbObjectType::collection(
329 "HR",
330 "NUMBER_LIST",
331 CollectionType::Varray,
332 OracleType::Number,
333 );
334
335 assert!(col_type.is_collection);
336 assert_eq!(col_type.collection_type, Some(CollectionType::Varray));
337 assert_eq!(col_type.element_type, Some(OracleType::Number));
338 }
339
340 #[test]
341 fn test_object_instance() {
342 let mut obj = DbObject::new("HR.EMPLOYEE_TYPE");
343 obj.set("ID", 123i64);
344 obj.set("NAME", "John Doe");
345
346 assert_eq!(obj.len(), 2);
347 assert!(obj.has("ID"));
348 assert!(obj.has("name")); assert!(!obj.has("MISSING"));
350
351 match obj.get("ID") {
352 Some(Value::Integer(v)) => assert_eq!(*v, 123),
353 _ => panic!("Expected Integer"),
354 }
355 }
356
357 #[test]
358 fn test_collection_instance() {
359 let mut col = DbObject::collection("HR.NUMBER_LIST");
360 col.append(1i64);
361 col.append(2i64);
362 col.append(3i64);
363
364 assert!(col.is_collection);
365 assert_eq!(col.len(), 3);
366 assert_eq!(col.get_elements().len(), 3);
367 }
368
369 #[test]
370 fn test_attribute_builder() {
371 let attr = DbObjectAttr::new("SALARY", OracleType::Number)
372 .with_precision(10, 2)
373 .not_null();
374
375 assert_eq!(attr.name, "SALARY");
376 assert_eq!(attr.precision, 10);
377 assert_eq!(attr.scale, 2);
378 assert!(!attr.nullable);
379 }
380
381 #[test]
382 fn test_value_from_conversions() {
383 assert!(matches!(Value::from(42i64), Value::Integer(42)));
384 assert!(matches!(Value::from(3.14f64), Value::Float(_)));
385 assert!(matches!(Value::from("test"), Value::String(_)));
386 assert!(matches!(Value::from(true), Value::Boolean(true)));
387 }
388}