lib.rs (8117B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 extern crate libc; 6 extern crate nserror; 7 extern crate nsstring; 8 extern crate xpcom; 9 10 mod bag; 11 12 use std::{ 13 borrow::Cow, 14 convert::{TryFrom, TryInto}, 15 }; 16 17 use libc::c_double; 18 use nserror::{nsresult, NS_ERROR_CANNOT_CONVERT_DATA, NS_OK}; 19 use nsstring::{nsACString, nsAString, nsCString, nsString}; 20 use xpcom::{getter_addrefs, interfaces::nsIVariant, RefPtr}; 21 22 pub use crate::bag::HashPropertyBag; 23 24 extern "C" { 25 fn NS_GetDataType(variant: *const nsIVariant) -> u16; 26 fn NS_NewStorageNullVariant(result: *mut *const nsIVariant); 27 fn NS_NewStorageBooleanVariant(value: bool, result: *mut *const nsIVariant); 28 fn NS_NewStorageIntegerVariant(value: i64, result: *mut *const nsIVariant); 29 fn NS_NewStorageFloatVariant(value: c_double, result: *mut *const nsIVariant); 30 fn NS_NewStorageTextVariant(value: *const nsAString, result: *mut *const nsIVariant); 31 fn NS_NewStorageUTF8TextVariant(value: *const nsACString, result: *mut *const nsIVariant); 32 } 33 34 // These are the relevant parts of the nsXPTTypeTag enum in xptinfo.h, 35 // which nsIVariant.idl reflects into the nsIDataType struct class and uses 36 // to constrain the values of nsIVariant::dataType. 37 #[repr(u16)] 38 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] 39 pub enum DataType { 40 Int32 = 2, 41 Int64 = 3, 42 Double = 9, 43 Bool = 10, 44 Void = 13, 45 CharStr = 15, 46 WCharStr = 16, 47 StringSizeIs = 20, 48 WStringSizeIs = 21, 49 Utf8String = 24, 50 CString = 25, 51 AString = 26, 52 EmptyArray = 254, 53 Empty = 255, 54 } 55 56 impl TryFrom<u16> for DataType { 57 type Error = nsresult; 58 59 /// Converts a raw type tag for an `nsIVariant` into a `DataType` variant. 60 /// Returns `NS_ERROR_CANNOT_CONVERT_DATA` if the type isn't one that we 61 /// support. 62 fn try_from(raw: u16) -> Result<Self, Self::Error> { 63 Ok(match raw { 64 2 => DataType::Int32, 65 3 => DataType::Int64, 66 9 => DataType::Double, 67 10 => DataType::Bool, 68 13 => DataType::Void, 69 15 => DataType::CharStr, 70 16 => DataType::WCharStr, 71 20 => DataType::StringSizeIs, 72 21 => DataType::WStringSizeIs, 73 24 => DataType::Utf8String, 74 25 => DataType::CString, 75 26 => DataType::AString, 76 254 => DataType::EmptyArray, 77 255 => DataType::Empty, 78 _ => Err(NS_ERROR_CANNOT_CONVERT_DATA)?, 79 }) 80 } 81 } 82 83 /// Extension methods implemented on `nsIVariant` types, to make them easier 84 /// to work with. 85 pub trait NsIVariantExt { 86 /// Returns the raw type tag for this variant. Call 87 /// `DataType::try_from()` on this tag to turn it into a `DataType`. 88 fn get_data_type(&self) -> u16; 89 90 /// Tries to clone this variant, failing with `NS_ERROR_CANNOT_CONVERT_DATA` 91 /// if its type is unsupported. 92 fn try_clone(&self) -> Result<RefPtr<nsIVariant>, nsresult>; 93 } 94 95 impl NsIVariantExt for nsIVariant { 96 fn get_data_type(&self) -> u16 { 97 unsafe { NS_GetDataType(self) } 98 } 99 100 fn try_clone(&self) -> Result<RefPtr<nsIVariant>, nsresult> { 101 Ok(match self.get_data_type().try_into()? { 102 DataType::Bool => bool::from_variant(self)?.into_variant(), 103 DataType::Int32 => i32::from_variant(self)?.into_variant(), 104 DataType::Int64 => i64::from_variant(self)?.into_variant(), 105 DataType::Double => f64::from_variant(self)?.into_variant(), 106 DataType::AString | DataType::WCharStr | DataType::WStringSizeIs => { 107 nsString::from_variant(self)?.into_variant() 108 } 109 DataType::CString 110 | DataType::CharStr 111 | DataType::StringSizeIs 112 | DataType::Utf8String => nsCString::from_variant(self)?.into_variant(), 113 DataType::Void | DataType::EmptyArray | DataType::Empty => ().into_variant(), 114 }) 115 } 116 } 117 118 pub trait VariantType { 119 fn type_name() -> Cow<'static, str>; 120 fn into_variant(self) -> RefPtr<nsIVariant>; 121 fn from_variant(variant: &nsIVariant) -> Result<Self, nsresult> 122 where 123 Self: Sized; 124 } 125 126 /// Implements traits to convert between variants and their types. 127 macro_rules! variant { 128 ($typ:ident, $constructor:ident, $getter:ident) => { 129 impl VariantType for $typ { 130 fn type_name() -> Cow<'static, str> { 131 stringify!($typ).into() 132 } 133 fn into_variant(self) -> RefPtr<nsIVariant> { 134 // getter_addrefs returns a Result<RefPtr<T>, nsresult>, 135 // but we know that our $constructor is infallible, so we can 136 // safely unwrap and return the RefPtr. 137 getter_addrefs(|p| { 138 unsafe { $constructor(self.into(), p) }; 139 NS_OK 140 }) 141 .unwrap() 142 } 143 fn from_variant(variant: &nsIVariant) -> Result<$typ, nsresult> { 144 let mut result = $typ::default(); 145 let rv = unsafe { variant.$getter(&mut result) }; 146 if rv.succeeded() { 147 Ok(result) 148 } else { 149 Err(rv) 150 } 151 } 152 } 153 }; 154 (* $typ:ident, $constructor:ident, $getter:ident) => { 155 impl VariantType for $typ { 156 fn type_name() -> Cow<'static, str> { 157 stringify!($typ).into() 158 } 159 fn into_variant(self) -> RefPtr<nsIVariant> { 160 // getter_addrefs returns a Result<RefPtr<T>, nsresult>, 161 // but we know that our $constructor is infallible, so we can 162 // safely unwrap and return the RefPtr. 163 getter_addrefs(|p| { 164 unsafe { $constructor(&*self, p) }; 165 NS_OK 166 }) 167 .unwrap() 168 } 169 fn from_variant(variant: &nsIVariant) -> Result<$typ, nsresult> { 170 let mut result = $typ::new(); 171 let rv = unsafe { variant.$getter(&mut *result) }; 172 if rv.succeeded() { 173 Ok(result) 174 } else { 175 Err(rv) 176 } 177 } 178 } 179 }; 180 } 181 182 // The unit type (()) is a reasonable equivalation of the null variant. 183 // The macro can't produce its implementations of VariantType, however, 184 // so we implement them concretely. 185 impl VariantType for () { 186 fn type_name() -> Cow<'static, str> { 187 "()".into() 188 } 189 fn into_variant(self) -> RefPtr<nsIVariant> { 190 // getter_addrefs returns a Result<RefPtr<T>, nsresult>, 191 // but we know that NS_NewStorageNullVariant is infallible, so we can 192 // safely unwrap and return the RefPtr. 193 getter_addrefs(|p| { 194 unsafe { NS_NewStorageNullVariant(p) }; 195 NS_OK 196 }) 197 .unwrap() 198 } 199 fn from_variant(_variant: &nsIVariant) -> Result<Self, nsresult> { 200 Ok(()) 201 } 202 } 203 204 impl<T> VariantType for Option<T> 205 where 206 T: VariantType, 207 { 208 fn type_name() -> Cow<'static, str> { 209 format!("Option<{}>", T::type_name()).into() 210 } 211 fn into_variant(self) -> RefPtr<nsIVariant> { 212 match self { 213 Some(v) => v.into_variant(), 214 None => ().into_variant(), 215 } 216 } 217 fn from_variant(variant: &nsIVariant) -> Result<Self, nsresult> { 218 Ok(match variant.get_data_type().try_into() { 219 Ok(DataType::Void) | Ok(DataType::EmptyArray) | Ok(DataType::Empty) => None, 220 _ => Some(VariantType::from_variant(variant)?), 221 }) 222 } 223 } 224 225 variant!(bool, NS_NewStorageBooleanVariant, GetAsBool); 226 variant!(i32, NS_NewStorageIntegerVariant, GetAsInt32); 227 variant!(i64, NS_NewStorageIntegerVariant, GetAsInt64); 228 variant!(f64, NS_NewStorageFloatVariant, GetAsDouble); 229 variant!(*nsString, NS_NewStorageTextVariant, GetAsAString); 230 variant!(*nsCString, NS_NewStorageUTF8TextVariant, GetAsAUTF8String);