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 {
139 Name {
140 name: self.name.clone(),
141 namespace: self
142 .namespace
143 .clone()
144 .or_else(|| enclosing_namespace.clone().filter(|ns| !ns.is_empty())),
145 }
146 }
147}
148
149impl TryFrom<&str> for Name {
150 type Error = Error;
151
152 fn try_from(value: &str) -> Result<Self, Self::Error> {
153 Self::new(value)
154 }
155}
156
157impl TryFrom<String> for Name {
158 type Error = Error;
159
160 fn try_from(value: String) -> Result<Self, Self::Error> {
161 Self::new(&value)
162 }
163}
164
165impl FromStr for Name {
166 type Err = Error;
167
168 fn from_str(s: &str) -> Result<Self, Self::Err> {
169 Self::new(s)
170 }
171}
172
173impl fmt::Display for Name {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 f.write_str(&self.fullname(None)[..])
176 }
177}
178
179impl<'de> Deserialize<'de> for Name {
180 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
181 where
182 D: serde::de::Deserializer<'de>,
183 {
184 Value::deserialize(deserializer).and_then(|value| {
185 use serde::de::Error;
186 if let Value::Object(json) = value {
187 Name::parse(&json, &None).map_err(Error::custom)
188 } else {
189 Err(Error::custom(format!("Expected a JSON object: {value:?}")))
190 }
191 })
192 }
193}
194
195#[derive(Clone, Debug, Hash, PartialEq, Eq)]
198pub struct Alias(Name);
199
200impl Alias {
201 pub fn new(name: &str) -> AvroResult<Self> {
202 Name::new(name).map(Self)
203 }
204
205 pub fn name(&self) -> String {
206 self.0.name.clone()
207 }
208
209 pub fn namespace(&self) -> Namespace {
210 self.0.namespace.clone()
211 }
212
213 pub fn fullname(&self, default_namespace: Namespace) -> String {
214 self.0.fullname(default_namespace)
215 }
216
217 pub fn fully_qualified_name(&self, default_namespace: &Namespace) -> Name {
218 self.0.fully_qualified_name(default_namespace)
219 }
220}
221
222impl TryFrom<&str> for Alias {
223 type Error = Error;
224
225 fn try_from(value: &str) -> Result<Self, Self::Error> {
226 Self::new(value)
227 }
228}
229
230impl TryFrom<String> for Alias {
231 type Error = Error;
232
233 fn try_from(value: String) -> Result<Self, Self::Error> {
234 Self::new(&value)
235 }
236}
237
238impl FromStr for Alias {
239 type Err = Error;
240
241 fn from_str(s: &str) -> Result<Self, Self::Err> {
242 Self::new(s)
243 }
244}
245
246impl Serialize for Alias {
247 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
248 where
249 S: Serializer,
250 {
251 serializer.serialize_str(&self.fullname(None))
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use crate::Error;
258
259 use super::*;
260 use apache_avro_test_helper::TestResult;
261
262 #[test]
263 fn test_namespace_from_name_with_empty_value() -> TestResult {
265 let name = Name::new(".name")?;
266 assert_eq!(name.name, "name");
267 assert_eq!(name.namespace, None);
268
269 Ok(())
270 }
271
272 #[test]
273 fn test_name_with_whitespace_value() {
275 match Name::new(" ").map_err(Error::into_details) {
276 Err(Details::InvalidSchemaName(_, _)) => {}
277 _ => panic!("Expected an Details::InvalidSchemaName!"),
278 }
279 }
280
281 #[test]
282 fn test_name_with_no_name_part() {
284 match Name::new("space.").map_err(Error::into_details) {
285 Err(Details::InvalidSchemaName(_, _)) => {}
286 _ => panic!("Expected an Details::InvalidSchemaName!"),
287 }
288 }
289
290 #[test]
293 fn test_avro_3897_funny_valid_names_and_namespaces() -> TestResult {
294 for funny_name in ["_", "_._", "__._", "_.__", "_._._"] {
295 let name = Name::new(funny_name);
296 assert!(name.is_ok());
297 }
298 Ok(())
299 }
300}