Skip to main content

ytsaurus_format/
lib.rs

1//! The data encoding used at a YTsaurus table or worker boundary.
2//!
3//! [`DataFormat`] is shared by `ytsaurus-client` operation/table APIs and by
4//! `ytsaurus-job` worker APIs.  Keeping that choice in one small crate means a
5//! launcher and its worker use the same value rather than two look-alike sets
6//! of format options that can drift apart.
7//!
8//! The enum is non-exhaustive: adding a YTsaurus format is a semver-compatible
9//! extension, while callers that need to distinguish formats are prompted to
10//! handle future variants.
11
12#![warn(missing_docs)]
13
14use std::collections::BTreeMap;
15
16pub use ytsaurus_skiff::Format as SkiffFormat;
17pub use ytsaurus_yson::YsonFormat;
18
19use ytsaurus_yson::{YsonNode, YsonValue};
20
21/// A supported YTsaurus data format.
22///
23/// This enum selects framing and the wire-format declaration sent to the
24/// cluster. The payload representation deliberately remains format-specific:
25/// YSON callers use bytes (or the existing serde-based convenience APIs), and
26/// Skiff callers use the dynamic schema/value APIs. A common enum must not
27/// pretend those two row models are interchangeable.
28#[derive(Debug, Clone, PartialEq, Eq)]
29#[non_exhaustive]
30pub enum DataFormat {
31    /// Text or binary YSON.
32    Yson(YsonFormat),
33    /// Schema-described Skiff.
34    Skiff(SkiffFormat),
35}
36
37impl DataFormat {
38    /// The normal format for YTsaurus jobs: binary YSON.
39    #[must_use]
40    pub const fn binary_yson() -> Self {
41        Self::Yson(YsonFormat::Binary)
42    }
43
44    /// Human-readable text YSON.
45    #[must_use]
46    pub const fn text_yson() -> Self {
47        Self::Yson(YsonFormat::Text)
48    }
49
50    /// Selects either YSON encoding explicitly.
51    #[must_use]
52    pub const fn yson(format: YsonFormat) -> Self {
53        Self::Yson(format)
54    }
55
56    /// Selects a validated Skiff format.
57    #[must_use]
58    pub fn skiff(format: SkiffFormat) -> Self {
59        Self::Skiff(format)
60    }
61
62    /// Returns the selected YSON encoding, if this is a YSON format.
63    #[must_use]
64    pub const fn as_yson(&self) -> Option<YsonFormat> {
65        match self {
66            Self::Yson(format) => Some(*format),
67            Self::Skiff(_) => None,
68        }
69    }
70
71    /// Returns the selected Skiff declaration, if this is Skiff.
72    #[must_use]
73    pub const fn as_skiff(&self) -> Option<&SkiffFormat> {
74        match self {
75            Self::Yson(_) => None,
76            Self::Skiff(format) => Some(format),
77        }
78    }
79
80    /// Renders the YSON `input_format` or `output_format` declaration accepted
81    /// by YTsaurus commands and operation specs.
82    #[must_use]
83    pub fn to_yson(&self) -> YsonValue {
84        match self {
85            Self::Yson(format) => yson_format(*format),
86            Self::Skiff(format) => format.to_yson(),
87        }
88    }
89}
90
91fn yson_format(format: YsonFormat) -> YsonValue {
92    let spelling = match format {
93        YsonFormat::Binary => b"binary".as_slice(),
94        YsonFormat::Text => b"text".as_slice(),
95    };
96    let mut attributes = BTreeMap::new();
97    attributes.insert(
98        b"format".to_vec(),
99        YsonValue {
100            attributes: None,
101            node: YsonNode::String(spelling.to_vec()),
102        },
103    );
104    YsonValue {
105        attributes: Some(attributes),
106        node: YsonNode::String(b"yson".to_vec()),
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use ytsaurus_skiff::{Schema, SchemaRef, WireType};
113
114    use super::*;
115
116    #[test]
117    fn yson_variants_render_the_cluster_declarations() {
118        assert_eq!(
119            ytsaurus_yson::to_string(&DataFormat::binary_yson().to_yson(), YsonFormat::Text)
120                .unwrap(),
121            "<format=binary>yson"
122        );
123        assert_eq!(
124            ytsaurus_yson::to_string(&DataFormat::text_yson().to_yson(), YsonFormat::Text).unwrap(),
125            "<format=text>yson"
126        );
127    }
128
129    #[test]
130    fn skiff_variant_delegates_to_the_validated_format() {
131        let skiff = SkiffFormat::new(vec![SchemaRef::Inline(Schema::tuple([Schema::named(
132            "value",
133            WireType::String32,
134        )]))])
135        .unwrap();
136        let format = DataFormat::skiff(skiff.clone());
137
138        assert_eq!(format.as_yson(), None);
139        assert_eq!(format.as_skiff(), Some(&skiff));
140        assert_eq!(format.to_yson(), skiff.to_yson());
141    }
142}