1 // Copyright 2013-2016, The Gtk-rs Project Developers. 2 // See the COPYRIGHT file at the top-level directory of this distribution. 3 // Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT> 4 5 //! `Variant` binding and helper traits. 6 //! 7 //! [`Variant`](struct.Variant.html) is an immutable dynamically-typed generic 8 //! container. Its type and value are defined at construction and never change. 9 //! 10 //! `Variant` types are described by [`VariantType`](../struct.VariantType.html) 11 //! "type strings". 12 //! 13 //! Although `GVariant` supports arbitrarily complex types, this binding is 14 //! currently limited to the basic ones: `bool`, `u8`, `i16`, `u16`, `i32`, 15 //! `u32`, `i64`, `u64`, `f64` and `&str`/`String`. 16 //! 17 //! # Examples 18 //! 19 //! ``` 20 //! use glib::prelude::*; // or `use gtk::prelude::*;` 21 //! use glib::Variant; 22 //! 23 //! // Using the `ToVariant` trait. 24 //! let num = 10.to_variant(); 25 //! 26 //! // `is` tests the type of the value. 27 //! assert!(num.is::<i32>()); 28 //! 29 //! // `get` tries to extract the value. 30 //! assert_eq!(num.get::<i32>(), Some(10)); 31 //! assert_eq!(num.get::<u32>(), None); 32 //! 33 //! // `Variant` implements `From` 34 //! let hello = Variant::from("Hello!"); 35 //! 36 //! // `get_str` tries to borrow a string slice. 37 //! assert_eq!(hello.get_str(), Some("Hello!")); 38 //! assert_eq!(num.get_str(), None); 39 //! ``` 40 41 use glib_sys; 42 use gobject_sys; 43 use gstring::GString; 44 use std::borrow::Cow; 45 use std::cmp::{Eq, Ordering, PartialEq, PartialOrd}; 46 use std::fmt; 47 use std::hash::{Hash, Hasher}; 48 use std::slice; 49 use std::str; 50 use translate::*; 51 use value; 52 use StaticType; 53 use Type; 54 use Value; 55 use VariantTy; 56 57 glib_wrapper! { 58 /// A generic immutable value capable of carrying various types. 59 /// 60 /// See the [module documentation](index.html) for more details. 61 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 69 impl StaticType for Variant { static_type() -> Type70 fn static_type() -> Type { 71 Type::Variant 72 } 73 } 74 75 #[doc(hidden)] 76 impl<'a> value::FromValueOptional<'a> for Variant { from_value_optional(value: &Value) -> Option<Self>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)] 85 impl value::SetValue for Variant { set_value(value: &mut Value, this: &Self)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)] 95 impl value::SetValueOptional for Variant { set_value_optional(value: &mut Value, this: Option<&Self>)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 104 impl Variant { 105 /// Returns the type of the value. type_(&self) -> &VariantTy106 pub fn type_(&self) -> &VariantTy { 107 unsafe { VariantTy::from_ptr(glib_sys::g_variant_get_type(self.to_glib_none().0)) } 108 } 109 110 /// Returns `true` if the type of the value corresponds to `T`. 111 #[inline] is<T: StaticVariantType>(&self) -> bool112 pub fn is<T: StaticVariantType>(&self) -> bool { 113 self.type_() == T::static_variant_type() 114 } 115 116 /// Tries to extract a value of type `T`. 117 /// 118 /// Returns `Some` if `T` matches the variant's type. 119 #[inline] get<T: FromVariant>(&self) -> Option<T>120 pub fn get<T: FromVariant>(&self) -> Option<T> { 121 T::from_variant(self) 122 } 123 124 /// Tries to extract a `&str`. 125 /// 126 /// Returns `Some` if the variant has a string type (`s`, `o` or `g` type 127 /// strings). get_str(&self) -> Option<&str>128 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 146 unsafe impl Send for Variant {} 147 unsafe impl Sync for Variant {} 148 149 impl fmt::Debug for Variant { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result150 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 159 impl fmt::Display for Variant { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result160 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 171 impl PartialEq for Variant { eq(&self, other: &Self) -> bool172 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 182 impl Eq for Variant {} 183 184 impl PartialOrd for Variant { partial_cmp(&self, other: &Self) -> Option<Ordering>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 212 impl Hash for Variant { hash<H: Hasher>(&self, state: &mut H)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 218 /// Converts to `Variant`. 219 pub trait ToVariant { 220 /// Returns a `Variant` clone of `self`. to_variant(&self) -> Variant221 fn to_variant(&self) -> Variant; 222 } 223 224 /// Extracts a value. 225 pub trait FromVariant: Sized + StaticVariantType { 226 /// Tries to extract a value. 227 /// 228 /// Returns `Some` if the variant's type matches `Self`. from_variant(variant: &Variant) -> Option<Self>229 fn from_variant(variant: &Variant) -> Option<Self>; 230 } 231 232 /// Returns `VariantType` of `Self`. 233 pub trait StaticVariantType { 234 /// Returns the `VariantType` corresponding to `Self`. static_variant_type() -> Cow<'static, VariantTy>235 fn static_variant_type() -> Cow<'static, VariantTy>; 236 } 237 238 impl<'a, T: ?Sized + ToVariant> ToVariant for &'a T { to_variant(&self) -> Variant239 fn to_variant(&self) -> Variant { 240 <T as ToVariant>::to_variant(self) 241 } 242 } 243 244 impl<'a, T: ?Sized + StaticVariantType> StaticVariantType for &'a T { static_variant_type() -> Cow<'static, VariantTy>245 fn static_variant_type() -> Cow<'static, VariantTy> { 246 <T as StaticVariantType>::static_variant_type() 247 } 248 } 249 250 macro_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 278 impl_numeric!(u8, "y", g_variant_new_byte, g_variant_get_byte); 279 impl_numeric!(i16, "n", g_variant_new_int16, g_variant_get_int16); 280 impl_numeric!(u16, "q", g_variant_new_uint16, g_variant_get_uint16); 281 impl_numeric!(i32, "i", g_variant_new_int32, g_variant_get_int32); 282 impl_numeric!(u32, "u", g_variant_new_uint32, g_variant_get_uint32); 283 impl_numeric!(i64, "x", g_variant_new_int64, g_variant_get_int64); 284 impl_numeric!(u64, "t", g_variant_new_uint64, g_variant_get_uint64); 285 impl_numeric!(f64, "d", g_variant_new_double, g_variant_get_double); 286 287 impl StaticVariantType for bool { static_variant_type() -> Cow<'static, VariantTy>288 fn static_variant_type() -> Cow<'static, VariantTy> { 289 unsafe { VariantTy::from_str_unchecked("b").into() } 290 } 291 } 292 293 impl ToVariant for bool { to_variant(&self) -> Variant294 fn to_variant(&self) -> Variant { 295 unsafe { from_glib_none(glib_sys::g_variant_new_boolean(self.to_glib())) } 296 } 297 } 298 299 impl FromVariant for bool { from_variant(variant: &Variant) -> Option<Self>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 313 impl StaticVariantType for String { static_variant_type() -> Cow<'static, VariantTy>314 fn static_variant_type() -> Cow<'static, VariantTy> { 315 unsafe { VariantTy::from_str_unchecked("s").into() } 316 } 317 } 318 319 impl ToVariant for String { to_variant(&self) -> Variant320 fn to_variant(&self) -> Variant { 321 self[..].to_variant() 322 } 323 } 324 325 impl FromVariant for String { from_variant(variant: &Variant) -> Option<Self>326 fn from_variant(variant: &Variant) -> Option<Self> { 327 variant.get_str().map(String::from) 328 } 329 } 330 331 impl StaticVariantType for str { static_variant_type() -> Cow<'static, VariantTy>332 fn static_variant_type() -> Cow<'static, VariantTy> { 333 unsafe { VariantTy::from_str_unchecked("s").into() } 334 } 335 } 336 337 impl ToVariant for str { to_variant(&self) -> Variant338 fn to_variant(&self) -> Variant { 339 unsafe { from_glib_none(glib_sys::g_variant_new_take_string(self.to_glib_full())) } 340 } 341 } 342 343 impl<T: ToVariant> From<T> for Variant { from(value: T) -> Variant344 fn from(value: T) -> Variant { 345 value.to_variant() 346 } 347 } 348 349 #[cfg(test)] 350 mod 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] test_str()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] test_string()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] test_eq()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] test_hash()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 } 426