rutie/class/
encoding.rs

1use crate::{
2    binding::encoding,
3    types::{EncodingIndex, Value, ValueType},
4    AnyException, AnyObject, Class, Exception, NilClass, Object, RString, VerifiedObject,
5};
6
7#[derive(Debug)]
8#[repr(C)]
9pub struct Encoding {
10    value: Value,
11}
12
13impl Encoding {
14    /// Creates a UTF-8 instance of `Encoding`.
15    ///
16    /// # Examples
17    ///
18    /// ```
19    /// use rutie::{Encoding, VM};
20    /// # VM::init();
21    ///
22    /// Encoding::utf8();
23    /// ```
24    ///
25    /// Ruby:
26    ///
27    /// ```ruby
28    /// Encoding::UTF_8
29    /// ```
30    pub fn utf8() -> Self {
31        Self::from(encoding::utf8_encoding())
32    }
33
34    /// Creates a US-ASCII instance of `Encoding`.
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// use rutie::{Encoding, VM};
40    /// # VM::init();
41    ///
42    /// Encoding::us_ascii();
43    /// ```
44    ///
45    /// Ruby:
46    ///
47    /// ```ruby
48    /// Encoding::US_ASCII
49    /// ```
50    pub fn us_ascii() -> Self {
51        Self::from(encoding::usascii_encoding())
52    }
53
54    /// Creates a new instance of `Encoding` from the default external encoding.
55    ///
56    /// # Examples
57    ///
58    /// ```
59    /// use rutie::{Encoding, VM};
60    /// # VM::init();
61    ///
62    /// Encoding::default_external();
63    /// ```
64    ///
65    /// Ruby:
66    ///
67    /// ```ruby
68    /// Encoding.default_external
69    /// ```
70    pub fn default_external() -> Self {
71        Self::from(encoding::default_external())
72    }
73
74    /// Creates an instance of `Ok(Encoding)` from the default internal encoding
75    /// if there is one, otherwise it returns `Err(NilClass)`.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use rutie::{Encoding, VM};
81    /// # VM::init();
82    ///
83    /// Encoding::default_internal();
84    /// ```
85    ///
86    /// Ruby:
87    ///
88    /// ```ruby
89    /// Encoding.default_internal
90    /// ```
91    pub fn default_internal() -> Result<Self, NilClass> {
92        let result = encoding::default_internal();
93
94        if result.is_nil() {
95            Err(NilClass::from(result))
96        } else {
97            Ok(Self::from(result))
98        }
99    }
100
101    /// Returns encoding name.
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// use rutie::{RString, Encoding, VM};
107    /// # VM::init();
108    ///
109    /// let enc = Encoding::utf8();
110    ///
111    /// assert_eq!(enc.name(), "UTF-8")
112    /// ```
113    ///
114    /// Ruby:
115    ///
116    /// ```ruby
117    /// enc = Encoding::UTF_8
118    ///
119    /// enc.name == "UTF-8"
120    /// ```
121    pub fn name(&self) -> String {
122        let name = unsafe { self.send("name", &[]) };
123
124        RString::from(name.value()).to_string()
125    }
126
127    /// Find an `Ok(Encoding)` for given string name or return an `Err(AnyException)`.
128    ///
129    /// # Examples
130    ///
131    /// ```
132    /// use rutie::{VM, Encoding};
133    /// # VM::init();
134    ///
135    /// let encoding = Encoding::find("UTF-8");
136    ///
137    /// match encoding {
138    ///     Ok(enc) => assert_eq!(enc.name(), "UTF-8"),
139    ///     Err(_) => unreachable!()
140    /// }
141    /// ```
142    ///
143    /// Ruby:
144    ///
145    /// ```ruby
146    /// encoding = Encoding.find("UTF-8")
147    ///
148    /// encoding.name == "UTF-8"
149    /// ```
150    ///
151    /// The following is an example where a Ruby exception object of `ArgumentError` is returned.
152    ///
153    /// ```
154    /// use rutie::{VM, Encoding, Exception};
155    /// # VM::init();
156    ///
157    /// let encoding = Encoding::find("UTF8");
158    ///
159    /// match encoding {
160    ///     Ok(_) => unreachable!(),
161    ///     Err(e) => assert_eq!(e.message(), "unknown encoding name - UTF8")
162    /// }
163    /// ```
164    pub fn find(s: &str) -> Result<Encoding, AnyException> {
165        let idx = encoding::find_encoding_index(s);
166
167        if idx < 0 {
168            Err(AnyException::new(
169                "ArgumentError",
170                Some(&format!("unknown encoding name - {}", s)),
171            ))
172        } else {
173            Ok(Encoding::from(encoding::from_encoding_index(idx)))
174        }
175    }
176
177    /// Returns an instance of `Ok(Encoding)` if the objects are
178    /// compatible encodings, otherwise it returns `Err(NilClass)`.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// use rutie::{Encoding, VM, RString, NilClass};
184    /// # VM::init();
185    ///
186    /// let utf8 = RString::new_utf8("asdf");
187    /// let us_ascii= RString::new_usascii_unchecked("qwerty");
188    ///
189    /// let result = Encoding::is_compatible(&utf8, &us_ascii);
190    ///
191    /// assert!(result.is_ok());
192    ///
193    /// let result = Encoding::is_compatible(&utf8, &NilClass::new());
194    ///
195    /// assert!(result.is_err());
196    /// ```
197    pub fn is_compatible(obj1: &impl Object, obj2: &impl Object) -> Result<Self, NilClass> {
198        let result = encoding::compatible_encoding(obj1.value(), obj2.value());
199
200        if result.is_nil() {
201            Err(NilClass::from(result))
202        } else {
203            Ok(Self::from(result))
204        }
205    }
206}
207
208impl Default for Encoding {
209    fn default() -> Self {
210        Encoding::default_external()
211    }
212}
213
214impl From<Value> for Encoding {
215    fn from(value: Value) -> Self {
216        Encoding { value }
217    }
218}
219
220impl Into<Value> for Encoding {
221    fn into(self) -> Value {
222        self.value
223    }
224}
225
226impl Into<AnyObject> for Encoding {
227    fn into(self) -> AnyObject {
228        AnyObject::from(self.value)
229    }
230}
231
232impl Object for Encoding {
233    #[inline]
234    fn value(&self) -> Value {
235        self.value
236    }
237}
238
239impl VerifiedObject for Encoding {
240    fn is_correct_type<T: Object>(object: &T) -> bool {
241        object.value().ty() == ValueType::Class
242            && Class::from_existing("Encoding").case_equals(object)
243    }
244
245    fn error_message() -> &'static str {
246        "Error converting to Encoding"
247    }
248}
249
250impl PartialEq for Encoding {
251    fn eq(&self, other: &Self) -> bool {
252        self.equals(other)
253    }
254}