1#![warn(missing_docs)]
2
3use std::borrow::Cow;
7use std::borrow::Borrow;
8use std::fmt;
9use std::error::Error;
10use std::ops::Deref;
11use std::convert::TryFrom;
12
13mod validity;
14
15#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
17pub struct InvalidStringError(&'static str);
18
19impl fmt::Display for InvalidStringError {
20 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21 write!(f, "String is not a valid {}", self.0)
22 }
23}
24
25impl Error for InvalidStringError {}
26
27pub trait StringLike: ToOwned {
31 const NAME: &'static str;
33
34 fn new(s: &str) -> Result<&Self, InvalidStringError> {
36 Self::is_valid(s)?;
37 Ok(Self::new_unchecked(s))
38 }
39
40 fn new_owned<S: Into<String>>(s: S) -> Result<<Self as ToOwned>::Owned, InvalidStringError> {
42 let s = s.into();
43 Self::is_valid(&s)?;
44 Ok(Self::new_unchecked_owned(s))
45 }
46
47 fn new_unchecked(_: &str) -> &Self;
52
53 fn new_unchecked_owned(_: String) -> <Self as ToOwned>::Owned;
58
59 fn is_valid(_: &str) -> Result<(), InvalidStringError>;
61}
62
63macro_rules! string_wrapper_base {
64 ($(#[$comment:meta])* $t: ident, $towned: ident) => {
65 $(#[$comment])*
66 #[repr(transparent)]
70 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
71 pub struct $t(str);
72
73 impl Deref for $t {
74 type Target = str;
75 fn deref(&self) -> &str { &self.0 }
76 }
77
78 impl fmt::Display for $t {
79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) }
80 }
81
82 impl AsRef<str> for $t {
83 fn as_ref(&self) -> &str { &self.0 }
84 }
85
86 impl ToOwned for $t {
87 type Owned = $towned;
88 fn to_owned(&self) -> $towned { $towned(self.0.into()) }
89 }
90
91 impl<'a> TryFrom<&'a str> for &'a $t {
92 type Error = InvalidStringError;
93 fn try_from(s: &'a str) -> Result<&'a $t, Self::Error> { $t::new(s) }
94 }
95
96 $(#[$comment])*
97 #[repr(transparent)]
101 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
102 pub struct $towned(String);
103
104 impl $towned {
105 pub fn new<S: Into<String>>(s: S) -> Result<Self, InvalidStringError> {
107 $t::new_owned(s)
108 }
109
110 pub fn into_inner(self) -> String { self.0 }
112 }
113
114 impl Deref for $towned {
115 type Target = $t;
116 fn deref(&self) -> &$t { $t::new_unchecked(&self.0) }
117 }
118
119 impl Borrow<$t> for $towned {
120 fn borrow(&self) -> &$t { &self }
121 }
122
123 impl fmt::Display for $towned {
124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) }
125 }
126
127 impl TryFrom<String> for $towned {
128 type Error = InvalidStringError;
129 fn try_from(s: String) -> Result<$towned, Self::Error> { $towned::new(s) }
130 }
131
132 impl<'a> From<$towned> for Cow<'a, $t> {
133 fn from(s: $towned) -> Cow<'a, $t> { Cow::Owned(s) }
134 }
135
136 impl<'a> From<&'a $t> for Cow<'a, $t> {
137 fn from(s: &'a $t) -> Cow<'a, $t> { Cow::Borrowed(s) }
138 }
139
140 impl<'a> From<&'a $towned> for Cow<'a, $t> {
141 fn from(s: &'a $towned) -> Cow<'a, $t> { Cow::Borrowed(&s) }
142 }
143 }
144}
145
146macro_rules! string_wrapper {
147 ($(#[$comment:meta])* $t: ident, $towned: ident, $validate: ident) => {
148 string_wrapper_base!($(#[$comment])* $t, $towned);
149
150 impl StringLike for $t {
151 const NAME: &'static str = stringify!($t);
152 fn new_unchecked(s: &str) -> &Self {
153 unsafe { std::mem::transmute(s) }
157 }
158 fn new_unchecked_owned(s: String) -> $towned {
159 $towned(s)
160 }
161 fn is_valid(s: &str) -> Result<(), InvalidStringError> {
162 validity::$validate(s.as_bytes()).map_err(|_| InvalidStringError(Self::NAME))
163 }
164 }
165
166 impl<'a> From<&'a $t> for &'a DBusStr {
167 fn from(s: &'a $t) -> &'a DBusStr { DBusStr::new_unchecked(&*s) }
168 }
169
170 impl<'a> TryFrom<&'a DBusStr> for &'a $t {
171 type Error = InvalidStringError;
172 fn try_from(s: &'a DBusStr) -> Result<&'a $t, Self::Error> {
173 $t::new(&*s)
174 }
175 }
176
177 impl AsRef<DBusStr> for $t {
178 fn as_ref(&self) -> &DBusStr { DBusStr::new_unchecked(self) }
179 }
180
181 impl $t {
182 pub fn as_dbus_str(&self) -> &DBusStr { DBusStr::new_unchecked(self) }
184 }
185
186 impl From<$towned> for DBusString {
187 fn from(s: $towned) -> DBusString { DBusStr::new_unchecked_owned(s.into_inner()) }
188 }
189
190 impl TryFrom<DBusString> for $towned {
191 type Error = InvalidStringError;
192 fn try_from(s: DBusString) -> Result<$towned, Self::Error> {
193 $t::new_owned(s.into_inner())
194 }
195 }
196 }
197}
198
199string_wrapper_base!(
200 DBusStr, DBusString
202);
203
204impl StringLike for DBusStr {
205 const NAME: &'static str = "DBusStr";
206 fn new_unchecked(s: &str) -> &Self {
207 unsafe { std::mem::transmute(s) }
211 }
212 fn new_unchecked_owned(s: String) -> DBusString {
213 DBusString(s)
214 }
215 fn is_valid(s: &str) -> Result<(), InvalidStringError> {
216 validity::is_valid_string(s).map_err(|_| InvalidStringError(Self::NAME))
217 }
218}
219
220string_wrapper!(
221 InterfaceName, InterfaceNameBuf, is_valid_interface_name
225);
226
227string_wrapper!(
228 MemberName, MemberNameBuf, is_valid_member_name
233);
234
235string_wrapper!(
236 ErrorName, ErrorNameBuf, is_valid_error_name
240);
241
242string_wrapper!(
243 BusName, BusNameBuf, is_valid_bus_name
247);
248
249impl<'a> From<&'a SignatureSingle> for &'a SignatureMulti {
250 fn from(s: &'a SignatureSingle) -> &'a SignatureMulti { SignatureMulti::new_unchecked(&s.0) }
251}
252
253impl From<SignatureSingleBuf> for SignatureMultiBuf {
254 fn from(s: SignatureSingleBuf) -> SignatureMultiBuf { SignatureMulti::new_unchecked_owned(s.0) }
255}
256
257
258impl SignatureMulti {
259 pub fn single(&self) -> Option<(&SignatureSingle, &SignatureMulti)> {
263 validity::sig_single(self.as_bytes(), 0, 0).map(|x|
264 (SignatureSingle::new_unchecked(&self[0..x]), SignatureMulti::new_unchecked(&self[x..]))
265 )
266 }
267}
268
269string_wrapper!(
270 SignatureSingle, SignatureSingleBuf, is_valid_signature_single
274);
275
276string_wrapper!(
277 SignatureMulti, SignatureMultiBuf, is_valid_signature_multi
281);
282
283string_wrapper!(
284 ObjectPath, ObjectPathBuf, is_valid_object_path
288);
289
290#[test]
291fn type_conversions() {
292 use std::borrow::Cow;
293 let x: &ObjectPath = ObjectPath::new("/test").unwrap();
294 let y: ObjectPathBuf = ObjectPath::new_owned("/test").unwrap();
295 assert_eq!(x, &*y);
296
297 let x = Cow::from(x);
298 let y = Cow::from(y);
299 assert_eq!(x, y);
300
301 let x: &DBusStr = (&*x).into();
302 let y = DBusString::from(y.into_owned());
303 assert_eq!(x, &*y);
304}
305
306#[test]
307fn errors() {
308 let q = MemberName::new("Hello.world").unwrap_err();
309 assert_eq!(q.to_string(), "String is not a valid MemberName".to_string());
310}
311
312#[test]
313fn sig_split() {
314 let s = SignatureMulti::new("ua{sv}(ss)").unwrap();
315 let (a2, s2) = s.single().unwrap();
316 assert_eq!(&**a2, "u");
317 assert_eq!(&**s2, "a{sv}(ss)");
318
319 let (a3, s3) = s2.single().unwrap();
320 assert_eq!(&**a3, "a{sv}");
321 assert_eq!(&**s3, "(ss)");
322
323 let (a4, s4) = s3.single().unwrap();
324 assert_eq!(&**a4, "(ss)");
325 assert_eq!(&**s4, "");
326
327 assert!(s4.single().is_none());
328}