1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use Object;

/// Interface for safe conversions between types
///
/// This trait is required by `Object::convert_to()` function.
///
/// All built-in types like `Hash`, `RString` and others implement it.
///
/// **You should implement this trait for custom classes which you receive from Ruby
/// if at least one of the following statements is false**:
///
///  - you own the Ruby code which passes the object to Rust;
///  - you are sure that the object always has correct type;
///  - your Ruby code has a good test coverage.
///
/// Various techniques can be used to check if the object has correct type:
///
///  - check the class of the object;
///  - check ancestors of the object's class;
///  - check if object is one of built-in objects (when it is inherited from one of those);
///  - use duck typing to check if object responds to required methods;
///  - etc
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate ruru;
///
/// use ruru::types::ValueType;
/// use ruru::{Class, Object, RString, VerifiedObject, VM};
///
/// // Check the class of the object
/// class!(Server);
///
/// impl VerifiedObject for Server {
///     fn is_correct_type<T: Object>(object: &T) -> bool {
///         object.class() == Class::from_existing("Server")
///     }
///
///     fn error_message() -> &'static str {
///         "Error converting to Server"
///     }
/// }
///
/// // Check presence of required methods (duck typing)
/// class!(Request);
///
/// methods!(
///     Request,
///     itself,
///
///     fn protocol() -> RString { RString::new("HTTP") }
///     fn body() -> RString { RString::new("request body") }
/// );
///
/// impl VerifiedObject for Request {
///     fn is_correct_type<T: Object>(object: &T) -> bool {
///         object.respond_to("protocol") && object.respond_to("body")
///     }
///
///     fn error_message() -> &'static str {
///         "Error converting to Request"
///     }
/// }
///
/// // Check if class inherits/includes some class or module
/// class!(Response);
///
/// impl VerifiedObject for Response {
///     fn is_correct_type<T: Object>(object: &T) -> bool {
///         object.class().ancestors().iter()
///             .any(|class| *class == Class::from_existing("BasicResponse"))
///     }
///
///     fn error_message() -> &'static str {
///         "Error converting to Response"
///     }
/// }
///
/// // Check if class was inherited from built-in classes
/// class!(Headers);
///
/// impl VerifiedObject for Headers {
///     fn is_correct_type<T: Object>(object: &T) -> bool {
///         object.value().ty() == ValueType::Hash
///     }
///
///     fn error_message() -> &'static str {
///         "Error converting to Headers"
///     }
/// }
///
/// fn main() {
///     # VM::init();
///     Class::new("Server", None);
///     Class::new("Response", Some(&Class::new("BasicResponse", None)));
///     Class::new("Headers", Some(&Class::from_existing("Hash")));
///     Class::new("Request", None).define(|itself| {
///         itself.def("protocol", protocol);
///         itself.def("body", body);
///     });
///
///     // Create new instances of classes and convert them to `AnyObject`s
///     // (make their type unknown)
///     let server = Class::from_existing("Server").new_instance(vec![]).to_any_object();
///     let request = Class::from_existing("Request").new_instance(vec![]).to_any_object();
///     let response = Class::from_existing("Response").new_instance(vec![]).to_any_object();
///     let headers = Class::from_existing("Headers").new_instance(vec![]).to_any_object();
///
///     assert!(server.try_convert_to::<Server>().is_ok());
///     assert!(request.try_convert_to::<Request>().is_ok());
///     assert!(response.try_convert_to::<Response>().is_ok());
///     assert!(headers.try_convert_to::<Headers>().is_ok());
///
///     // P.S.
///     // The following is possible to compile, but the program will panic
///     // if you perform any actions with these objects.
///     // Try to avoid unsafe conversions.
///     let bad_request = unsafe { server.to::<Request>() };
///     let bad_server = unsafe { headers.to::<Server>() };
/// }
/// ```
pub trait VerifiedObject: Object {
    fn is_correct_type<T: Object>(object: &T) -> bool;
    fn error_message() -> &'static str;
}