iceberg_rust_spec/spec/
identifier.rs1use core::fmt::{self, Display};
6use derive_getters::Getters;
7
8use serde_derive::{Deserialize, Serialize};
9
10use crate::error::Error;
11
12use super::namespace::Namespace;
13
14pub static SEPARATOR: &str = ".";
16
17#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub struct Identifier {
20 namespace: Namespace,
21 name: String,
22}
23
24impl Identifier {
25 pub fn new(namespace: &[String], name: &str) -> Self {
27 Self {
28 namespace: Namespace(namespace.to_vec()),
29 name: name.to_owned(),
30 }
31 }
32
33 pub fn try_new(names: &[String], default_namespace: Option<&[String]>) -> Result<Self, Error> {
35 let mut parts = names.iter().rev();
36 let table_name = parts.next().ok_or(Error::InvalidFormat(format!(
37 "Identifier {names:?} is empty"
38 )))?;
39 if table_name.is_empty() {
40 return Err(Error::InvalidFormat(format!(
41 "Table name {table_name:?} is empty"
42 )));
43 }
44 let namespace: Vec<String> = parts.rev().map(ToOwned::to_owned).collect();
45 let namespace = if namespace.is_empty() {
46 default_namespace
47 .ok_or(Error::NotFound("Default namespace".to_owned()))?
48 .iter()
49 .map(ToOwned::to_owned)
50 .collect()
51 } else {
52 namespace
53 };
54 Ok(Identifier {
55 namespace: Namespace(namespace),
56 name: table_name.to_owned(),
57 })
58 }
59
60 pub fn parse(identifier: &str, default_namespace: Option<&[String]>) -> Result<Self, Error> {
62 let names = identifier
63 .split(SEPARATOR)
64 .map(ToOwned::to_owned)
65 .collect::<Vec<String>>();
66 Identifier::try_new(&names, default_namespace)
67 }
68 pub fn namespace(&self) -> &Namespace {
70 &self.namespace
71 }
72 pub fn name(&self) -> &str {
74 &self.name
75 }
76}
77
78impl Display for Identifier {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 write!(f, "{}{}{}", self.namespace, SEPARATOR, self.name)
81 }
82}
83
84impl TryFrom<&str> for Identifier {
85 type Error = Error;
86 fn try_from(value: &str) -> Result<Self, Self::Error> {
87 Self::parse(value, None)
88 }
89}
90
91#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Getters)]
93pub struct FullIdentifier {
94 #[serde(skip_serializing_if = "Option::is_none")]
95 catalog: Option<String>,
96 namespace: Namespace,
97 name: String,
98}
99
100impl FullIdentifier {
101 pub fn new(catalog: Option<&str>, namespace: &[String], name: &str) -> Self {
102 Self {
103 catalog: catalog.map(ToString::to_string),
104 namespace: Namespace(namespace.to_owned()),
105 name: name.to_owned(),
106 }
107 }
108}
109
110impl From<&FullIdentifier> for Identifier {
111 fn from(value: &FullIdentifier) -> Self {
112 Identifier {
113 namespace: value.namespace.clone(),
114 name: value.name.clone(),
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::Identifier;
122
123 #[test]
124 fn test_new() {
125 let identifier = Identifier::try_new(
126 &[
127 "level1".to_string(),
128 "level2".to_string(),
129 "table".to_string(),
130 ],
131 None,
132 )
133 .unwrap();
134 assert_eq!(&format!("{identifier}"), "level1.level2.table");
135 }
136 #[test]
137 #[should_panic]
138 fn test_empty() {
139 let _ = Identifier::try_new(
140 &["level1".to_string(), "level2".to_string(), "".to_string()],
141 None,
142 )
143 .unwrap();
144 }
145 #[test]
146 #[should_panic]
147 fn test_empty_identifier() {
148 let _ = Identifier::try_new(&[], None).unwrap();
149 }
150 #[test]
151 fn test_parse() {
152 let identifier = Identifier::parse("level1.level2.table", None).unwrap();
153 assert_eq!(&format!("{identifier}"), "level1.level2.table");
154 }
155}