use core::fmt;
use crate::parse;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive] pub enum Encoding<'a> {
Char,
Short,
Int,
Long,
LongLong,
UChar,
UShort,
UInt,
ULong,
ULongLong,
Float,
Double,
LongDouble,
FloatComplex,
DoubleComplex,
LongDoubleComplex,
Bool,
Void,
String,
Object,
Block,
Class,
Sel,
Unknown,
BitField(u8, &'a Encoding<'a>),
Pointer(&'a Encoding<'a>),
Array(usize, &'a Encoding<'a>),
Struct(&'a str, &'a [Encoding<'a>]),
Union(&'a str, &'a [Encoding<'a>]),
}
impl Encoding<'_> {
pub const C_LONG: Self = {
if cfg!(any(target_pointer_width = "32", windows)) {
Self::Long
} else {
Self::LongLong
}
};
pub const C_U_LONG: Self = {
if cfg!(any(target_pointer_width = "32", windows)) {
Encoding::ULong
} else {
Encoding::ULongLong
}
};
pub fn equivalent_to(&self, other: &Self) -> bool {
self == other
}
pub fn equivalent_to_str(&self, s: &str) -> bool {
if let Some(res) = self.equivalent_to_start_of_str(s) {
res.is_empty()
} else {
false
}
}
pub fn equivalent_to_start_of_str<'a>(&self, s: &'a str) -> Option<&'a str> {
let s = s.trim_start_matches(parse::QUALIFIERS);
parse::rm_enc_prefix(s, self)
}
}
impl fmt::Display for Encoding<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
use Encoding::*;
let code = match *self {
Char => "c",
Short => "s",
Int => "i",
Long => "l",
LongLong => "q",
UChar => "C",
UShort => "S",
UInt => "I",
ULong => "L",
ULongLong => "Q",
Float => "f",
Double => "d",
LongDouble => "D",
FloatComplex => "jf",
DoubleComplex => "jd",
LongDoubleComplex => "jD",
Bool => "B",
Void => "v",
String => "*",
Object => "@",
Block => "@?",
Class => "#",
Sel => ":",
Unknown => "?",
BitField(b, _type) => {
return write!(formatter, "b{}", b);
}
Pointer(t) => {
return write!(formatter, "^{}", t);
}
Array(len, item) => {
return write!(formatter, "[{}{}]", len, item);
}
Struct(name, fields) => {
write!(formatter, "{{{}=", name)?;
for field in fields {
fmt::Display::fmt(field, formatter)?;
}
return formatter.write_str("}");
}
Union(name, members) => {
write!(formatter, "({}=", name)?;
for member in members {
fmt::Display::fmt(member, formatter)?;
}
return formatter.write_str(")");
}
};
formatter.write_str(code)
}
}
#[cfg(test)]
mod tests {
use super::Encoding;
use alloc::string::ToString;
fn send_sync<T: Send + Sync>() {}
#[test]
fn test_send_sync() {
send_sync::<Encoding<'_>>();
}
#[test]
fn test_array_display() {
let e = Encoding::Array(12, &Encoding::Int);
assert_eq!(e.to_string(), "[12i]");
assert!(e.equivalent_to_str("[12i]"));
}
#[test]
fn test_pointer_display() {
let e = Encoding::Pointer(&Encoding::Int);
assert_eq!(e.to_string(), "^i");
assert!(e.equivalent_to_str("^i"));
}
#[test]
fn test_pointer_eq() {
let i = Encoding::Int;
let p = Encoding::Pointer(&Encoding::Int);
assert_eq!(p, p);
assert_ne!(p, i);
}
#[test]
fn test_int_display() {
assert_eq!(Encoding::Int.to_string(), "i");
assert!(Encoding::Int.equivalent_to_str("i"));
}
#[test]
fn test_eq() {
let i = Encoding::Int;
let c = Encoding::Char;
assert_eq!(i, i);
assert_ne!(i, c);
}
#[test]
fn test_struct_display() {
let s = Encoding::Struct("CGPoint", &[Encoding::Char, Encoding::Int]);
assert_eq!(s.to_string(), "{CGPoint=ci}");
assert!(s.equivalent_to_str("{CGPoint=ci}"));
}
#[test]
fn test_struct_eq() {
let s = Encoding::Struct("CGPoint", &[Encoding::Char, Encoding::Int]);
assert_eq!(s, s);
assert_ne!(s, Encoding::Int);
}
#[test]
fn test_union_display() {
let u = Encoding::Union("Onion", &[Encoding::Char, Encoding::Int]);
assert_eq!(u.to_string(), "(Onion=ci)");
assert!(u.equivalent_to_str("(Onion=ci)"));
}
#[test]
fn test_union_eq() {
let u = Encoding::Union("Onion", &[Encoding::Char, Encoding::Int]);
assert_eq!(u, u);
assert_ne!(u, Encoding::Int);
}
}