Skip to main content

deser_php/
object.rs

1//! Class names and property visibility (see the crate documentation).
2use alloc::borrow::Cow;
3use alloc::string::String;
4use core::fmt;
5
6use deser_core::State;
7use deser_core::de::{Deserialize, OwnedSink, Sink, SinkHandle};
8use deser_core::ser::{Describe, Emit, Serialize};
9use deser_core::{Atom, ContainerShape, Error};
10
11/// The class of a value, attached as event data to its first event.
12///
13/// The deserializer publishes the classes of objects, enum cases and
14/// custom serialized objects, the serializer writes them.
15#[derive(Debug, Default, Clone)]
16pub(crate) struct ClassName(pub(crate) Option<String>);
17
18/// The visibility of a property, attached as event data to its key.
19#[derive(Debug, Default, Clone)]
20pub(crate) struct PropertyVisibility(pub(crate) Option<Visibility>);
21
22/// The visibility of a property of an object.
23///
24/// PHP writes the names of protected and private properties with a prefix
25/// (`\0*\0name` and `\0Class\0name`).  The deserializer passes on the bare
26/// name and the visibility as event data of the key (see
27/// [`take_visibility`]), the serializer adds the prefix again if a key has
28/// a visibility (see [`set_visibility`]).
29#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
30pub enum Visibility {
31    /// A public property (no prefix).
32    #[default]
33    Public,
34    /// A protected property (`\0*\0name`).
35    Protected,
36    /// A private property of the given class (`\0Class\0name`).
37    Private(String),
38}
39
40/// Takes the class of the current value from the state.
41///
42/// This is intended to be called by sinks from within
43/// [`Sink::atom`] or [`Sink::map`].  Returns `None` if the value is not an
44/// object (or an enum case or custom serialized object) or the data format
45/// is not PHP's.
46///
47/// ```
48/// use deser::State;
49///
50/// fn class(state: &mut State) -> Option<String> {
51///     deser_php::take_class(state)
52/// }
53/// ```
54pub fn take_class(state: &mut State) -> Option<String> {
55    // the class is detached so that values that capture event data do not
56    // keep an empty class which would replace the class of a wrapper
57    state.take_event::<ClassName>().and_then(|class| class.0)
58}
59
60/// Sets the class of the value that is serialized.
61///
62/// This is what [`Object`] uses internally.  It must be called from
63/// [`Serialize::serialize`] and applies to the value serialized from that
64/// call (see [`State::event`]).  Maps are written as objects of the class,
65/// strings as enum cases (`E:`) and bytes as custom serialized objects
66/// (`C:`).  Serializers of other formats ignore it.
67pub fn set_class<S: Into<String>>(state: &mut State, class: S) {
68    state.event_mut::<ClassName>().0 = Some(class.into());
69}
70
71/// Takes the visibility of the current key from the state.
72///
73/// Returns `None` for keys that are not names of protected or private
74/// properties.
75pub fn take_visibility(state: &mut State) -> Option<Visibility> {
76    state
77        .take_event::<PropertyVisibility>()
78        .and_then(|visibility| visibility.0)
79}
80
81/// Sets the visibility of the key that is serialized.
82///
83/// It must be called from [`Serialize::serialize`] of the key.  The key is
84/// written with the prefix of the visibility.
85pub fn set_visibility(state: &mut State, visibility: Visibility) {
86    state.event_mut::<PropertyVisibility>().0 = Some(visibility);
87}
88
89/// A value with the class of a PHP object.
90///
91/// When deserialized the class of the value (if there is one) is captured,
92/// when serialized the value is written as an object of the class:
93///
94/// ```
95/// use std::collections::BTreeMap;
96/// use deser_php::Object;
97///
98/// let input = br#"O:4:"User":1:{s:4:"name";s:4:"Jane";}"#;
99/// let user: Object<BTreeMap<String, String>> = deser_php::from_slice(input).unwrap();
100/// assert_eq!(user.class.as_deref(), Some("User"));
101/// assert_eq!(user.value["name"], "Jane");
102/// assert_eq!(deser_php::to_vec(&user).unwrap(), input);
103/// ```
104///
105/// Maps are written as objects, strings as enum cases (`E:`) and bytes as
106/// custom serialized objects (`C:`).  Other formats do not have classes.
107/// When deserializing from such formats, the class is `None`, when
108/// serializing it's ignored.
109#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
110pub struct Object<T> {
111    /// The class of the value.
112    pub class: Option<String>,
113    /// The value.
114    pub value: T,
115}
116
117impl<T> Object<T> {
118    /// Creates a value with a class.
119    pub fn new<S: Into<String>>(class: S, value: T) -> Object<T> {
120        Object {
121            class: Some(class.into()),
122            value,
123        }
124    }
125
126    /// Returns the inner value.
127    pub fn into_inner(self) -> T {
128        self.value
129    }
130}
131
132impl<T: fmt::Debug> fmt::Debug for Object<T> {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        match self.class {
135            Some(ref class) => {
136                write!(f, "{} ", class)?;
137                fmt::Debug::fmt(&self.value, f)
138            }
139            None => fmt::Debug::fmt(&self.value, f),
140        }
141    }
142}
143
144impl<T: Serialize> Serialize for Object<T> {
145    fn serialize<'a>(this: &'a Self, state: &mut State) -> Result<Emit<'a>, Error> {
146        // the class is set after the value attached its data (like the
147        // class of a recorded value), it replaces it
148        let emit = T::serialize(&this.value, state)?;
149        if let Some(ref class) = this.class {
150            set_class(state, class.as_str());
151        }
152        Ok(emit)
153    }
154
155    fn finish(this: &Self, state: &mut State) -> Result<(), Error> {
156        T::finish(&this.value, state)
157    }
158
159    fn is_optional(this: &Self) -> bool {
160        T::is_optional(&this.value)
161    }
162
163    fn container_shape(this: &Self) -> ContainerShape {
164        T::container_shape(&this.value)
165    }
166
167    fn describe(this: &Self, d: &mut dyn Describe) {
168        T::describe(&this.value, d)
169    }
170}
171
172impl<'de, T: Deserialize<'de>> Deserialize<'de> for Object<T> {
173    fn deserialize_into<'out>(
174        out: &'out mut Option<Self>,
175        state: &mut State,
176    ) -> SinkHandle<'out, 'de> {
177        SinkHandle::arena(
178            ObjectSink {
179                out,
180                slot: None,
181                compound: None,
182                class: None,
183            },
184            state,
185        )
186    }
187
188    fn expecting() -> Cow<'static, str> {
189        T::expecting()
190    }
191
192    fn describe_type(d: &mut dyn Describe) {
193        T::describe_type(d)
194    }
195}
196
197struct ObjectSink<'a, 'de, T> {
198    out: &'a mut Option<Object<T>>,
199    // atoms are deserialized directly into this slot, maps and sequences
200    // need a sink that lives across calls
201    slot: Option<T>,
202    compound: Option<OwnedSink<'de, T>>,
203    class: Option<String>,
204}
205
206impl<'a, 'de, T: Deserialize<'de>> ObjectSink<'a, 'de, T> {
207    fn compound(&mut self, state: &mut State) -> &mut dyn Sink<'de> {
208        self.compound
209            .get_or_insert_with(|| OwnedSink::deserialize(state))
210            .get_mut()
211    }
212}
213
214impl<'a, 'de, T: Deserialize<'de>> Sink<'de> for ObjectSink<'a, 'de, T> {
215    fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
216        self.class = take_class(state);
217        let mut sink = T::deserialize_into(&mut self.slot, state);
218        sink.atom(atom, state)?;
219        sink.finish(state)
220    }
221
222    fn borrowed_atom(&mut self, atom: Atom<'de>, state: &mut State) -> Result<(), Error> {
223        self.class = take_class(state);
224        let mut sink = T::deserialize_into(&mut self.slot, state);
225        sink.borrowed_atom(atom, state)?;
226        sink.finish(state)
227    }
228
229    fn map(&mut self, state: &mut State) -> Result<(), Error> {
230        self.class = take_class(state);
231        self.compound(state).map(state)
232    }
233
234    fn seq(&mut self, state: &mut State) -> Result<(), Error> {
235        self.class = take_class(state);
236        self.compound(state).seq(state)
237    }
238
239    fn next_key(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
240        self.compound(state).next_key(state)
241    }
242
243    fn next_value(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
244        self.compound(state).next_value(state)
245    }
246
247    fn value_for_key(
248        &mut self,
249        key: &str,
250        state: &mut State,
251    ) -> Result<Option<SinkHandle<'_, 'de>>, Error> {
252        self.compound(state).value_for_key(key, state)
253    }
254
255    fn recover(&mut self, err: Error, state: &mut State) -> Result<(), Error> {
256        match self.compound {
257            Some(ref mut compound) => compound.get_mut().recover(err, state),
258            None => Err(err),
259        }
260    }
261
262    fn finish(&mut self, state: &mut State) -> Result<(), Error> {
263        let value = match self.compound {
264            Some(ref mut compound) => {
265                compound.get_mut().finish(state)?;
266                compound.take()
267            }
268            None => self.slot.take(),
269        };
270        let class = self.class.take();
271        *self.out = value.map(|value| Object { class, value });
272        Ok(())
273    }
274
275    fn expecting(&self) -> Cow<'_, str> {
276        if let Some(ref compound) = self.compound {
277            return compound.get().expecting();
278        }
279        T::expecting()
280    }
281}