apache_avro/schema/
name.rs1use serde::{Deserialize, Serialize, Serializer};
19use serde_json::{Map, Value};
20use std::borrow::Cow;
21use std::collections::HashMap;
22use std::fmt::{Debug, Display, Formatter};
23use std::str::FromStr;
24
25use crate::{
26 AvroResult, Error, Schema,
27 error::Details,
28 util::MapHelper,
29 validator::{validate_namespace, validate_schema_name},
30};
31
32#[derive(Clone, Hash, PartialEq, Eq)]
43pub struct Name {
44 namespace_and_name: String,
46 index_of_name: usize,
50}
51
52pub type Aliases = Option<Vec<Alias>>;
54pub type Names = HashMap<Name, Schema>;
56pub type NamesRef<'a> = HashMap<Name, &'a Schema>;
58pub type Namespace = Option<String>;
60pub type NamespaceRef<'a> = Option<&'a str>;
62
63impl Name {
64 pub fn new(name: impl Into<String> + AsRef<str>) -> AvroResult<Self> {
68 Self::new_with_enclosing_namespace(name, None)
69 }
70
71 pub fn new_with_enclosing_namespace(
73 name: impl Into<String> + AsRef<str>,
74 enclosing_namespace: NamespaceRef,
75 ) -> AvroResult<Self> {
76 let name_ref = name.as_ref();
83 let index_of_name = validate_schema_name(name_ref)?;
84 if index_of_name > name_ref.len() {
85 return Err(Details::InvalidSchemaNameValidatorImplementation.into());
86 }
87
88 if index_of_name == 0
89 && let Some(namespace) = enclosing_namespace
90 && !namespace.is_empty()
91 {
92 validate_namespace(namespace)?;
93 Ok(Self {
94 namespace_and_name: format!("{namespace}.{name_ref}"),
95 index_of_name: namespace.len() + 1,
96 })
97 } else if index_of_name == 1 {
98 Ok(Self {
100 namespace_and_name: name.as_ref()[1..].into(),
101 index_of_name: 0,
102 })
103 } else {
104 Ok(Self {
105 namespace_and_name: name.into(),
106 index_of_name,
107 })
108 }
109 }
110
111 pub(crate) fn parse(
113 complex: &Map<String, Value>,
114 enclosing_namespace: NamespaceRef,
115 ) -> AvroResult<Self> {
116 let name_field = complex.name().ok_or(Details::GetNameField)?;
117 Self::new_with_enclosing_namespace(
118 name_field,
119 complex.string("namespace").or(enclosing_namespace),
120 )
121 }
122
123 pub fn name(&self) -> &str {
124 &self.namespace_and_name[self.index_of_name..]
125 }
126
127 pub fn namespace(&self) -> NamespaceRef<'_> {
128 if self.index_of_name == 0 {
129 None
130 } else {
131 Some(&self.namespace_and_name[..(self.index_of_name - 1)])
132 }
133 }
134
135 pub fn fullname(&self, enclosing_namespace: NamespaceRef) -> String {
140 if self.index_of_name == 0
141 && let Some(namespace) = enclosing_namespace
142 && !namespace.is_empty()
143 {
144 format!("{namespace}.{}", self.namespace_and_name)
145 } else {
146 self.namespace_and_name.clone()
147 }
148 }
149
150 pub fn fully_qualified_name(&self, enclosing_namespace: NamespaceRef) -> Cow<'_, Name> {
165 if self.index_of_name == 0
166 && let Some(namespace) = enclosing_namespace
167 && !namespace.is_empty()
168 {
169 Cow::Owned(Self {
170 namespace_and_name: format!("{namespace}.{}", self.namespace_and_name),
171 index_of_name: namespace.len() + 1,
172 })
173 } else {
174 Cow::Borrowed(self)
175 }
176 }
177
178 pub(crate) fn invalid_empty_name() -> Self {
185 Self {
186 namespace_and_name: String::new(),
187 index_of_name: usize::MAX,
188 }
189 }
190}
191
192impl TryFrom<&str> for Name {
193 type Error = Error;
194
195 fn try_from(value: &str) -> Result<Self, Self::Error> {
196 Self::new(value)
197 }
198}
199
200impl TryFrom<String> for Name {
201 type Error = Error;
202
203 fn try_from(value: String) -> Result<Self, Self::Error> {
204 Self::new(&value)
205 }
206}
207
208impl FromStr for Name {
209 type Err = Error;
210
211 fn from_str(s: &str) -> Result<Self, Self::Err> {
212 Self::new(s)
213 }
214}
215
216impl Debug for Name {
217 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
218 if self.index_of_name > self.namespace_and_name.len() {
219 f.debug_tuple("Name").field(&"Invalid name!").finish()
220 } else {
221 let mut debug = f.debug_struct("Name");
222 debug.field("name", &self.name());
223 if self.index_of_name != 0 {
224 debug.field("namespace", &self.namespace());
225 debug.finish()
226 } else {
227 debug.finish_non_exhaustive()
228 }
229 }
230 }
231}
232
233impl AsRef<str> for Name {
234 fn as_ref(&self) -> &str {
235 self.namespace_and_name.as_ref()
236 }
237}
238
239impl Display for Name {
240 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
241 assert!(
242 self.index_of_name <= self.namespace_and_name.len(),
243 "Invalid name used"
244 );
245 f.write_str(&self.namespace_and_name)
246 }
247}
248
249impl<'de> Deserialize<'de> for Name {
250 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
251 where
252 D: serde::de::Deserializer<'de>,
253 {
254 Value::deserialize(deserializer).and_then(|value| {
255 use serde::de::Error;
256 if let Value::Object(json) = value {
257 Name::parse(&json, None).map_err(Error::custom)
258 } else {
259 Err(Error::custom(format!("Expected a JSON object: {value:?}")))
260 }
261 })
262 }
263}
264
265#[derive(Clone, Debug, Hash, PartialEq, Eq)]
269pub struct Alias(Name);
270
271impl Alias {
272 pub fn new(name: impl Into<String> + AsRef<str>) -> AvroResult<Self> {
273 Name::new(name).map(Self)
274 }
275
276 pub fn new_with_enclosing_namespace(
278 name: impl Into<String> + AsRef<str>,
279 enclosing_namespace: NamespaceRef,
280 ) -> AvroResult<Self> {
281 Name::new_with_enclosing_namespace(name, enclosing_namespace).map(Self)
282 }
283
284 pub fn name(&self) -> &str {
285 self.0.name()
286 }
287
288 pub fn namespace(&self) -> NamespaceRef<'_> {
289 self.0.namespace()
290 }
291
292 pub fn fullname(&self, enclosing_namespace: NamespaceRef) -> String {
293 self.0.fullname(enclosing_namespace)
294 }
295
296 pub fn fully_qualified_name(&self, default_namespace: NamespaceRef) -> Cow<'_, Name> {
297 self.0.fully_qualified_name(default_namespace)
298 }
299}
300
301impl TryFrom<&str> for Alias {
302 type Error = Error;
303
304 fn try_from(value: &str) -> Result<Self, Self::Error> {
305 Self::new(value)
306 }
307}
308
309impl TryFrom<String> for Alias {
310 type Error = Error;
311
312 fn try_from(value: String) -> Result<Self, Self::Error> {
313 Self::new(&value)
314 }
315}
316
317impl FromStr for Alias {
318 type Err = Error;
319
320 fn from_str(s: &str) -> Result<Self, Self::Err> {
321 Self::new(s)
322 }
323}
324
325impl Serialize for Alias {
326 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
327 where
328 S: Serializer,
329 {
330 serializer.serialize_str(&self.fullname(None))
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use crate::Error;
337
338 use super::*;
339 use apache_avro_test_helper::TestResult;
340
341 #[test]
342 fn test_namespace_from_name_with_empty_value() -> TestResult {
344 let name = Name::new(".name")?;
345 assert_eq!(name.namespace_and_name, "name");
346 assert_eq!(name.index_of_name, 0);
347
348 Ok(())
349 }
350
351 #[test]
352 fn test_name_with_whitespace_value() {
354 match Name::new(" ").map_err(Error::into_details) {
355 Err(Details::InvalidSchemaName(_, _)) => {}
356 _ => panic!("Expected an Details::InvalidSchemaName!"),
357 }
358 }
359
360 #[test]
361 fn test_name_with_no_name_part() {
363 match Name::new("space.").map_err(Error::into_details) {
364 Err(Details::InvalidSchemaName(_, _)) => {}
365 _ => panic!("Expected an Details::InvalidSchemaName!"),
366 }
367 }
368
369 #[test]
372 fn test_avro_3897_funny_valid_names_and_namespaces() -> TestResult {
373 for funny_name in ["_", "_._", "__._", "_.__", "_._._"] {
374 let name = Name::new(funny_name);
375 assert!(name.is_ok());
376 }
377 Ok(())
378 }
379}