use std::{
convert::{TryFrom, TryInto},
error::Error,
ffi::{CStr, CString, FromBytesWithNulError},
fmt,
os::raw::c_int,
};
use crate::{
object::NonNullObject,
prelude::*,
ruby,
};
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Encoding(NonNullObject);
impl AsRef<AnyObject> for Encoding {
#[inline]
fn as_ref(&self) -> &AnyObject { self.0.as_ref() }
}
impl From<Encoding> for AnyObject {
#[inline]
fn from(object: Encoding) -> AnyObject { object.0.into() }
}
impl PartialEq<AnyObject> for Encoding {
#[inline]
fn eq(&self, obj: &AnyObject) -> bool {
self.as_any_object() == obj
}
}
unsafe impl Object for Encoding {
#[inline]
fn cast<A: Object>(obj: A) -> Option<Self> {
if obj.class().inherits(Class::of::<Self>()) {
unsafe { Some(Self::cast_unchecked(obj)) }
} else {
None
}
}
}
impl fmt::Display for Encoding {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.as_any_object().fmt(f)
}
}
impl PartialEq for Encoding {
#[inline]
fn eq(&self, other: &Self) -> bool {
self._enc() == other._enc()
}
}
impl Eq for Encoding {}
impl TryFrom<&CStr> for Encoding {
type Error = EncodingLookupError;
#[inline]
fn try_from(s: &CStr) -> Result<Self, Self::Error> {
let index = unsafe { ruby::rb_enc_find_index(s.as_ptr()) };
if index < 0 {
Err(EncodingLookupError::UnknownName)
} else {
Ok(Encoding::_from_index(index))
}
}
}
impl TryFrom<&CString> for Encoding {
type Error = EncodingLookupError;
#[inline]
fn try_from(s: &CString) -> Result<Self, Self::Error> {
s.as_c_str().try_into()
}
}
impl TryFrom<&[u8]> for Encoding {
type Error = EncodingLookupError;
#[inline]
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
CStr::from_bytes_with_nul(bytes)?.try_into()
}
}
impl TryFrom<&Vec<u8>> for Encoding {
type Error = EncodingLookupError;
#[inline]
fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
bytes.as_slice().try_into()
}
}
impl TryFrom<&str> for Encoding {
type Error = EncodingLookupError;
#[inline]
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.as_bytes().try_into()
}
}
impl TryFrom<&std::string::String> for Encoding {
type Error = EncodingLookupError;
#[inline]
fn try_from(s: &std::string::String) -> Result<Self, Self::Error> {
s.as_str().try_into()
}
}
impl Encoding {
#[inline]
pub(crate) fn _from_enc(enc: *mut ruby::rb_encoding) -> Self {
unsafe { Self::from_raw(ruby::rb_enc_from_encoding(enc)) }
}
#[inline]
pub(crate) fn _from_index(i: c_int) -> Self {
unsafe { Self::_from_enc(ruby::rb_enc_from_index(i)) }
}
#[inline]
pub(crate) fn _rdata(self) -> *mut ruby::RData {
self.as_any_object()._ptr() as _
}
#[inline]
pub(crate) fn _enc(self) -> *mut ruby::rb_encoding {
unsafe {
let enc = (*self._rdata()).data as *mut ruby::rb_encoding;
debug_assert_eq!(enc, ruby::rb_to_encoding(self.raw()));
enc
}
}
#[inline]
pub(crate) fn _index(self) -> c_int {
unsafe { (*self._enc()).index() }
}
#[inline]
pub fn ascii_8bit() -> Encoding {
unsafe { Encoding::_from_enc(ruby::rb_ascii8bit_encoding()) }
}
#[inline]
pub fn utf8() -> Encoding {
unsafe { Encoding::_from_enc(ruby::rb_utf8_encoding()) }
}
#[inline]
pub fn us_ascii() -> Encoding {
unsafe { Encoding::_from_enc(ruby::rb_usascii_encoding()) }
}
#[inline]
pub fn find<E>(encoding: E) -> Result<Self, EncodingLookupError>
where E: TryInto<Self, Error=EncodingLookupError>
{
encoding.try_into()
}
#[inline]
pub fn name(&self) -> &CStr {
unsafe { CStr::from_ptr((*self._enc()).name) }
}
#[inline]
pub fn is_ascii_8bit(self) -> bool {
self._index() == ruby::rb_encoding::ascii_8bit_index()
}
#[inline]
pub fn is_utf8(self) -> bool {
self._index() == ruby::rb_encoding::utf8_index()
}
#[inline]
pub fn is_us_ascii(self) -> bool {
self._index() == ruby::rb_encoding::us_ascii_index()
}
#[inline]
pub fn is_locale(self) -> bool {
unsafe { self._index() == ruby::rb_locale_encindex() }
}
#[inline]
pub fn is_filesystem(self) -> bool {
unsafe { self._index() == ruby::rb_filesystem_encindex() }
}
#[inline]
pub fn is_default_external(self) -> bool {
unsafe { self._enc() == ruby::rb_default_external_encoding() }
}
#[inline]
pub fn is_default_internal(self) -> bool {
unsafe { self._enc() == ruby::rb_default_internal_encoding() }
}
}
#[derive(Debug)]
pub enum EncodingLookupError {
UnknownName,
InvalidCStr(FromBytesWithNulError),
}
impl Error for EncodingLookupError {
#[inline]
fn description(&self) -> &str {
use EncodingLookupError::*;
match self {
UnknownName => "Unknown encoding name",
InvalidCStr(error) => error.description(),
}
}
}
impl fmt::Display for EncodingLookupError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use EncodingLookupError::*;
match self {
UnknownName => self.description().fmt(f),
InvalidCStr(error) => error.fmt(f),
}
}
}
impl From<FromBytesWithNulError> for EncodingLookupError {
#[inline]
fn from(error: FromBytesWithNulError) -> Self {
EncodingLookupError::InvalidCStr(error)
}
}