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; }