Skip to main content

ytsaurus_yson/
de.rs

1use crate::access::{AttributesWrapperAccess, CommaSeparated, EmptyMapAccess, EnumAccess};
2use crate::lexer::YsonIterator;
3use crate::node::{Token, YsonNode, YsonValue};
4use crate::{access::FlatStructAccess, error::YsonError};
5use serde::Deserialize;
6use serde::de::{self, MapAccess, SeqAccess, Visitor};
7use std::borrow::Cow;
8use std::collections::BTreeMap;
9
10/// A structure for deserializing YSON data into Rust types.
11pub struct Deserializer<'de> {
12    pub(crate) lexer: YsonIterator<'de>,
13    pub(crate) is_reading_attributes: bool,
14    /// Whether the container just visited consumed its own terminator.
15    ///
16    /// Written by `CommaSeparated`'s `Drop`, read by `end_container` the moment
17    /// the visit returns — so it always describes the container that has just
18    /// closed, never an earlier one.
19    pub(crate) container_ended: bool,
20    depth: usize,
21    max_depth: usize,
22}
23
24impl<'de> Deserializer<'de> {
25    /// Creates a new YSON deserializer from the given byte slice.
26    ///
27    /// # Arguments
28    ///
29    /// * `input` - The raw byte slice containing YSON data.
30    /// * `is_binary` - Set to `true` if the input is in YSON binary format,
31    ///   or `false` if it is in YSON text format.
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use ytsaurus_yson::de::Deserializer;
37    /// use serde::Deserialize;
38    ///
39    /// let input = b"42";
40    /// let mut de = Deserializer::from_bytes(input, false);
41    /// let value = i64::deserialize(&mut de).unwrap();
42    ///
43    /// assert_eq!(value, 42);
44    /// ```
45    #[must_use]
46    pub fn from_bytes(input: &'de [u8], is_binary: bool) -> Self {
47        Deserializer {
48            lexer: YsonIterator::new(input, is_binary),
49            is_reading_attributes: false,
50            container_ended: false,
51            depth: 0,
52            max_depth: 128,
53        }
54    }
55
56    /// Closes a container whose visitor stopped reading before the end of it.
57    ///
58    /// A `Vec` asks for one element more than there are, and the `None` that
59    /// answers it is what consumes the `]`. A **fixed-length** visitor — a
60    /// tuple, a tuple struct, an array — asks for exactly its length and stops,
61    /// so nothing ever reads the terminator. Left there it is read as the
62    /// *enclosing* container's: `[[1;2];3]` into `(Vec<i32>, i32)` would end the
63    /// outer list at the inner `]` and lose the `3`.
64    ///
65    /// So whoever opened the container closes it, if the visitor did not.
66    ///
67    /// # Errors
68    ///
69    /// Returns [`YsonError`] if what follows is not the terminator — which is
70    /// how a list longer than the tuple it is being read into is refused,
71    /// rather than silently truncated.
72    fn end_container(&mut self, end_byte: u8) -> Result<(), YsonError> {
73        if self.container_ended {
74            return Ok(());
75        }
76
77        // A trailing `;` is allowed before the terminator, exactly as it is
78        // between elements: `[10;20;]` is a two-element list.
79        if self.lexer.peek_byte()? == b';' {
80            self.lexer.next_token()?;
81        }
82
83        let byte = self.lexer.peek_byte()?;
84        if byte != end_byte {
85            return Err(YsonError::Custom(format!(
86                "expected {:?} to close the value, found byte {byte:#04x} at offset {}",
87                end_byte as char,
88                self.lexer.pos()
89            )));
90        }
91        self.lexer.next_token()?;
92        Ok(())
93    }
94
95    /// Verifies the input is exhausted, insignificant whitespace aside.
96    ///
97    /// [`crate::from_slice`] calls this after the value: trailing bytes mean a
98    /// corrupt or concatenated document, and reading just the front of one as
99    /// a healthy value is how corruption goes unnoticed.
100    ///
101    /// # Errors
102    ///
103    /// Returns [`YsonError`] naming the offset of the first trailing byte.
104    pub fn end(&mut self) -> Result<(), YsonError> {
105        match self.lexer.peek_byte() {
106            Err(YsonError::Eof) => Ok(()),
107            Ok(byte) => Err(YsonError::Custom(format!(
108                "trailing data after the value: byte {byte:#04x} at offset {}",
109                self.lexer.pos()
110            ))),
111            Err(other) => Err(other),
112        }
113    }
114
115    pub(crate) fn enter_recursion(&mut self) -> Result<(), YsonError> {
116        self.depth += 1;
117        if self.depth > self.max_depth {
118            return Err(YsonError::Custom("Recursion limit exceeded".into()));
119        }
120        Ok(())
121    }
122
123    pub(crate) fn leave_recursion(&mut self) {
124        self.depth -= 1;
125    }
126
127    fn skip_attributes(&mut self) -> Result<(), YsonError> {
128        if self.lexer.peek_byte()? == b'<' {
129            self.enter_recursion()?;
130            self.lexer.next_token()?;
131            let mut attr_depth = 1;
132            while attr_depth > 0 {
133                match self.lexer.next_token()? {
134                    Token::BeginAttributes => attr_depth += 1,
135                    Token::EndAttributes => attr_depth -= 1,
136                    _ => {}
137                }
138                if attr_depth > self.max_depth {
139                    return Err(YsonError::Custom("Attributes nesting too deep".into()));
140                }
141            }
142            self.leave_recursion();
143        }
144        Ok(())
145    }
146}
147
148macro_rules! delegate_skip_attributes {
149    ( $($method:ident),* $(,)? ) => {
150        $(
151            fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
152            where
153                V: Visitor<'de>,
154            {
155                if !self.is_reading_attributes {
156                    self.skip_attributes()?;
157                }
158                self.deserialize_any(visitor)
159            }
160        )*
161    };
162}
163
164impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> {
165    type Error = YsonError;
166
167    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
168    where
169        V: Visitor<'de>,
170    {
171        let was_reading_attributes = self.is_reading_attributes;
172        self.is_reading_attributes = false;
173
174        if was_reading_attributes {
175            if self.lexer.peek_byte()? != b'<' {
176                return visitor.visit_map(EmptyMapAccess);
177            }
178            self.lexer.next_token()?;
179            return visitor.visit_map(CommaSeparated::new(self, b'>')?);
180        }
181
182        if self.lexer.peek_byte()? == b'<' {
183            return visitor.visit_map(FlatStructAccess::new(self)?);
184        }
185
186        match self.lexer.next_token()? {
187            Token::Entity => visitor.visit_unit(),
188            Token::Boolean(b) => visitor.visit_bool(b),
189            Token::Int64(i) => visitor.visit_i64(i),
190            Token::Uint64(u) => visitor.visit_u64(u),
191            Token::Double(d) => visitor.visit_f64(d),
192            Token::String(s) => match s {
193                Cow::Borrowed(b) => {
194                    if let Ok(utf8) = std::str::from_utf8(b) {
195                        visitor.visit_borrowed_str(utf8)
196                    } else {
197                        visitor.visit_borrowed_bytes(b)
198                    }
199                }
200                Cow::Owned(vec) => match String::from_utf8(vec) {
201                    Ok(utf8) => visitor.visit_string(utf8),
202                    Err(e) => visitor.visit_byte_buf(e.into_bytes()),
203                },
204            },
205            Token::BeginList => {
206                let value = visitor.visit_seq(CommaSeparated::new(&mut *self, b']')?)?;
207                self.end_container(b']')?;
208                Ok(value)
209            }
210            Token::BeginMap => {
211                let value = visitor.visit_map(CommaSeparated::new(&mut *self, b'}')?)?;
212                self.end_container(b'}')?;
213                Ok(value)
214            }
215            Token::BeginAttributes => {
216                let value = visitor.visit_map(CommaSeparated::new(&mut *self, b'>')?)?;
217                self.end_container(b'>')?;
218                Ok(value)
219            }
220            t => Err(YsonError::Custom(format!("Unexpected token: {t:?}"))),
221        }
222    }
223
224    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
225    where
226        V: Visitor<'de>,
227    {
228        let was_reading_attributes = self.is_reading_attributes;
229        self.is_reading_attributes = false;
230
231        if was_reading_attributes {
232            if self.lexer.peek_byte()? == b'<' {
233                self.is_reading_attributes = true;
234                let res = visitor.visit_some(&mut *self);
235                self.is_reading_attributes = false;
236                res
237            } else {
238                visitor.visit_none()
239            }
240        } else {
241            self.skip_attributes()?;
242            if self.lexer.peek_byte()? == b'#' {
243                self.lexer.next_token()?;
244                visitor.visit_none()
245            } else {
246                visitor.visit_some(self)
247            }
248        }
249    }
250
251    fn deserialize_struct<V>(
252        self,
253        name: &'static str,
254        fields: &'static [&'static str],
255        visitor: V,
256    ) -> Result<V::Value, Self::Error>
257    where
258        V: Visitor<'de>,
259    {
260        if name == "$__yson_attributes" {
261            return visitor.visit_seq(AttributesWrapperAccess::new(self)?);
262        }
263        if fields.iter().any(|f| f.starts_with('@')) {
264            return visitor.visit_map(FlatStructAccess::new(self)?);
265        }
266
267        if !self.is_reading_attributes {
268            self.skip_attributes()?;
269        }
270        self.deserialize_any(visitor)
271    }
272
273    fn deserialize_enum<V>(
274        self,
275        _name: &'static str,
276        _variants: &'static [&'static str],
277        visitor: V,
278    ) -> Result<V::Value, Self::Error>
279    where
280        V: Visitor<'de>,
281    {
282        if !self.is_reading_attributes {
283            self.skip_attributes()?;
284        }
285
286        let peeked = self.lexer.peek_byte()?;
287        if peeked == b'{' {
288            self.lexer.next_token()?;
289            let val = visitor.visit_enum(EnumAccess::new(self, true))?;
290
291            loop {
292                match self.lexer.peek_byte() {
293                    Ok(b';' | b'}') => break,
294                    Ok(_) => {
295                        self.lexer.next_token()?;
296                    }
297                    Err(_) => break,
298                }
299            }
300
301            if let Ok(b';') = self.lexer.peek_byte() {
302                self.lexer.next_token()?;
303            }
304
305            match self.lexer.next_token()? {
306                Token::EndMap => Ok(val),
307                t => Err(YsonError::Custom(format!(
308                    "Expected '}}' after variant, got {t:?}"
309                ))),
310            }
311        } else {
312            visitor.visit_enum(EnumAccess::new(self, false))
313        }
314    }
315
316    delegate_skip_attributes! {
317        deserialize_bool, deserialize_i8, deserialize_i16, deserialize_i32,
318        deserialize_i64, deserialize_i128, deserialize_u8, deserialize_u16,
319        deserialize_u32, deserialize_u64, deserialize_u128, deserialize_f32,
320        deserialize_f64, deserialize_char, deserialize_str, deserialize_string,
321        deserialize_bytes, deserialize_byte_buf, deserialize_unit,
322        deserialize_seq, deserialize_map, deserialize_identifier,
323        deserialize_ignored_any
324    }
325
326    fn deserialize_unit_struct<V>(
327        self,
328        _name: &'static str,
329        visitor: V,
330    ) -> Result<V::Value, Self::Error>
331    where
332        V: Visitor<'de>,
333    {
334        if !self.is_reading_attributes {
335            self.skip_attributes()?;
336        }
337        self.deserialize_any(visitor)
338    }
339
340    fn deserialize_newtype_struct<V>(
341        self,
342        _name: &'static str,
343        visitor: V,
344    ) -> Result<V::Value, Self::Error>
345    where
346        V: Visitor<'de>,
347    {
348        if !self.is_reading_attributes {
349            self.skip_attributes()?;
350        }
351        self.deserialize_any(visitor)
352    }
353
354    fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
355    where
356        V: Visitor<'de>,
357    {
358        if !self.is_reading_attributes {
359            self.skip_attributes()?;
360        }
361        self.deserialize_any(visitor)
362    }
363
364    fn deserialize_tuple_struct<V>(
365        self,
366        _name: &'static str,
367        _len: usize,
368        visitor: V,
369    ) -> Result<V::Value, Self::Error>
370    where
371        V: Visitor<'de>,
372    {
373        if !self.is_reading_attributes {
374            self.skip_attributes()?;
375        }
376        self.deserialize_any(visitor)
377    }
378}
379
380/// A streaming deserializer that reads a sequence of YSON values from an input buffer.
381///
382/// In many YSON use cases, data is provided as a sequence of top-level values
383/// optionally separated by semicolons (e.g., `1; 2; 3;`). `StreamDeserializer`
384/// allows you to lazily iterate through these values without having to wrap
385/// them in a list `[...]`.
386///
387/// # Examples
388///
389/// ```
390/// use ytsaurus_yson::de::StreamDeserializer;
391///
392/// let input = b"1; 2; 3";
393/// let mut stream = StreamDeserializer::<i32>::new(input, false);
394///
395/// assert_eq!(stream.next_item().unwrap(), Some(1));
396/// assert_eq!(stream.next_item().unwrap(), Some(2));
397/// assert_eq!(stream.next_item().unwrap(), Some(3));
398/// assert_eq!(stream.next_item().unwrap(), None); // End of stream
399/// ```
400pub struct StreamDeserializer<'de, T> {
401    de: Deserializer<'de>,
402    first: bool,
403    _marker: std::marker::PhantomData<T>,
404}
405
406impl<'de, T> StreamDeserializer<'de, T>
407where
408    T: de::Deserialize<'de>,
409{
410    /// Creates a new `StreamDeserializer` from the given byte slice.
411    ///
412    /// # Arguments
413    ///
414    /// * `input` - The raw byte slice containing a sequence of YSON values.
415    /// * `is_binary` - `true` for binary format, `false` for text format.
416    #[must_use]
417    pub fn new(input: &'de [u8], is_binary: bool) -> Self {
418        Self {
419            de: Deserializer::from_bytes(input, is_binary),
420            first: true,
421            _marker: std::marker::PhantomData,
422        }
423    }
424
425    /// Deserializes the next item in the stream.
426    ///
427    /// # Returns
428    ///
429    /// - `Ok(Some(T))` if a value was successfully deserialized.
430    /// - `Ok(None)` if the end of the input was reached.
431    /// - `Err(YsonError)` if a parsing error occurred or if the data doesn't match type `T`.
432    ///
433    /// # Errors
434    ///
435    /// This method will return an error if:
436    /// - The YSON syntax is malformed.
437    /// - An item separator (semicolon) is missing where one is expected.
438    /// - The input ends prematurely after a separator.
439    pub fn next_item(&mut self) -> Result<Option<T>, YsonError> {
440        let peek_res = self.de.lexer.peek_byte();
441
442        if matches!(peek_res, Err(YsonError::Eof)) {
443            return Ok(None);
444        }
445
446        let next_byte = peek_res?;
447
448        if self.first {
449            self.first = false;
450        } else if next_byte == b';' {
451            self.de.lexer.next_token()?;
452            if matches!(self.de.lexer.peek_byte(), Err(YsonError::Eof)) {
453                return Ok(None);
454            }
455        }
456
457        let item = T::deserialize(&mut self.de)?;
458        Ok(Some(item))
459    }
460}
461
462/// A YSON map key or attribute name.
463///
464/// YSON keys are byte strings, not UTF-8, and [`YsonNode::Map`] stores them as
465/// `Vec<u8>` — so decoding a key through `String` would reject perfectly legal
466/// documents. This accepts both the UTF-8 and the raw-bytes visitor calls and
467/// keeps the bytes intact either way.
468struct MapKey(Vec<u8>);
469
470impl<'de> Deserialize<'de> for MapKey {
471    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
472    where
473        D: de::Deserializer<'de>,
474    {
475        struct MapKeyVisitor;
476
477        impl Visitor<'_> for MapKeyVisitor {
478            type Value = MapKey;
479
480            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
481                formatter.write_str("a YSON map key (byte string)")
482            }
483
484            fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
485                Ok(MapKey(v.as_bytes().to_vec()))
486            }
487
488            fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
489                Ok(MapKey(v.into_bytes()))
490            }
491
492            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
493                Ok(MapKey(v.to_vec()))
494            }
495
496            fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
497                Ok(MapKey(v))
498            }
499        }
500
501        deserializer.deserialize_any(MapKeyVisitor)
502    }
503}
504
505macro_rules! impl_visit_primitives {
506    ( $( $method:ident ( $v_type:ty ) => $node_variant:ident ),* ) => {
507        $(
508            fn $method<E>(self, v: $v_type) -> Result<Self::Value, E> {
509                Ok(YsonValue {
510                    attributes: None,
511                    node: YsonNode::$node_variant(v),
512                })
513            }
514        )*
515    };
516}
517
518impl<'de> Deserialize<'de> for YsonValue {
519    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
520    where
521        D: de::Deserializer<'de>,
522    {
523        struct YsonValueVisitor;
524
525        impl<'de> Visitor<'de> for YsonValueVisitor {
526            type Value = YsonValue;
527
528            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
529                formatter.write_str("any YSON value")
530            }
531
532            impl_visit_primitives! {
533                visit_bool(bool) => Boolean,
534                visit_i64(i64) => Int64,
535                visit_u64(u64) => Uint64,
536                visit_f64(f64) => Double
537            }
538
539            fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
540                Ok(YsonValue {
541                    attributes: None,
542                    node: YsonNode::String(v.as_bytes().to_vec()),
543                })
544            }
545
546            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
547                Ok(YsonValue {
548                    attributes: None,
549                    node: YsonNode::String(v.to_vec()),
550                })
551            }
552
553            fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
554                Ok(YsonValue {
555                    attributes: None,
556                    node: YsonNode::String(v),
557                })
558            }
559
560            fn visit_unit<E>(self) -> Result<Self::Value, E> {
561                Ok(YsonValue {
562                    attributes: None,
563                    node: YsonNode::Entity,
564                })
565            }
566
567            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
568            where
569                A: SeqAccess<'de>,
570            {
571                let mut vec = Vec::new();
572                while let Some(elem) = seq.next_element()? {
573                    vec.push(elem);
574                }
575                Ok(YsonValue {
576                    attributes: None,
577                    node: YsonNode::List(vec),
578                })
579            }
580
581            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
582            where
583                M: MapAccess<'de>,
584            {
585                let mut attributes = BTreeMap::new();
586                let mut plain_map = BTreeMap::new();
587                let mut body_node = None;
588                let mut is_attributed = false;
589
590                while let Some(MapKey(key)) = map.next_key::<MapKey>()? {
591                    if let Some(attr_name) = key.strip_prefix(b"@") {
592                        is_attributed = true;
593                        attributes.insert(attr_name.to_vec(), map.next_value()?);
594                    } else if key == b"$value" {
595                        is_attributed = true;
596                        let val: YsonValue = map.next_value()?;
597                        body_node = Some(val.node);
598                        if let Some(inner_attrs) = val.attributes {
599                            attributes.extend(inner_attrs);
600                        }
601                    } else {
602                        plain_map.insert(key, map.next_value()?);
603                    }
604                }
605
606                if is_attributed {
607                    // This is the flat presentation of an attributed value:
608                    // "@" keys are the attributes, and the body is either a
609                    // "$value" entry (non-map bodies) or the remaining plain
610                    // keys (a map body, whose entries arrive at this level).
611                    // The plain keys used to be discarded outright, so an
612                    // attributed *map* decoded to an attributed entity — the
613                    // whole body silently lost.
614                    if !plain_map.is_empty() {
615                        if body_node.is_some() {
616                            let (key, _) = plain_map.iter().next().expect("checked non-empty");
617                            return Err(serde::de::Error::custom(format!(
618                                "map carries \"$value\" beside a plain key {:?}; \
619                                 one value cannot have two bodies",
620                                String::from_utf8_lossy(key)
621                            )));
622                        }
623                        body_node = Some(YsonNode::Map(plain_map));
624                    }
625                    Ok(YsonValue {
626                        attributes: if attributes.is_empty() {
627                            None
628                        } else {
629                            Some(attributes)
630                        },
631                        node: body_node.unwrap_or(YsonNode::Entity),
632                    })
633                } else {
634                    Ok(YsonValue {
635                        attributes: None,
636                        node: YsonNode::Map(plain_map),
637                    })
638                }
639            }
640        }
641
642        deserializer.deserialize_any(YsonValueVisitor)
643    }
644}