zlink_names/
field_name.rs1use zcheapstr::CheapStr;
4
5use crate::parse;
6
7#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct FieldName<'a>(pub(crate) CheapStr<'a>);
24
25#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct OwnedFieldName(FieldName<'static>);
28
29impl_name!(
30 FieldName,
31 OwnedFieldName,
32 is_valid_field_name,
33 crate::Error::InvalidFieldName,
34 "a Varlink field name"
35);
36
37pub fn is_valid_field_name(name: &str) -> bool {
50 parse::parses_fully(parse::field_name, name)
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::Error;
57
58 #[test]
59 fn field_names() {
60 let valid = ["a", "Z", "user_name", "user0", "a_0_b", "type", "foo123"];
61 for name in valid {
62 assert!(is_valid_field_name(name), "`{name}` must be valid");
63 }
64
65 let invalid = [
66 "",
67 "_foo",
68 "0foo",
69 "user-name",
70 "user name",
71 "user.name",
72 "user!",
73 "üser",
74 "user\u{e9}",
75 "x__",
76 "foo__bar",
77 ];
78 for name in invalid {
79 assert!(!is_valid_field_name(name), "`{name}` must be invalid");
80 }
81 }
82
83 #[test]
84 fn try_from() {
85 let name = FieldName::try_from("user_name").unwrap();
86 assert_eq!(name.as_str(), "user_name");
87 let name = FieldName::try_from(String::from("user_name")).unwrap();
88 assert_eq!(name.as_str(), "user_name");
89
90 assert_eq!(
91 FieldName::try_from("user-name"),
92 Err(Error::InvalidFieldName)
93 );
94 assert_eq!(
95 FieldName::try_from(String::from("_user")),
96 Err(Error::InvalidFieldName)
97 );
98 }
99
100 #[test]
101 fn owned_try_from() {
102 let name = OwnedFieldName::try_from("user_name").unwrap();
103 assert_eq!(name.as_str(), "user_name");
104 let name = OwnedFieldName::try_from(String::from("user_name")).unwrap();
105 assert_eq!(name.as_str(), "user_name");
106
107 assert_eq!(
108 OwnedFieldName::try_from("user-name"),
109 Err(Error::InvalidFieldName)
110 );
111 assert_eq!(
112 OwnedFieldName::try_from(String::new()),
113 Err(Error::InvalidFieldName)
114 );
115 }
116
117 #[test]
120 fn owned_copies_are_independent() {
121 let source = String::from("user_name");
122 let name = FieldName::try_from(source.as_str()).unwrap();
123 let owned: OwnedFieldName = name.to_owned();
124 let owned_too: OwnedFieldName = name.clone().into_owned();
125 let owned_from = OwnedFieldName::from(name);
126 drop(source);
128
129 assert_eq!(owned, "user_name");
130 assert_eq!(owned_too, owned);
131 assert_eq!(owned_from, owned);
132 }
133
134 #[test]
135 fn static_names_are_not_copied() {
136 const NAME: FieldName<'static> = FieldName::from_static_str_unchecked("user_name");
137
138 let name = NAME;
139 assert_eq!(name, "user_name");
140 assert_eq!(
141 name.to_owned().as_str().as_ptr(),
142 name.as_str().as_ptr(),
143 "an owned copy of a static name shares its data"
144 );
145 }
146
147 #[test]
148 fn as_str_in_const() {
149 const NAME: &FieldName<'static> = &FieldName::from_static_str_unchecked("user_name");
150 const NAME_STR: &str = NAME.as_str();
151
152 assert_eq!(NAME_STR, "user_name");
153 }
154
155 #[test]
156 fn borrowed_views_share_data() {
157 let owned = OwnedFieldName::try_from(String::from("user_name")).unwrap();
158 let ptr = owned.as_str().as_ptr();
159
160 let view = owned.as_ref();
161 assert_eq!(view.as_str().as_ptr(), ptr);
162 assert_eq!(FieldName::from(&owned).as_str().as_ptr(), ptr);
163 assert_eq!(FieldName::from(&view).as_str().as_ptr(), ptr);
164 assert_eq!(owned.inner().as_str().as_ptr(), ptr);
165
166 let inner = owned.into_inner();
167 assert_eq!(inner.as_str().as_ptr(), ptr);
168 assert_eq!(FieldName::from(OwnedFieldName::from(inner)), "user_name");
169 }
170
171 #[test]
172 fn comparing() {
173 let name = FieldName::try_from("user_name").unwrap();
174 assert!(name == *"user_name");
175 assert!(name == "user_name");
176 assert!(*"user_name" == name);
177 assert!("user_name" == name);
178 assert!(name != "other");
179 assert!("other" != name);
180
181 let owned = name.to_owned();
182 assert!(owned == *"user_name");
183 assert!(owned == "user_name");
184 assert!(*"user_name" == owned);
185 assert!("user_name" == owned);
186 assert!(owned != "other");
187
188 assert!(name == owned);
191 assert!(owned == name);
192 assert_eq!(name, FieldName::from_static_str_unchecked("user_name"));
193 assert!(FieldName::from_static_str_unchecked("a") < FieldName::try_from("b").unwrap());
194 }
195
196 #[test]
197 fn string_like_access() {
198 let name = FieldName::try_from("user_name").unwrap();
199 assert_eq!(name.to_string(), "user_name");
200 assert_eq!(format!("{name:>11}"), " user_name");
201 assert_eq!(OwnedFieldName::from(name.clone()).to_string(), "user_name");
202
203 assert_eq!(name.len(), 9);
205 assert!(name.starts_with("user"));
206 let as_ref: &str = name.as_ref();
208 assert_eq!(as_ref, "user_name");
209 let owned = name.to_owned();
211 let as_str: &str = &owned;
212 assert_eq!(as_str, "user_name");
213 }
214}