1use glib_sys;
42use gobject_sys;
43use gstring::GString;
44use std::borrow::Cow;
45use std::cmp::{Eq, Ordering, PartialEq, PartialOrd};
46use std::fmt;
47use std::hash::{Hash, Hasher};
48use std::slice;
49use std::str;
50use translate::*;
51use value;
52use StaticType;
53use Type;
54use Value;
55use VariantTy;
56
57glib_wrapper! {
58 pub struct Variant(Shared<glib_sys::GVariant>);
62
63 match fn {
64 ref => |ptr| glib_sys::g_variant_ref_sink(ptr),
65 unref => |ptr| glib_sys::g_variant_unref(ptr),
66 }
67}
68
69impl StaticType for Variant {
70 fn static_type() -> Type {
71 Type::Variant
72 }
73}
74
75#[doc(hidden)]
76impl<'a> value::FromValueOptional<'a> for Variant {
77 unsafe fn from_value_optional(value: &Value) -> Option<Self> {
78 from_glib_full(gobject_sys::g_value_dup_variant(
79 ToGlibPtr::to_glib_none(value).0,
80 ))
81 }
82}
83
84#[doc(hidden)]
85impl value::SetValue for Variant {
86 unsafe fn set_value(value: &mut Value, this: &Self) {
87 gobject_sys::g_value_set_variant(
88 ToGlibPtrMut::to_glib_none_mut(value).0,
89 ToGlibPtr::<*mut glib_sys::GVariant>::to_glib_none(this).0,
90 )
91 }
92}
93
94#[doc(hidden)]
95impl value::SetValueOptional for Variant {
96 unsafe fn set_value_optional(value: &mut Value, this: Option<&Self>) {
97 gobject_sys::g_value_set_variant(
98 ToGlibPtrMut::to_glib_none_mut(value).0,
99 ToGlibPtr::<*mut glib_sys::GVariant>::to_glib_none(&this).0,
100 )
101 }
102}
103
104impl Variant {
105 pub fn type_(&self) -> &VariantTy {
107 unsafe { VariantTy::from_ptr(glib_sys::g_variant_get_type(self.to_glib_none().0)) }
108 }
109
110 #[inline]
112 pub fn is<T: StaticVariantType>(&self) -> bool {
113 self.type_() == T::static_variant_type()
114 }
115
116 #[inline]
120 pub fn get<T: FromVariant>(&self) -> Option<T> {
121 T::from_variant(self)
122 }
123
124 pub fn get_str(&self) -> Option<&str> {
129 unsafe {
130 match self.type_().to_str() {
131 "s" | "o" | "g" => {
132 let mut len = 0;
133 let ptr = glib_sys::g_variant_get_string(self.to_glib_none().0, &mut len);
134 let ret = str::from_utf8_unchecked(slice::from_raw_parts(
135 ptr as *const u8,
136 len as usize,
137 ));
138 Some(ret)
139 }
140 _ => None,
141 }
142 }
143 }
144}
145
146unsafe impl Send for Variant {}
147unsafe impl Sync for Variant {}
148
149impl fmt::Debug for Variant {
150 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151 f.debug_struct("Variant")
152 .field("ptr", &self.to_glib_none().0)
153 .field("type", &self.type_())
154 .field("value", &self.to_string())
155 .finish()
156 }
157}
158
159impl fmt::Display for Variant {
160 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161 let serialized: GString = unsafe {
162 from_glib_full(glib_sys::g_variant_print(
163 self.to_glib_none().0,
164 false.to_glib(),
165 ))
166 };
167 f.write_str(&serialized)
168 }
169}
170
171impl PartialEq for Variant {
172 fn eq(&self, other: &Self) -> bool {
173 unsafe {
174 from_glib(glib_sys::g_variant_equal(
175 self.to_glib_none().0 as *const _,
176 other.to_glib_none().0 as *const _,
177 ))
178 }
179 }
180}
181
182impl Eq for Variant {}
183
184impl PartialOrd for Variant {
185 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
186 unsafe {
187 if glib_sys::g_variant_classify(self.to_glib_none().0)
188 != glib_sys::g_variant_classify(other.to_glib_none().0)
189 {
190 return None;
191 }
192
193 if glib_sys::g_variant_is_container(self.to_glib_none().0) != glib_sys::GFALSE {
194 return None;
195 }
196
197 let res = glib_sys::g_variant_compare(
198 self.to_glib_none().0 as *const _,
199 other.to_glib_none().0 as *const _,
200 );
201 if res < 0 {
202 Some(Ordering::Less)
203 } else if res > 0 {
204 Some(Ordering::Equal)
205 } else {
206 Some(Ordering::Greater)
207 }
208 }
209 }
210}
211
212impl Hash for Variant {
213 fn hash<H: Hasher>(&self, state: &mut H) {
214 unsafe { state.write_u32(glib_sys::g_variant_hash(self.to_glib_none().0 as *const _)) }
215 }
216}
217
218pub trait ToVariant {
220 fn to_variant(&self) -> Variant;
222}
223
224pub trait FromVariant: Sized + StaticVariantType {
226 fn from_variant(variant: &Variant) -> Option<Self>;
230}
231
232pub trait StaticVariantType {
234 fn static_variant_type() -> Cow<'static, VariantTy>;
236}
237
238impl<'a, T: ?Sized + ToVariant> ToVariant for &'a T {
239 fn to_variant(&self) -> Variant {
240 <T as ToVariant>::to_variant(self)
241 }
242}
243
244impl<'a, T: ?Sized + StaticVariantType> StaticVariantType for &'a T {
245 fn static_variant_type() -> Cow<'static, VariantTy> {
246 <T as StaticVariantType>::static_variant_type()
247 }
248}
249
250macro_rules! impl_numeric {
251 ($name:ty, $type_str:expr, $new_fn:ident, $get_fn:ident) => {
252 impl StaticVariantType for $name {
253 fn static_variant_type() -> Cow<'static, VariantTy> {
254 unsafe { VariantTy::from_str_unchecked($type_str).into() }
255 }
256 }
257
258 impl ToVariant for $name {
259 fn to_variant(&self) -> Variant {
260 unsafe { from_glib_none(glib_sys::$new_fn(*self)) }
261 }
262 }
263
264 impl FromVariant for $name {
265 fn from_variant(variant: &Variant) -> Option<Self> {
266 unsafe {
267 if variant.is::<Self>() {
268 Some(glib_sys::$get_fn(variant.to_glib_none().0))
269 } else {
270 None
271 }
272 }
273 }
274 }
275 };
276}
277
278impl_numeric!(u8, "y", g_variant_new_byte, g_variant_get_byte);
279impl_numeric!(i16, "n", g_variant_new_int16, g_variant_get_int16);
280impl_numeric!(u16, "q", g_variant_new_uint16, g_variant_get_uint16);
281impl_numeric!(i32, "i", g_variant_new_int32, g_variant_get_int32);
282impl_numeric!(u32, "u", g_variant_new_uint32, g_variant_get_uint32);
283impl_numeric!(i64, "x", g_variant_new_int64, g_variant_get_int64);
284impl_numeric!(u64, "t", g_variant_new_uint64, g_variant_get_uint64);
285impl_numeric!(f64, "d", g_variant_new_double, g_variant_get_double);
286
287impl StaticVariantType for bool {
288 fn static_variant_type() -> Cow<'static, VariantTy> {
289 unsafe { VariantTy::from_str_unchecked("b").into() }
290 }
291}
292
293impl ToVariant for bool {
294 fn to_variant(&self) -> Variant {
295 unsafe { from_glib_none(glib_sys::g_variant_new_boolean(self.to_glib())) }
296 }
297}
298
299impl FromVariant for bool {
300 fn from_variant(variant: &Variant) -> Option<Self> {
301 unsafe {
302 if variant.is::<Self>() {
303 Some(from_glib(glib_sys::g_variant_get_boolean(
304 variant.to_glib_none().0,
305 )))
306 } else {
307 None
308 }
309 }
310 }
311}
312
313impl StaticVariantType for String {
314 fn static_variant_type() -> Cow<'static, VariantTy> {
315 unsafe { VariantTy::from_str_unchecked("s").into() }
316 }
317}
318
319impl ToVariant for String {
320 fn to_variant(&self) -> Variant {
321 self[..].to_variant()
322 }
323}
324
325impl FromVariant for String {
326 fn from_variant(variant: &Variant) -> Option<Self> {
327 variant.get_str().map(String::from)
328 }
329}
330
331impl StaticVariantType for str {
332 fn static_variant_type() -> Cow<'static, VariantTy> {
333 unsafe { VariantTy::from_str_unchecked("s").into() }
334 }
335}
336
337impl ToVariant for str {
338 fn to_variant(&self) -> Variant {
339 unsafe { from_glib_none(glib_sys::g_variant_new_take_string(self.to_glib_full())) }
340 }
341}
342
343impl<T: ToVariant> From<T> for Variant {
344 fn from(value: T) -> Variant {
345 value.to_variant()
346 }
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use std::collections::HashSet;
353
354 macro_rules! unsigned {
355 ($name:ident, $ty:ident) => {
356 #[test]
357 fn $name() {
358 let mut n = $ty::max_value();
359 while n > 0 {
360 let v = Variant::from(n);
361 assert_eq!(v.get(), Some(n));
362 n /= 2;
363 }
364 }
365 };
366 }
367
368 macro_rules! signed {
369 ($name:ident, $ty:ident) => {
370 #[test]
371 fn $name() {
372 let mut n = $ty::max_value();
373 while n > 0 {
374 let v = Variant::from(n);
375 assert_eq!(v.get(), Some(n));
376 let v = Variant::from(-n);
377 assert_eq!(v.get(), Some(-n));
378 n /= 2;
379 }
380 }
381 };
382 }
383
384 unsigned!(test_u8, u8);
385 unsigned!(test_u16, u16);
386 unsigned!(test_u32, u32);
387 unsigned!(test_u64, u64);
388 signed!(test_i16, i16);
389 signed!(test_i32, i32);
390 signed!(test_i64, i64);
391
392 #[test]
393 fn test_str() {
394 let s = "this is a test";
395 let v = Variant::from(s);
396 assert_eq!(v.get_str(), Some(s));
397 }
398
399 #[test]
400 fn test_string() {
401 let s = String::from("this is a test");
402 let v = Variant::from(s.clone());
403 assert_eq!(v.get(), Some(s.clone()));
404 }
405
406 #[test]
407 fn test_eq() {
408 let v1 = Variant::from("this is a test");
409 let v2 = Variant::from("this is a test");
410 let v3 = Variant::from("test");
411 assert_eq!(v1, v2);
412 assert!(v1 != v3);
413 }
414
415 #[test]
416 fn test_hash() {
417 let v1 = Variant::from("this is a test");
418 let v2 = Variant::from("this is a test");
419 let v3 = Variant::from("test");
420 let mut set = HashSet::new();
421 set.insert(v1);
422 assert!(set.contains(&v2));
423 assert!(!set.contains(&v3));
424 }
425}