#![allow(dead_code)]
use deser::State;
use deser::de::{Deserialize, Sink, SinkHandle, default_atom};
use deser::ext::ExtValue;
use deser::ser::{Emit, MapEmitter, SeqEmitter, Serialize, SerializeHandle};
use deser::{Atom, Error, ImplicitValue};
use deser_php::{Reference, Visibility, set_class, set_visibility, take_class, take_visibility};
pub fn hex(s: &str) -> Vec<u8> {
assert!(s.len().is_multiple_of(2), "odd hex string {:?}", s);
(0..s.len())
.step_by(2)
.map(|idx| u8::from_str_radix(&s[idx..idx + 2], 16).unwrap())
.collect()
}
#[derive(Debug, Clone)]
pub enum Php {
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(String),
Bytes(Vec<u8>),
List(Vec<Php>),
Map(Option<String>, Vec<(Key, Php)>),
Classed(String, Box<Php>),
Ref(Reference),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Key {
pub name: Php,
pub visibility: Option<Visibility>,
}
impl PartialEq for Php {
fn eq(&self, other: &Php) -> bool {
match (self, other) {
(Php::Float(a), Php::Float(b)) => {
a.to_bits() == b.to_bits() || a.is_nan() && b.is_nan()
}
(Php::Null, Php::Null) => true,
(Php::Bool(a), Php::Bool(b)) => a == b,
(Php::Int(a), Php::Int(b)) => a == b,
(Php::Str(a), Php::Str(b)) => a == b,
(Php::Bytes(a), Php::Bytes(b)) => a == b,
(Php::List(a), Php::List(b)) => a == b,
(Php::Map(a, b), Php::Map(c, d)) => a == c && b == d,
(Php::Classed(a, b), Php::Classed(c, d)) => a == c && b == d,
(Php::Ref(a), Php::Ref(b)) => a == b,
_ => false,
}
}
}
impl Serialize for Php {
fn serialize<'a>(this: &'a Self, state: &mut State) -> Result<Emit<'a>, Error> {
Ok(Emit::Atom(match *this {
Php::Null => Atom::Null,
Php::Bool(value) => Atom::Bool(value),
Php::Int(value) => Atom::I64(value),
Php::Float(value) => Atom::F64(value),
Php::Str(ref value) => Atom::Str(value.as_str().into()),
Php::Bytes(ref value) => Atom::Bytes(value.as_slice().into()),
Php::Ref(ref value) => Atom::Ext(ExtValue::borrowed(value)),
Php::List(ref items) => return Ok(Emit::seq(ListEmitter(items.iter()), state)),
Php::Map(ref class, ref entries) => {
let emit = Emit::map(EntryEmitter(entries.iter(), None), state);
if let Some(class) = class {
set_class(state, class.as_str());
}
return Ok(emit);
}
Php::Classed(ref class, ref value) => {
let emit = Php::serialize(value, state)?;
set_class(state, class.as_str());
return Ok(emit);
}
}))
}
}
impl Serialize for Key {
fn serialize<'a>(this: &'a Self, state: &mut State) -> Result<Emit<'a>, Error> {
let emit = Php::serialize(&this.name, state)?;
if let Some(ref visibility) = this.visibility {
set_visibility(state, visibility.clone());
}
Ok(emit)
}
}
struct ListEmitter<'a>(std::slice::Iter<'a, Php>);
impl SeqEmitter for ListEmitter<'_> {
fn next(&mut self, _state: &mut State) -> Result<Option<SerializeHandle<'_>>, Error> {
Ok(self.0.next().map(SerializeHandle::to))
}
}
struct EntryEmitter<'a>(std::slice::Iter<'a, (Key, Php)>, Option<&'a Php>);
impl MapEmitter for EntryEmitter<'_> {
fn next_key(&mut self, _state: &mut State) -> Result<Option<SerializeHandle<'_>>, Error> {
Ok(self.0.next().map(|(key, value)| {
self.1 = Some(value);
SerializeHandle::to(key)
}))
}
fn next_value(&mut self, _state: &mut State) -> Result<SerializeHandle<'_>, Error> {
Ok(SerializeHandle::to(self.1.unwrap()))
}
}
fn atom_value(atom: Atom) -> Option<Php> {
Some(match atom {
Atom::Null => Php::Null,
Atom::Bool(value) => Php::Bool(value),
Atom::Str(value) => Php::Str(value.into_owned()),
Atom::Bytes(value) => Php::Bytes(value.into_owned()),
Atom::U64(value) => Php::Int(value.try_into().unwrap()),
Atom::I64(value) => Php::Int(value),
Atom::F64(value) => Php::Float(value),
Atom::Implicit(value) => match value.value() {
ImplicitValue::U64(value) => Php::Int(value.try_into().unwrap()),
ImplicitValue::I64(value) => Php::Int(value),
other => panic!("unexpected implicit value {:?}", other),
},
Atom::Ext(ref ext) => Php::Ref(*ext.downcast_ref::<Reference>()?),
_ => return None,
})
}
impl<'de> Deserialize<'de> for Php {
fn deserialize_into<'out>(
out: &'out mut Option<Self>,
state: &mut State,
) -> SinkHandle<'out, 'de> {
SinkHandle::arena(
PhpSink {
out,
compound: None,
class: None,
key: None,
value: None,
},
state,
)
}
}
impl<'de> Deserialize<'de> for Key {
fn deserialize_into<'out>(
out: &'out mut Option<Self>,
state: &mut State,
) -> SinkHandle<'out, 'de> {
SinkHandle::arena(KeySink { out }, state)
}
}
struct KeySink<'a> {
out: &'a mut Option<Key>,
}
impl<'de> Sink<'de> for KeySink<'_> {
fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
let visibility = take_visibility(state);
let name = match atom_value(atom.clone()) {
Some(name) => name,
None => return default_atom(self, atom, state),
};
*self.out = Some(Key { name, visibility });
Ok(())
}
}
enum Compound {
List(Vec<Php>),
Map(Vec<(Key, Php)>),
}
struct PhpSink<'a> {
out: &'a mut Option<Php>,
compound: Option<Compound>,
class: Option<String>,
key: Option<Key>,
value: Option<Php>,
}
impl PhpSink<'_> {
fn flush(&mut self) {
match self.compound {
Some(Compound::List(ref mut items)) => items.extend(self.value.take()),
Some(Compound::Map(ref mut entries)) => {
if let (Some(key), Some(value)) = (self.key.take(), self.value.take()) {
entries.push((key, value));
}
}
None => {}
}
}
}
impl<'de> Sink<'de> for PhpSink<'_> {
fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
let class = take_class(state);
let value = match atom_value(atom.clone()) {
Some(value) => value,
None => return default_atom(self, atom, state),
};
*self.out = Some(match class {
Some(class) => Php::Classed(class, Box::new(value)),
None => value,
});
Ok(())
}
fn map(&mut self, state: &mut State) -> Result<(), Error> {
self.class = take_class(state);
self.compound = Some(Compound::Map(Vec::new()));
Ok(())
}
fn seq(&mut self, state: &mut State) -> Result<(), Error> {
self.class = take_class(state);
assert!(self.class.is_none(), "sequence with class");
self.compound = Some(Compound::List(Vec::new()));
Ok(())
}
fn next_key(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
self.flush();
Ok(Key::deserialize_into(&mut self.key, state))
}
fn next_value(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
if let Some(Compound::List(_)) = self.compound {
self.flush();
}
Ok(Php::deserialize_into(&mut self.value, state))
}
fn finish(&mut self, _state: &mut State) -> Result<(), Error> {
self.flush();
match self.compound.take() {
Some(Compound::List(items)) => *self.out = Some(Php::List(items)),
Some(Compound::Map(entries)) => *self.out = Some(Php::Map(self.class.take(), entries)),
None => {}
}
Ok(())
}
}