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>() };
/// }
/// ```