use core::fmt;
use core::mem;
use {Descriptor, Encoding};
use super::{is_valid, parse, ParseResult};
use super::multi::StrEncodings;
#[derive(Clone, Copy, Debug)]
pub struct ParseEncodingError<S>(S) where S: AsRef<str>;
impl<S> fmt::Display for ParseEncodingError<S> where S: AsRef<str> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "Invalid encoding: {:?}", self.0.as_ref())
}
}
#[derive(Clone, Copy, Debug)]
pub struct StrEncoding<S: ?Sized = str>(S) where S: AsRef<str>;
impl StrEncoding {
pub fn from_str(s: &str) -> Result<&StrEncoding, ParseEncodingError<&str>> {
if is_valid(s) {
Ok(StrEncoding::from_str_unchecked(s))
} else {
Err(ParseEncodingError(s))
}
}
pub fn from_str_unchecked(s: &str) -> &StrEncoding {
unsafe { mem::transmute(s) }
}
}
impl<S> StrEncoding<S> where S: AsRef<str> {
pub fn new(s: S) -> Result<StrEncoding<S>, ParseEncodingError<S>> {
if is_valid(s.as_ref()) {
Ok(StrEncoding::new_unchecked(s))
} else {
Err(ParseEncodingError(s))
}
}
pub fn new_unchecked(s: S) -> StrEncoding<S> {
StrEncoding(s)
}
}
impl<S: ?Sized> StrEncoding<S> where S: AsRef<str> {
pub fn as_str(&self) -> &str {
self.0.as_ref()
}
}
impl<S: ?Sized> Encoding for StrEncoding<S> where S: AsRef<str> {
type PointerTarget = StrEncoding;
type ArrayItem = StrEncoding;
type StructFields = StrEncodings;
type UnionMembers = StrEncodings;
fn descriptor(&self) -> Descriptor<StrEncoding, StrEncoding, StrEncodings, StrEncodings> {
let s = self.as_str();
match parse(s) {
ParseResult::Primitive(p) => Descriptor::Primitive(p),
ParseResult::Pointer(t) =>
Descriptor::Pointer(StrEncoding::from_str_unchecked(t)),
ParseResult::Array(len, item) =>
Descriptor::Array(len, StrEncoding::from_str_unchecked(item)),
ParseResult::Struct(name, fields) =>
Descriptor::Struct(name, StrEncodings::from_str_unchecked(fields)),
ParseResult::Union(name, members) =>
Descriptor::Union(name, StrEncodings::from_str_unchecked(members)),
ParseResult::Error => panic!("Failed to parse an encoding from {:?}", s),
}
}
fn write<W: fmt::Write>(&self, writer: &mut W) -> fmt::Result {
writer.write_str(self.as_str())
}
}
impl<S: ?Sized> fmt::Display for StrEncoding<S> where S: AsRef<str> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.write(formatter)
}
}
impl<S: ?Sized, E: ?Sized> PartialEq<E> for StrEncoding<S>
where S: AsRef<str>, E: Encoding {
fn eq(&self, other: &E) -> bool {
self.eq_encoding(other)
}
}
#[cfg(test)]
mod tests {
use encoding::{Array, Primitive, Struct};
use super::*;
#[test]
fn test_parsed_array() {
let a = StrEncoding::from_str_unchecked("[12i]");
assert!(a == &Array::new(12, Primitive::Int));
}
#[test]
fn test_parsed_struct() {
let parsed = StrEncoding::from_str_unchecked("{CGPoint=ci}");
let expected = Struct::new("CGPoint", (Primitive::Char, Primitive::Int));
assert!(parsed == &expected);
}
}