apache_avro/schema/
name.rs1use serde::{Deserialize, Serialize, Serializer};
19use serde_json::{Map, Value};
20use std::collections::HashMap;
21use std::fmt;
22use std::str::FromStr;
23
24use crate::{
25 AvroResult, Error, Schema,
26 error::Details,
27 util::MapHelper,
28 validator::{validate_namespace, validate_schema_name},
29};
30
31#[derive(Clone, Debug, Hash, PartialEq, Eq)]
42pub struct Name {
43 pub name: String,
44 pub namespace: Namespace,
45}
46
47pub type Aliases = Option<Vec<Alias>>;
49pub type Names = HashMap<Name, Schema>;
51pub type NamesRef<'a> = HashMap<Name, &'a Schema>;
53pub type Namespace = Option<String>;
55
56impl Name {
57 pub fn new(name: &str) -> AvroResult<Self> {
61 let (name, namespace) = Name::get_name_and_namespace(name)?;
62 Ok(Self {
63 name,
64 namespace: namespace.filter(|ns| !ns.is_empty()),
65 })
66 }
67
68 fn get_name_and_namespace(name: &str) -> AvroResult<(String, Namespace)> {
69 validate_schema_name(name)
70 }
71
72 pub(crate) fn parse(
74 complex: &Map<String, Value>,
75 enclosing_namespace: &Namespace,
76 ) -> AvroResult<Self> {
77 let (name, namespace_from_name) = complex
78 .name()
79 .map(|name| Name::get_name_and_namespace(name.as_str()).unwrap())
80 .ok_or(Details::GetNameField)?;
81 let type_name = match complex.get("type") {
83 Some(Value::Object(complex_type)) => complex_type.name().or(None),
84 _ => None,
85 };
86
87 let namespace = namespace_from_name
88 .or_else(|| {
89 complex
90 .string("namespace")
91 .or_else(|| enclosing_namespace.clone())
92 })
93 .filter(|ns| !ns.is_empty());
94
95 if let Some(ref ns) = namespace {
96 validate_namespace(ns)?;
97 }
98
99 Ok(Self {
100 name: type_name.unwrap_or(name),
101 namespace,
102 })
103 }
104
105 pub fn fullname(&self, default_namespace: Namespace) -> String {
110 if self.name.contains('.') {
111 self.name.clone()
112 } else {
113 let namespace = self.namespace.clone().or(default_namespace);
114
115 match namespace {
116 Some(ref namespace) if !namespace.is_empty() => {
117 format!("{}.{}", namespace, self.name)
118 }
119 _ => self.name.clone(),
120 }
121 }
122 }
123
124 pub fn fully_qualified_name(&self, enclosing_namespace: &Namespace) -> Name {
138 Name {
139 name: self.name.clone(),
140 namespace: self
141 .namespace
142 .clone()
143 .or_else(|| enclosing_namespace.clone().filter(|ns| !ns.is_empty())),
144 }
145 }
146}
147
148impl TryFrom<&str> for Name {
149 type Error = Error;
150
151 fn try_from(value: &str) -> Result<Self, Self::Error> {
152 Self::new(value)
153 }
154}
155
156impl TryFrom<String> for Name {
157 type Error = Error;
158
159 fn try_from(value: String) -> Result<Self, Self::Error> {
160 Self::new(&value)
161 }
162}
163
164impl FromStr for Name {
165 type Err = Error;
166
167 fn from_str(s: &str) -> Result<Self, Self::Err> {
168 Self::new(s)
169 }
170}
171
172impl fmt::Display for Name {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 f.write_str(&self.fullname(None)[..])
175 }
176}
177
178impl<'de> Deserialize<'de> for Name {
179 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
180 where
181 D: serde::de::Deserializer<'de>,
182 {
183 Value::deserialize(deserializer).and_then(|value| {
184 use serde::de::Error;
185 if let Value::Object(json) = value {
186 Name::parse(&json, &None).map_err(Error::custom)
187 } else {
188 Err(Error::custom(format!("Expected a JSON object: {value:?}")))
189 }
190 })
191 }
192}
193
194#[derive(Clone, Debug, Hash, PartialEq, Eq)]
197pub struct Alias(Name);
198
199impl Alias {
200 pub fn new(name: &str) -> AvroResult<Self> {
201 Name::new(name).map(Self)
202 }
203
204 pub fn name(&self) -> String {
205 self.0.name.clone()
206 }
207
208 pub fn namespace(&self) -> Namespace {
209 self.0.namespace.clone()
210 }
211
212 pub fn fullname(&self, default_namespace: Namespace) -> String {
213 self.0.fullname(default_namespace)
214 }
215
216 pub fn fully_qualified_name(&self, default_namespace: &Namespace) -> Name {
217 self.0.fully_qualified_name(default_namespace)
218 }
219}
220
221impl TryFrom<&str> for Alias {
222 type Error = Error;
223
224 fn try_from(value: &str) -> Result<Self, Self::Error> {
225 Self::new(value)
226 }
227}
228
229impl TryFrom<String> for Alias {
230 type Error = Error;
231
232 fn try_from(value: String) -> Result<Self, Self::Error> {
233 Self::new(&value)
234 }
235}
236
237impl FromStr for Alias {
238 type Err = Error;
239
240 fn from_str(s: &str) -> Result<Self, Self::Err> {
241 Self::new(s)
242 }
243}
244
245impl Serialize for Alias {
246 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
247 where
248 S: Serializer,
249 {
250 serializer.serialize_str(&self.fullname(None))
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use crate::Error;
257
258 use super::*;
259 use apache_avro_test_helper::TestResult;
260
261 #[test]
262 fn test_namespace_from_name_with_empty_value() -> TestResult {
264 let name = Name::new(".name")?;
265 assert_eq!(name.name, "name");
266 assert_eq!(name.namespace, None);
267
268 Ok(())
269 }
270
271 #[test]
272 fn test_name_with_whitespace_value() {
274 match Name::new(" ").map_err(Error::into_details) {
275 Err(Details::InvalidSchemaName(_, _)) => {}
276 _ => panic!("Expected an Details::InvalidSchemaName!"),
277 }
278 }
279
280 #[test]
281 fn test_name_with_no_name_part() {
283 match Name::new("space.").map_err(Error::into_details) {
284 Err(Details::InvalidSchemaName(_, _)) => {}
285 _ => panic!("Expected an Details::InvalidSchemaName!"),
286 }
287 }
288
289 #[test]
292 fn test_avro_3897_funny_valid_names_and_namespaces() -> TestResult {
293 for funny_name in ["_", "_._", "__._", "_.__", "_._._"] {
294 let name = Name::new(funny_name);
295 assert!(name.is_ok());
296 }
297 Ok(())
298 }
299}