use std::convert::From;
use crate::{
binding::{class::is_frozen, encoding, string, vm},
types::{Value, ValueType},
AnyException, AnyObject, Array, Boolean, CodepointIterator, Encoding, EncodingSupport,
Exception, Hash, Integer, NilClass, Object, TryConvert, VerifiedObject,
};
#[derive(Debug)]
#[repr(C)]
pub struct RString {
value: Value,
}
impl RString {
#[deprecated(
since = "0.3.2",
note = "please use `new_usascii_unchecked` or `new_utf8` instead"
)]
pub fn new(string: &str) -> Self {
Self::new_usascii_unchecked(string)
}
pub fn new_utf8(string: &str) -> Self {
Self::from(string::new_utf8(string))
}
pub fn new_usascii_unchecked(string: &str) -> Self {
Self::from(string::new(string))
}
pub fn from_bytes(bytes: &[u8], enc: &Encoding) -> Self {
Self::from(string::new_from_bytes(bytes, enc.value()))
}
pub fn to_string(&self) -> String {
string::value_to_string(self.value())
}
pub fn to_string_unchecked(&self) -> String {
string::value_to_string_unchecked(self.value())
}
pub fn to_vec_u8_unchecked(&self) -> Vec<u8> {
self.to_bytes_unchecked().to_vec()
}
pub fn to_str(&self) -> &str {
let value = self.value();
string::value_to_str(value)
}
pub fn to_str_unchecked(&self) -> &str {
let value = self.value();
string::value_to_str_unchecked(value)
}
pub fn to_bytes_unchecked(&self) -> &[u8] {
let value = self.value();
string::value_to_bytes_unchecked(value)
}
pub fn codepoints(&self) -> Array {
CodepointIterator::new(self)
.into_iter()
.map(|n| Integer::new(n as i64).to_any_object())
.collect()
}
pub fn bytesize(&self) -> i64 {
string::bytesize(self.value())
}
pub fn count_chars(&self) -> i64 {
string::count_chars(self.value())
}
pub fn concat(&mut self, string: &str) {
string::concat(self.value(), string.as_bytes());
}
}
impl EncodingSupport for RString {
fn encoding(&self) -> Encoding {
Encoding::from(encoding::from_encoding_index(encoding::enc_get_index(
self.value(),
)))
}
fn force_encoding(&mut self, enc: Encoding) -> Result<Self, AnyException> {
if string::is_lockedtmp(self.value()) {
return Err(AnyException::new(
"RuntimeError",
Some("can't modify string; temporarily locked"),
));
}
if self.is_frozen() {
return Err(AnyException::new(
"FrozenError",
Some("can't modify frozen String"),
));
}
self.value = encoding::force_encoding(self.value(), enc.value());
encoding::coderange_clear(self.value);
Ok(Self::from(self.value()))
}
fn encode(&self, enc: Encoding, opts: Option<Hash>) -> Self {
let nil = NilClass::new().value();
let value = match opts {
Some(options) => {
let ecflags = encoding::econv_prepare_opts(options.value(), &nil);
encoding::encode(self.value(), enc.value(), ecflags, options.value())
}
None => encoding::encode(self.value(), enc.value(), 0, nil),
};
Self::from(value)
}
fn is_valid_encoding(&self) -> bool {
let result = unsafe { self.send("valid_encoding?", &[]) };
result.try_convert_to::<Boolean>().unwrap().to_bool()
}
fn compatible_with(&self, other: &impl Object) -> bool {
encoding::is_compatible_encoding(self.value(), other.value())
}
fn compatible_encoding(obj1: &impl Object, obj2: &impl Object) -> AnyObject {
encoding::compatible_encoding(obj1.value(), obj2.value()).into()
}
}
impl From<Value> for RString {
fn from(value: Value) -> Self {
RString { value }
}
}
impl From<String> for RString {
fn from(string: String) -> Self {
Self::new_utf8(string.as_str())
}
}
impl From<&'static str> for RString {
fn from(string: &'static str) -> Self {
Self::new_utf8(string)
}
}
impl Into<Value> for RString {
fn into(self) -> Value {
self.value
}
}
impl Into<AnyObject> for RString {
fn into(self) -> AnyObject {
AnyObject::from(self.value)
}
}
impl TryConvert<AnyObject> for RString {
type Nil = NilClass;
fn try_convert(obj: AnyObject) -> Result<Self, NilClass> {
let result = string::method_to_str(obj.value());
if result.is_nil() {
Err(NilClass::from(result))
} else {
Ok(Self::from(result))
}
}
}
impl Object for RString {
#[inline]
fn value(&self) -> Value {
self.value
}
}
impl VerifiedObject for RString {
fn is_correct_type<T: Object>(object: &T) -> bool {
object.value().ty() == ValueType::RString
}
fn error_message() -> &'static str {
"Error converting to String"
}
}
impl PartialEq for RString {
fn eq(&self, other: &Self) -> bool {
self.equals(other)
}
}